DEV Community

Cover image for Configure Fallback Images in React and Next.js
Elisabeth Leonhardt
Elisabeth Leonhardt

Posted on • Updated on

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:

broken src on the left, 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;

Enter fullscreen mode Exit fullscreen mode

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. ☕

Showing a fallback image instead of the alternative text

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;
Enter fullscreen mode Exit fullscreen mode

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 😀

Top comments (5)

Collapse
 
salmanhpt profile image
salmanhpt

do you think this will also solve the image indexing issue that is faced by many.

I have yet to see a Next/Image generated URL to be indexed by google, like this:

www.example.com/_next/image?url=%2Fimages%2Fhome%2FDog-image-1.jpg&w=384&q=100

Collapse
 
dineufeld profile image
Dimitri Neufeld • Edited

found conditional logic issue, it should be
src={imageError ? fallBackSrc : src}
....

Collapse
 
frontenddeveli profile image
Elisabeth Leonhardt

That's absolutely correct! Thank you for pointing this out - I already the example in the article 😀

Collapse
 
meetmakwana7396 profile image
Meet Makwana

This is for one Image component, how can i handle this when i'm listing products with map method,

Collapse
 
alinp25 profile image
Alin Pisica

In the current situation, OptimizedImageWithFallback is thought of as a standalone reusable component. Normally, you could simply iterate through the list using a map and for each element there will be rendered the component.

Example:
items.map(item => <div className="product" />{...other code here}<OptimizedImageWithFallback src={item.src} alt={item.alt} />){...other code here}</div>
(sorry for possible syntax errors, written on the phone)