htmlimagenext.js

Image tag fallback Issue on Nextjs


Issue: Base64 Image URLs sometimes break UI instead of falling back

I'm rendering images in my UI using Base64-encoded JPEG URLs. While some images display correctly, others break the UI and only show the alt text. I expect the fallback UI ("No Image" div) to render instead when the image is invalid.


Here's the JSX Code I'm using:

{property.propertyContent[0].imageUrls.length > 0 ? (
  <img
    src={property.propertyContent[0].imageUrls}
    alt={property.title}
    className="object-cover w-full h-full"
  />
) : (
  <div className="flex items-center justify-center w-full h-full bg-gray-200">
    <span className="text-gray-500">No Image</span>
  </div>
)}

This works only if:

However, when imageUrls has a broken or invalid Base64 string (but still has length > 0), the image tag renders but fails to load — and the fallback div is never shown.


What I'm trying to achieve

I want to gracefully fallback to "No Image" when:


What I’ve tried


Expected behavior

If the Base64 image fails to load for any reason (malformed or corrupted), I want to hide the image and show a fallback div like this:

<div class="bg-gray-200">No Image</div>

How can I fix this?


Solution

  • If you don’t want to use state, you can just hide the element inside the handler and keep the placeholder in the DOM:
    
    <img
      src={url}
      alt={title}
      className="object-cover w-full h-full"
      onError={e => (e.currentTarget.style.display = 'none')}
    />
    <div className="flex items-center justify-center w-full h-full bg-gray-200">
      No Image
    </div>
    

    You can’t detect a corrupt image with a ternary on length; you have to wait for the load attempt and react to onError.
    If you don't want image fallback, you need to use state:

    function PropertyImage({ url, title }) {
      const [broken, setBroken] = React.useState(false);
    
      React.useEffect(() => setBroken(false), [url]); // reset when URL changes
    
      return broken || !url ? (
        <div className="flex items-center justify-center w-full h-full bg-gray-200">
          <span className="text-gray-500">No Image</span>
        </div>
      ) : (
        <img
          src={url}
          alt={title}
          className="object-cover w-full h-full"
          onError={() => setBroken(true)}
        />
      );
    }
    

    I think this is the only way.