DEV Community

Cover image for Amazing preview images with Next.js and LQIP Modern
Anish De
Anish De

Posted on • Originally published at blog.anishde.dev

Amazing preview images with Next.js and LQIP Modern

Images take a long time to load and can have a disruptive impact on UX. Today we are going to be looking at creating preview images with a library called lqip-modern.

What is LQIP?

LQIP simply stands for Low Quality Image Placeholders. They have extremely small file sizes and act as placeholders for the actual image while the actual image is still loading. These extremely small file sizes are obtained by blurring the image, resizing it to a smaller size, or reducing the quality in the case of JPEGs.

Compatibility

WebP is supported by all modern browsers. Also, WebP support is present in Safari on macOS only if one is using macOS 11 (Big Sur) or later. source

If 100% compatibility is the goal, we can use JPEG LQIPs as well (they are almost 2-3 times the size of a WebP image).

Let us now look at how we can use lqip-modern with Next.js

Using LQIP Modern with Next.js

Next.js has an in-built next/image component which can provide preview images for local files without the use of an external library, but it cannot for remote images.

Now, there is also a limitation with our approach here, that is, preview images are created at build time. This means, that if the external image changes, then the preview image will not change.

However, this method will be especially useful if you are fetching the image from a CMS. If the image is ever updated, a build can be triggered which will create a new preview image. A better approach would be to use on-demand Incremental Static Regeneration or regular incremental static regeneration, however, that is out of the scope of this article. You can read my blog post on implementing on-demand incremental static regeneration with Directus to learn more.

In this example, we are going to look at creating preview images for an image from Unsplash. I am going to be using this awesome image of a Vercel mug along with some computer peripherals for this tutorial. However, you can choose any picture you like.

Firstly, let us create a new Next.js application -

npx create-next-app next-lqip-demo
# OR
yarn create next-app next-lqip-demo
Enter fullscreen mode Exit fullscreen mode

After it has been created, open up the project in your favorite code editor.

Now, open up the pages/index.js file and replace it with the following code -

import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";

export default function Home() {
  return (
    <div className={styles.container}>
      <Head>
        <title>LQIP demo with Next.js</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to{" "}
          <a href="https://nextjs.org">this demo of LQIP with Next.js!</a>
        </h1>

        <div style={{ marginTop: "4rem" }}>
          <Image
            src="https://images.unsplash.com/photo-1642083139428-9ee5fa423c46"
            alt="Vercel mug with computer peripherals"
            height={480}
            width={320}
          />
        </div>
      </main>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Also, replace the code inside next.config.js with the following -

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: ["images.unsplash.com"],
  },
};

module.exports = nextConfig;
Enter fullscreen mode Exit fullscreen mode

We are using the next/image component to show our image from Unsplash. As the image is from a remote URL, we have to also add the domain in next.config.js.

Now run npm run dev or yarn dev to start a development server and then visit localhost:3000. You will be able to see the page heading with the image -

image.png

When you first visited the page, you would have noticed the image took a split of a second to load. Depending on your internet connection, it can be fast or slow. If you have a fast internet connection, open up developer tools and go to the network tab. Here you can throttle your internet connection to simulate a slow loading time -

image.png

Using LQIP to optimize our remote image

Firstly, let us install lqip-modern, and sharp. Sharp is an awesome package that helps with image transformations and is used by lqip-modern -

npm install --save lqip-modern sharp
# OR
yarn add lqip-modern sharp
Enter fullscreen mode Exit fullscreen mode

Now, replace the code in pages/index.js with the following -

import lqipModern from "lqip-modern";
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";

export default function Home({ imageUrl, previewImageUrl }) {
  return (
    <div className={styles.container}>
      <Head>
        <title>LQIP demo with Next.js</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to{" "}
          <a href="https://nextjs.org">this demo of LQIP with Next.js!</a>
        </h1>

        <div style={{ marginTop: "4rem" }}>
          <Image
            src={imageUrl}
            alt="Vercel mug with computer peripherals"
            height={480}
            width={320}
            placeholder="blur"
            blurDataURL={previewImageUrl}
          />
        </div>
      </main>
    </div>
  );
}

export const getStaticProps = async () => {
  const unsplashImageUrl =
    "https://images.unsplash.com/photo-1642083139428-9ee5fa423c46";
  const image = await fetch(unsplashImageUrl);
  const imageBuffer = Buffer.from(await image.arrayBuffer());
  const previewImage = await lqipModern(imageBuffer);

  return {
    props: {
      imageUrl: unsplashImageUrl,
      previewImageUrl: previewImage.metadata.dataURIBase64,
    },
  };
};
Enter fullscreen mode Exit fullscreen mode

In getStaticProps, we first fetch the image and convert it to a Buffer. We then give lqip-modern our buffer and it returns us an object called previewImage which contains a buffer and some metadata. Inside the metadata, there is a field called dataURIBase64 which is a base64 URL for our preview image. We pass this in via props to our client-side application.

On the client-side, we have added a new placeholder="blur" parameter to our Image component that will show a blur placeholder. As it is a remote image, we are required to pass in the blurDataURL parameter. We pass in the base64 URL for our preview image we obtained from the metadata earlier, here.

Now if you reload the page, while the image is loading, you should see the preview image.

Preview Image Demo.gif

For those wondering, this is the image lqip-modern made us -

image.png

It is tiny at just 11x16 (the next/image component makes it fill the width and height of the original image) and is just 78 bytes!

Using JPEG instead of WebP

If you want to support all browsers, you can add the outputFormat option when making the preview image to get a JPEG preview image, like this -

  const previewImage = await lqipModern(imageBuffer, { outputFormat: "jpeg" });
Enter fullscreen mode Exit fullscreen mode

The JPEG image is the same dimensions as our WebP image but significantly larger in size at 303 bytes -

image.png

Note that these file sizes will vary depending on what image you use. The difference in file size between JPEG and WebP can go under double in some cases.

Conclusion

Alright, that is it! Let us go over what we did in this tutorial -

  • Learned about LQIP images
  • Created a Next.js application and added an image from Unsplash
  • Used lqip-modern to create preview images
  • Looked at how we can obtain JPEG preview images

Hope you liked this tutorial! Do share it if you have found it useful :)

Important Links

Discussion (0)