In today's web development landscape, optimizing images is pivotal for performance and user experience. While Next.js offers an Image component for image optimization, crafting a reusable image solution tailored to specific application needs proves invaluable.
This blog explores the significance of a customizable image component beyond Next.js' defaults. We'll dive into the benefits of creating a bespoke image component, enabling versatile image handling for diverse use cases in Next.js applications. Let's uncover how this customizable approach enhances image management, elevates user experience, and optimizes performance across Next.js projects.
Setting Up Next.js & Required LibrariesYou can easily create nextjs app in my time 14 by running
npx create-next-app@latestEnter fullscreen modeExit fullscreen modeI'll stick with javascript and tailwindcss, feel free to use typescript if you want, I'll provide repo link at the end.
After creating your project, we have to install three more libraries that we need for the image component that we gonna create
npm install sharp plaiceholder @plaiceholder/nextEnter fullscreen modeExit fullscreen modeWe need these packages to convert our images to base64 for blurDataUrl the next image component requires us when we want to use external images.
Since we are using remote images from a remote source, in my case I will use Unsplash, so we have to add Unsplash image API into next config remotePatterns, as well as add the image formats
const nextConfig = { images: {formats: ["image/avif", "image/webp"],remotePatterns: [ {hostname: "images.unsplash.com", },], },};Enter fullscreen modeExit fullscreen mode Creating a Reusable Image ComponentOur image component is very simple you can copy and paste the code below, I named BaseImage since it will be my base Image component throughout my project
"use client";import React, { useState } from "react";import placeholderImage from "@/assets/placeholder.jpg";import Image from "next/image";import defaultBlur from "@/assets/blurData";export default function BaseImage({ src, width, height, blurData, ...rest }) { const [imgSrc, setImgSrc] = useState(src); return ( {setImgSrc(placeholderImage); }} {...rest}/> );}Enter fullscreen modeExit fullscreen modeNotice that the component is a client component because we are using a state to set the fallback src image, and we're passing the blurData of a remote image that we gonna use, notice we also have a default blur data, case remote image has some problem to access it we will show our fallback blur data.
Creating base64 util functionBefore we use our BaseImage component, we have to add a function that takes our remote source and returns a base64 of that image so then we'll be able to use that as a blurDataUrl for that image, here is the implementation
import { unstable_noStore } from "next/cache";import { getPlaiceholder } from "plaiceholder";async function getBlurData(src) { unstable_noStore(); try {const buffer = await fetch(src).then(async (res) => { return Buffer.from(await res.arrayBuffer());});const data = await getPlaiceholder(buffer);return data; } catch (err) {console.error("Error fetching or processing image:", err);return { base64: "", img: "" }; }}export { getBlurData };Enter fullscreen modeExit fullscreen modeNote that the function I created under the root of app/lib/utils.js because the plaiceholder package is working under the nodejs environment so we have to put it under the app directory.
Example: Implementing in a Card ComponentHere is an example of how to use our image component with a grid card container
import { getBlurData } from "@/app/lib/utils";import React from "react";import BaseImage from "./BaseImage";import Link from "next/link";const data = [ {id: 1,title: "Cat 1",description: "Cat 1 is a playful and energetic feline who loves to explore and chase toys around the house.",imgSrc: "https://images.unsplash.com/photo-1646753522408-077ef9839300?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwcm9maWxlLXBhZ2V8NjZ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=500&q=60", }, {id: 2,title: "Cat 2",description: "Cat 2 is a calm and affectionate furry friend who enjoys lounging in cozy spots and getting cuddles.",imgSrc: "https://images.unsplash.com/photo-1651950519238-15835722f8bb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwcm9maWxlLXBhZ2V8Mjh8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=500&q=60", }, {id: 3,title: "Cat 3",description: "Cat 3 is an adventurous and curious cat who loves exploring the outdoors and climbing trees.",imgSrc: "https://images.unsplash.com/photo-1651950537598-373e4358d320?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwcm9maWxlLXBhZ2V8MjV8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=500&q=60", }, // This is for an error image // { //id: 4, //title: "Cat 2", //description: // "Cat 3 is an adventurous and curious cat who loves exploring the outdoors and climbing trees.", //imgSrc: // "https://images.unsplash.com/photo-1651950537598-373e4358d3202?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwcm9maWxlLXBhZ2V8MjV8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=500&q=60", // },];const Cards = () => { return ( {data.map((x) => ( ))} );};async function Card({ imgSrc, title, description }) { const { base64 } = await getBlurData(imgSrc); return ({title}
{description}
);}export default Cards;Enter fullscreen modeExit fullscreen mode Implementation with Two-Section Layoutimport React from "react";import BaseImage from "./BaseImage";import { getBlurData } from "@/app/lib/utils";const LAYOUT_URL = "https://images.unsplash.com/photo-1465146344425-f00d5f5c8f07";const TwoSectionLayout = async () => { const { base64 } = await getBlurData(LAYOUT_URL); return (Before they sold outreadme glutenCopper mug try-hard pitchfork pour-over freeway heirloom neutral airplant cold-pressed tacos poke beard tote bag. Heirloom echo parkmash tote bag selvage hot chicken authentic tumeric truffaut hexagontry-hard chambray.
Button);};export default TwoSectionLayout;Enter fullscreen modeExit fullscreen mode ConclusionOptimizing images is integral to web development's performance and user appeal. Throughout this exploration, we've emphasized the pivotal role of a tailored image component in Next.js, surpassing default capabilities.
Github link: Source Code