Configure Fallback Images in React and Next.js

Why did I need a fallback?

Recently at work, I had to display lots of user data with images on the website I was building. I was getting all the data from an API and it was just a matter of putting things on the screen...

Except that, in some cases, the image for the user didn't exist anymore. So although I had a src for my image tag, there was no image and the page would just show the alternative text I provided. Here you can see a broken src on the left and a normal image on the right:

This looked horrible, so I was asked to put a fallback image whenever there was a problem with the source.

Fallback images in React

For React, the solution is only one additional line to the code you would normally write. Let's take a look:

import fallback from "../public/fallback-image.png";

function ImageWithFallback({ src, alt, fallBackSrc = fallback.src }) {
  return (
    <div style={{ border: "1px solid black", height: "50vh" }}>
      <img
        src={src}
        alt={alt}
        style={{ height: "100%", aspectRatio: "1 / 1", objectFit: "cover" }}
        onError={(e) => (e.currentTarget.src = fallBackSrc)}
      />
    </div>
  );
}

export default ImageWithFallback;

Div and styling are only there for illustration purposes. We can see that this doesn't differ from the regular image tag we already know. The magic happens in the onError callback function, which gets fired as soon as there is a problem with the src. When this happens, our src will be replaced by our fallback image and we can go take a break. ☕

Fallback images with optimized Images in Next.js

In my case, I was using the Image-tag from Next.js to take advantage of lazy loading and image optimization. When I tried to use the same onError function with Next.js, the fallback image would never show! Therefore, I created a piece of state so I could rerender the component in case of an error:

import fallback from "../public/fallback-image.png";
import Image from "next/image";
import { useState } from "react";

function OptimizedImageWithFallback({ src, alt, fallBackSrc = fallback.src }) {
  const [imageError, setImageError] = useState(false);
  return (
    <div
      style={{
        border: "1px solid black",
        position: "relative",
      }}
    >
      <Image
        src={imageError ? fallBackSrc : src }
        alt={alt}
        width={500}
        height={500}
        objectFit='cover'
        onError={() => setImageError(true)}
      />
    </div>
  );
}

export default OptimizedImageWithFallback;

The outer div is a requirement of the next image tag and again for some styling. The onError function in this case just changes the error state to true, causing a rerender and changing the src to fallBackSrc.

That's it! I hope it helped you out! See you next time 😀

21