Search code examples
next.jsnextjs-image

Add placeholder="blur" to multiple remote images in nextjs


I have a nextjs app which shows movies and their posters from an api and I am trying to add a blur effect to all images before they are fully loaded to be displayed. I found this comment on github discussion which uses plaiceholder but I couldn't find a way to make it work for multiple images. Here is some of my codes

// index.js

...
 return (
    <>
      <MovieList data={props.data.trending} title="Trending Right Now" />
      <MovieList data={props.data.popular} title="Popular" />
    </>
  );

export async function getStaticProps() {
  var promises = [];
  const api_key = '...'; // don't worry about api key I can make another one
  const urls = [
    `https://api.themoviedb.org/3/trending/movie/week?api_key=${api_key}`,
    `https://api.themoviedb.org/3/movie/popular?api_key=${api_key}&language=en-US&page=1`
  ];

  urls.forEach(function(url) {
    promises.push(
      axios.get(url).then(
        function(data) {
          return { success: true, data: data };
        },
        function() {
          return { success: false };
        }
      )
    );
  });
  const [trendingRes, popularRes] = await Promise.all(promises);

  const trending = trendingRes.success === false ? null : trendingRes.data.data;
  const popular = popularRes.success === false ? null : popularRes.data.data;

  return { props: { data: { trending, popular } } };
}
// MovieList.js

   {props.data
        ? props.data.results.map((movie, index) => {
            return (
              <>
                ...
                <div>
                  <Image
                    src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
                    alt="movie poster"
                    width="260"
                    height="391"
                    placeholder="blur" // doesn't work
                  />
                </div>
              </>
            );
          })
     : 'Not found'}

You can see full code here


Solution

  • I finally managed to work by using plaiceholder and converting images in each api to blurhash code during api calls and use that code along with react-blurhash in my movieList component

    // index.js 
    
      const [trendingRes, popularRes] = await Promise.all(
        urls.map((url) =>
          axios.get(url).then(
            (data) =>
              Promise.all(
                data.data.results.map((one) => {
                  return getPlaiceholder(
                    `https://image.tmdb.org/t/p/w500${one.poster_path}`
                  )
                    .then(({ blurhash, img }) => {
                      return { img, ...one, blurhash };
                    })
                    .catch(() => ({ ...one, img: "error", blurhash: null }));
                })
              ).then((values) => ({ success: true, data: values })),
    
            () => ({ success: false })
          )
        )
      ).then((data) =>  data);
    
    // movieList.js 
    
          {props.data
            ? props.data.map((movie, index) => {
                return (
                  <>
                    {movie.blurhash ? (
                      <div className="overflow-hidden relative block w-[260px] h-[391px]">
                        <BlurhashCanvas
                          punch={1}
                          hash={movie.blurhash.hash}
                          width={movie.blurhash.height}
                          height={movie.blurhash.width}
                          className="absolute left-0 top-0 w-full h-full inset-0"
                        />
    
                        <Image src={movie.img.src} width="260" height="391" />
                      </div>
        
                   // show image without blur effect if there is any blurhash error 
                    ) : ( 
                      <Image
                        src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
                        width="260"
                        height="391"
                        className="aspect-w-260 aspect-h-391"
                      />
                    )}
                  </>
                );
              })
            : "Not found"}