Amazing preview images with Next.js and LQIP Modern

Search for a command to run...

No comments yet. Be the first to comment.
If you have worked with the Solana Wallet Adapter before, you will know that it is very easy to set up a Connect Wallet button with a decent modal. However, customization is pretty limited. We can only add some custom CSS hence changing the styles bu...

And CandyPay QR Codes which people can scan to claim the NFT in under 15 seconds

As developers, most of us use the terminal to interact with our computers for many tasks as we find it more productive. We are familiar with commands like ls, cd, cat, grep, and find. These are primarily pre-installed on our computers and mostly get ...

Most of us use version control systems (mostly git) for our projects but the git CLI is unproductive. We often need to run multiple commands and got to type more characters. Well, what if I told you there are tools that can improve this significantl...

Authentication in Web3 is extremely easy but supporting all the wallets and making a nice UI can be painful and time-consuming. Thankfully, there are many libraries which makes this extremely easy as well. Today we are going to be looking at adding R...

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.
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.
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
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
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>
);
}
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;
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 -

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 -

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
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,
},
};
};
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.

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

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!
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" });
The JPEG image is the same dimensions as our WebP image but significantly larger in size at 303 bytes -

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.
Alright, that is it! Let us go over what we did in this tutorial -
lqip-modern to create preview imagesHope you liked this tutorial! Do share it if you have found it useful :)