Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to have a fallback image in NextJS?

Recently, I have been working on a project in NextJS which uses the YoutubeAPI to fetch video information, including thumbnail URLs.

The thumbnail URL for a full resolution image looks like this: https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg

However, sometimes YouTube fails to generate a full-resolution image, and in that case, the image is not displayed on my webpage.

In the case that the image with the URL https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg does not exist I wish to use another URL like https://i.ytimg.com/vi/${videoId}/hqdefault.jpg

What is the best way to handle this with next/image?

like image 811
m_ognjen Avatar asked Apr 05 '21 07:04

m_ognjen


People also ask

How do I add a fallback image?

Place your image in a container (it might already be in one). Make the container have the same width and height as the image. Set the fallback image as the background image of the container. Set the remote image as the background image of your img tag.

How does Nextjs optimize images?

The default loader for Next.js applications uses the built-in Image Optimization API, which optimizes images from anywhere on the web, and then serves them directly from the Next.js web server.

Where do I put images in Nextjs?

Firstly import the image component in next. js and define the image path in src props in the image component. Now your image shows in the browser. In the image component, src, width, and height props are required by next.

How do I display images in next JS?

There are two ways you can display images in Next.js, you either use the conventional <img> tag or a specialized <Image/> component that is unique to Next. The differences between these tags are quite much, but they pretty much do the same thing; which is to display images to the browser.

How do I use the <image> component in my next project?

To use the <Image/> component in your Next.js project, the first thing you need to do is import it into your desired component from Next’s library: The next step is to add the JSX syntax to your code with at least an src, width and height property:

How do I handle onerror in nextjs?

Now normally you can handle this all on the client side using the image tags onError listener. There is a bit of a trick to get this idea to work with NextJS. The onError event may not be fired when rendering via SSR.

How do I render images on a next page?

You can render images on your Next.js webpage without much hassle with the <img> tag, provided the image, if stored locally, is moved to the /public folder directory. But the issue of optimization remains. The <img> tag renders images to the webpage with their original size and resolution regardless of the viewport the image is being rendered on.


2 Answers

You can create a custom image component that extends the built-in next/image and adds the fallback logic if the image fails to load by triggering the onError callback.

import React, { useState } from 'react';
import Image from 'next/image';

const ImageWithFallback = (props) => {
    const { src, fallbackSrc, ...rest } = props;
    const [imgSrc, setImgSrc] = useState(src);

    return (
        <Image
            {...rest}
            src={imgSrc}
            onError={() => {
                setImgSrc(fallbackSrc);
            }}
        />
    );
};

export default ImageWithFallback;

Then, you can directly use the custom component instead of next/image as follows:

<ImageWithFallback
    key={videoId}
    layout="fill"
    src={`https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg`}
    fallbackSrc={`https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`}
/>

Passing a key prop to trigger a re-render on videoId change.

like image 161
juliomalves Avatar answered Sep 20 '22 14:09

juliomalves


@juliomalves has given 99% percent of the answer, however I would like to add to it. There is a problem when changing the src in his solution, as the image would not update because it's getting the imgSrc value which is not getting updated. This is my addition to his answer:

import React, { useState } from 'react';
import Image from 'next/image';

const ImageFallback = (props) => {
    
    const { src, fallbackSrc, ...rest } = props;
    const [imgSrc, setImgSrc] = useState(false);
    const [oldSrc, setOldSrc] = useState(src);
    if (oldSrc!==src)
    {
        setImgSrc(false)
        setOldSrc(src)
    }
    return (
        <Image
            {...rest}
            src={imgSrc?fallbackSrc:src}
            onError={() => {
                setImgSrc(true);
            }}
        />
    );
};

export default ImageFallback;

Now imgSrc is used only as a flag, and there is tracking of src value, which helps to change the image even if you had an image that had the fallback image on before.

like image 28
Panos Paschalis Avatar answered Sep 20 '22 14:09

Panos Paschalis