Dynamic image placeholder in Next.js

How to create a dynamic image placeholder?

TLDR

Check the full code here

Long version

Being able to immediately see something in the screen makes the app feel faster,
either in a fast connection or in a slow connecti…


This content originally appeared on DEV Community and was authored by Carlo Gino Catapang

How to create a dynamic image placeholder?

TLDR

Check the full code here

Long version

Being able to immediately see something in the screen makes the app feel faster,
either in a fast connection or in a slow connection.

The GIF below shows what a user will see for an image loaded using a slow internet connection.

It gives the impression that something is wrong with our app

Alternative

We can use the built-in placeholder image in Next.js,
but for some cases like a cover images, we might need something that resembles the actual image.

I will discuss in another blog when to prefer the default placeholder image over this dynamic one.

Better but not enough. The placeholder did not load quickly enough to address the first issue.
Also, The sudden change in colors makes it feel unnatural to the eye.
However, we can create a custom placeholder for each image, but do we really need to?

In this blog post, I will show how to create a dynamic placeholder image in Next.js.

Here's the general steps on how to solve the issue

  1. Create a placeholder metadata based on the image
  2. Create a svg component from the placeholder metadata
  3. Create a container for the image and placeholder
  4. Unmount the placeholder image after actual image completed loading
  5. Putting all the components together
  6. End to end integration in a nexjs page

1. Create a placeholder metadata based on the image

An easy way is to use plaiceholder

import {getPlaiceholder} from 'plaiceholder'

const placeholder = await getPlaiceholder(uri, { size: 64 })

// OR

const placeholder = await getPlaiceholder(uri)

// `size` decides how many blocks there will be
// ranges from 4 to 64
// default is 4

getPlaiceholder returns a promise of object with the following properties:

  • base64
  • blurhash
  • css
  • img
  • svg

For our purposes, we only need the img and svg property.

2. Create the svg component

The way to create the svg component will depend on how a placeholder metadata is created.
Here's a reference to plaiceholder's version.

To better visualize how to create the svg component here is a sample svg metadata

2.a. Create the svg container

The first element in the svg metadata is the svg element.
The second element in the svg metadata are the svg properties.

function BlurredImage({ svg }){
  const Svg = svg[0]
  const svgProps = svg[1]

  return <Svg {...svgProps}>
    {/* TODO */}
  </Svg>
}
2.b. Add the list of rects as svg children

The third element in the svg metadata is the list of rects, which will be rendered as svg children.

function BlurredImage({ svg }){
  // ...
  const rectangles = svg[2]

  return <Svg {...}>
    {rectangles.map((rect) => {
      const Rect = rect[0]
      const rectProps = rect[1]

      <Rect {...rectProps} key={`${rectProps.x}${rectProps.y}`} />
    )}}
  </Svg>
}

By doing step 2.a and 2.b, we can create a svg component that looks like this:

2.c. Blur the svg

The svg can be blurred to remove the pixelated look.

function BlurredImage({ svg }){
  const Svg = svg[0]
  const svgProps = svg[1]

  return <Svg
    style={{
      ...svgProps.style,
      filter: `blur(5px)`,
    }}
  >
  {...}
  </Svg>
}

Applying step 2.c will make the svg looks like this:

NOTE: Make sure to apply an appropriate filter value

For svg metadata with fewer rects, the result might looks like this:

3. Create a container; then, add the svg and image to display

The svg and Image can be optionally wrapped in a another component(for styling).
Spread the img props in the next Image component.

import Image from 'next/image'

function BlurredImage({ img }){
  return <Container>
    <Svg {...}>
    <Image {...img} />
  </Container>

  // Create the Container anyway you want
}

4. Unmount the placeholder image after actual image completed loading

Since the Image is already loaded, the placeholder component can be unmounted.
This can simply be achieved by using a useState and the Image's' onLoadingComplete callback method.

function BlurredImage({...}){
  const [hasPlaceholder, setHasPlaceholder] = useState(true)

  return <Container>
    {hasPlaceholder && <Svg {...} />}
    <Image {...} onLoadingComplete={() => setHasPlaceholder(false)} />
  </Container>
}

5. Putting all the components together

Here's the final Custom Image component with minor refactoring and default prop values:

import React, {useState} from 'react'
import styled from '@emotion/styled'
import Image from 'next/image'

export function BlurredImage({
  svg: [Svg, svgProps, rectangles],
  img,
  alt,
  style,
  blurLevel = 5,
  height = undefined,
  width = undefined,
  ...props
}) {
  const [hasPlaceholder, setHasPlaceholder] = useState(true)

  return (
    <Container style={style}>
      {hasPlaceholder && (
        <Svg
          {...svgProps}
          style={{
            ...svgProps.style,
            filter: `blur(${blurLevel}px)`,
          }}
        >
          {rectangles.map(([Rect, rectProps]) => (
            <Rect {...rectProps} key={`${rectProps.x}${rectProps.y}`} />
          ))}
        </Svg>
      )}

      <Image
        {...img}
        {...props}
        height={height}
        width={width}
        alt={alt}
        onLoadingComplete={() => setHasPlaceholder(false)}
      />
    </Container>
  )
}

const Container = styled.div`
  position: relative;
  overflow: hidden;
  height: 100%;
  width: 100%;
`;

6. End to end integration in a NexJs page

Time to integrate our custom component in a NextJs application

import {getPlaiceholder} from 'plaiceholder';
import {BlurringImage} from '../components/BlurringImage';

export default function IndexPage({img, svg}) {
  return (
    {/* <SomeHeaderComponent /> */}
    <BlurringImage
      img={img}
      svg={svg}
      layout="responsive"
      width={1200}
      height={800}
    />
  )
}

// or getServerSideProps depending on your needs
export async function getStaticProps() {
  const uri = 'https://i.imgur.com/gf3TZMr.jpeg;

  const {img, svg} = await getPlaiceholder(uri, {
    size: 64,
  });

  return {
    props: {
      img,
      svg,
    },
  }
}

Here's the final result:

The web page seems to be loading faster even on a slow internet connection, and the transition of image seems to be more natural.

Conclusion

By adding a dynamic placehoder image, the users' experience will improve due to an immediate feedback which
gives the impression that the application is working faster.
There's no need to stare at an empty screen while waiting for an image to load, specially on a slower network.
Also, the transition seems to be more natural as the placeholder image is derived from the original image.


This content originally appeared on DEV Community and was authored by Carlo Gino Catapang


Print Share Comment Cite Upload Translate Updates
APA

Carlo Gino Catapang | Sciencx (2021-11-14T14:12:56+00:00) Dynamic image placeholder in Next.js. Retrieved from https://www.scien.cx/2021/11/14/dynamic-image-placeholder-in-next-js/

MLA
" » Dynamic image placeholder in Next.js." Carlo Gino Catapang | Sciencx - Sunday November 14, 2021, https://www.scien.cx/2021/11/14/dynamic-image-placeholder-in-next-js/
HARVARD
Carlo Gino Catapang | Sciencx Sunday November 14, 2021 » Dynamic image placeholder in Next.js., viewed ,<https://www.scien.cx/2021/11/14/dynamic-image-placeholder-in-next-js/>
VANCOUVER
Carlo Gino Catapang | Sciencx - » Dynamic image placeholder in Next.js. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/11/14/dynamic-image-placeholder-in-next-js/
CHICAGO
" » Dynamic image placeholder in Next.js." Carlo Gino Catapang | Sciencx - Accessed . https://www.scien.cx/2021/11/14/dynamic-image-placeholder-in-next-js/
IEEE
" » Dynamic image placeholder in Next.js." Carlo Gino Catapang | Sciencx [Online]. Available: https://www.scien.cx/2021/11/14/dynamic-image-placeholder-in-next-js/. [Accessed: ]
rf:citation
» Dynamic image placeholder in Next.js | Carlo Gino Catapang | Sciencx | https://www.scien.cx/2021/11/14/dynamic-image-placeholder-in-next-js/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.