Improving my website's performance (Part - 1) - Image Optimizations

11 min read
📝 1113 words
Improving my website's performance (Part - 1) - Image Optimizations

Current State (July 25, 2025)

The Vercel Experience score is at 73 of this website, need to improve

July 25, 2025 - Vercel dashboard

In this series, I am going to show you what steps i am taking to improve my site's web performance.

Large images were loading in the guestbook comp on the Homepage

1. Fixed - Optimizing images for guestbook

So if you visit my website's homepage Home, the lighthouse in Chrome yelled saying that images were not optimized for images coming from guestbooks profile images of the list view.

guestbook avatar images

Guestbook images were not loading according the required size, they were larger in size, So i need a way to optimize this images based on the size i am rendering.

The Images that were loading were coming from the github api, to make the images transform based on width and height, i had to pass in a query to the api, so that it renders accordingly.

There is article by Google Lighthouse page on Properly sized images - optimizing images and Lighthouse calculates this scores.

properly sized images

so all i had to do was attach the '?s=width' to the api with the required width, currently in my case it was "40px", optimizing it with the next/image. it came pretty well.

<Image
    src={`${avatarUrl}&s=${width}`} // appending the size query
    alt={avatarAlt}
    width={width}
    height={height}
    className="rounded-full object-cover"
    priority
/>

changes done for issue 1

so i had to optimize the code to support the proper width of the avatar images:

"use client";
import Image from "next/image";

export default function Avatar({
  avatarUrl,
  avatarAlt = "",
  width = 48,
  height = 48,
}: {
  avatarUrl: string;
  avatarAlt?: string;
  width?: number;
  height?: number;
}) {
  // Optimize GitHub avatar URL by adding size parameter
  const optimizedAvatarUrl = new URL(avatarUrl);
  optimizedAvatarUrl.searchParams.set("s", Math.max(width, height).toString()); // ?s="48" // width of 48px

  return (
    <div
      className="flex items-center justify-center overflow-hidden rounded-full  mx-2"
      style={{ width: `${width}px`, height: `${height}px` }}
    >
      <Image
        src={optimizedAvatarUrl.toString()}
        alt={avatarAlt}
        width={width}
        height={height}
        className="rounded-full object-cover"
        priority
      />
    </div>
  );
}

Now the score has bumped a bit to 99 for the Homepage. a bit of improvement on the Lighthouse Scores.

updated lighthouse scores after fixing issue1

Current State (July 31, 2025)

Today, I ran the performance check for Desktop view again for lighthouse in Chrome (Incognito mode) by visiting https://sujaykundu.com

So today I found this issue:

Issue 2 web optimization

So I have a component for showing rotating images, but this images are not optimized

Issue 2 web optimization issues

So, I actually fixed it by optimizing the component, so the old component looked something like this:

"use client";
import Image from "next/image";
import { useEffect, useState } from "react";

export const AutoImageChange: React.FC<{
  imageUrls?: string[];
}> = ({ imageUrls }) => {
  const [currentImage, setCurrentImage] = useState(0);
  const [currentImageSrc, setCurrentImageSrc] = useState("");
  const [isReady, setIsReady] = useState(false);

  let imagesToChange = [
    "/assets/images/img0.jpg",
    "/assets/images/img1.png",
    "/assets/images/img2.jpg",
    "/assets/images/img3.jpg",
    "/assets/images/img4.jpg",
    "/assets/images/img5.jpg",
  ];

  if (imageUrls) {
    imagesToChange = imageUrls;
  }

  const handleImageTransition = () => {
    setIsReady(true);
  };

  useEffect(() => {
    const interval = setInterval(() => {
      setIsReady(false);
      const newImage = (currentImage + 1) % imagesToChange.length;

      setCurrentImage(newImage);

      setCurrentImageSrc(imagesToChange[newImage]);
    }, 2000);

    return () => clearInterval(interval);
  }, [currentImage]);

  return (
    <div className="avatar">
      <div className="w-20 md:w-32 rounded-full">
        <Image
          src={currentImageSrc || imagesToChange[0]}
          alt="About me"
          className={`rounded-full transition duration-1000 ${
            isReady ? "blur-0" : "blur-sm"
          }`}
          fill
          // width={100}
          // height={100}
          onLoad={handleImageTransition}
          priority
        />
      </div>
    </div>
  );
};

export default AutoImageChange;

So earlier, the images were not having proper width and height, and the images were

So I optimized the component as below :

"use client";
import Image from "next/image";
import { useEffect, useState, useCallback, useMemo } from "react";

type AutoImageChangeProps = {
  imageUrls?: string[];
  interval?: number;
  width?: number;
  height?: number;
  className?: string;
};

const useImageRotation = (images: string[], intervalDuration: number) => {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [isReady, setIsReady] = useState(false);

  const rotateImage = useCallback(() => {
    setIsReady(false);
    setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length);
  }, [images.length]);

  useEffect(() => {
    const interval = setInterval(rotateImage, intervalDuration);
    return () => clearInterval(interval);
  }, [rotateImage, intervalDuration]);

  return {
    currentImage: images[currentIndex],
    isReady,
    setIsReady,
  };
};

export const AutoImageChange: React.FC<AutoImageChangeProps> = ({
  imageUrls,
  interval = 2000,
  width = 128, // default width for md breakpoint
  height = 128, // default height for md breakpoint
}) => {
  const defaultImages = useMemo(
    () => [
      "/assets/images/img0.jpg",
      "/assets/images/img1.png",
      "/assets/images/img2.jpg",
      "/assets/images/img3.jpg",
      "/assets/images/img4.jpg",
      "/assets/images/img5.jpg",
    ],
    []
  );

  const images = useMemo(
    () => imageUrls || defaultImages,
    [imageUrls, defaultImages]
  );

  const { currentImage, isReady, setIsReady } = useImageRotation(
    images,
    interval
  );

  const handleImageTransition = useCallback(() => {
    setIsReady(true);
  }, [setIsReady]);

  // Calculate responsive dimensions
  const mobileWidth = Math.round(width * 0.625); // 20/32 ratio for mobile
  const mobileHeight = Math.round(height * 0.625);

  return (
    <div className="avatar relative">
      <div
        className="relative rounded-full border-blue-50 dark:border-blue-900 border-2"
        style={{
          width: `${width}px`,
          height: `${height}px`,
          ["--mobile-width" as string]: `${mobileWidth}px`,
          ["--mobile-height" as string]: `${mobileHeight}px`,
        }}
      >
        <Image
          src={currentImage}
          alt="image-1"
          className={`rounded-full transition duration-1000  ${
            isReady ? "blur-0" : "blur-sm"
          }`}
          fill
          sizes={`(max-width: 768px) ${mobileWidth}px, ${width}px`}
          quality={90}
          style={{
            objectFit: "cover",
            width: "100%",
            height: "100%",
          }}
          onLoad={handleImageTransition}
          priority
        />
      </div>
    </div>
  );
};

export default AutoImageChange;

And then resized all my images to use width and height "128px", when used with the AutoImageChange component by passing width and height as "128px" and compressed images.

Resizing the images with Figma Export

So as you can see i have different sizes, which i made it same

Before :

Issue 2 web optimization issues

After

Issue 2 web optimization issues

Then I exported all the images in png format

Issue 2 web optimization issues

Compressing the images

I use a free online tool to compress all my images. iLoveImg

This will significantly reduce the sizes of the images.. Less size, more faster the images load... but we need to do one more thing.

Issue 2 web optimization issues

Issue 2 web optimization issues

Convert Compressed png images to webp format

It's a good practices to load the images in webp format. (this is kind of compressed images formatted for web). This will also significantly improve our image sizes to be less and helps in loading images faster.

Issue 2 web optimization issues

After all this process, finally letting our AutoImageChange component to use the webp images

const techIcons: string[] = [
    "/assets/images/128/compressed/bolt-blue.webp",
    "/assets/images/128/compressed/bolt.webp",
    "/assets/images/128/compressed/chatgpt.webp",
    "/assets/images/128/compressed/claude-color.webp",
    "/assets/images/128/compressed/elementor.webp",
    "/assets/images/128/compressed/figma.webp",
    "/assets/images/128/compressed/framer.webp",
    "/assets/images/128/compressed/hygraph.webp",
    "/assets/images/128/compressed/make.webp",
    "/assets/images/128/compressed/nextjs.webp",
    "/assets/images/128/compressed/payload.webp",
    "/assets/images/128/compressed/strapi.webp",
    "/assets/images/128/compressed/supabase.webp",
    "/assets/images/128/compressed/webflow.webp",
    "/assets/images/128/compressed/zapier.webp",
    "/assets/images/128/compressed/n8n.webp",
    "/assets/images/128/compressed/wordpress-white.webp",
    "/assets/images/128/compressed/super.webp"
  ];
  
  return (
   <AutoImageChange imageUrls={techIcons} width={128} height={128} />
  );
  

This felt great and loaded quickly. Now seeing the sizes its reduced significantly and improved the overall lighthouse performance score.

updated lighthouse scores after fixing issue2

Wohoo ! we achieved Performance Score (100) in Lighthouse.