useImageOnLoad()

A simple React Hook that helps you improve UX while images are loading. Rather than having an image that "unfolds" from top to bottom, we load a smaller version first which will be blurred and which will be replaced by the normal size image once loaded.

You can see an implementation of this hook on this site: React Gallery. Don't hesitate to take the logic and write your own CSS instead.

The Hook

1import { CSSProperties, useState } from 'react'
2
3interface ImageStyle {
4 thumbnail: CSSProperties
5 fullSize: CSSProperties
6}
7
8interface ImageOnLoadType {
9 handleImageOnLoad: () => void
10 css: ImageStyle
11}
12
13function useImageOnLoad(): ImageOnLoadType {
14 const [isLoaded, setIsLoaded] = useState<boolean>(false)
15
16 // Triggered when full image will be loaded.
17 const handleImageOnLoad = () => {
18 setIsLoaded(true)
19 }
20
21 const css: ImageStyle = {
22 // Thumbnail style.
23 thumbnail: {
24 visibility: isLoaded ? 'hidden' : 'visible',
25 filter: 'blur(8px)',
26 transition: 'visibility 0ms ease-out 500ms',
27 },
28 // Full image style.
29 fullSize: {
30 opacity: isLoaded ? 1 : 0,
31 transition: 'opacity 500ms ease-in 0ms',
32 },
33 }
34
35 return { handleImageOnLoad, css }
36}
37
38export default useImageOnLoad

Usage

1import React, { CSSProperties } from 'react'
2
3import useImageOnLoad from './useImageOnLoad'
4
5export default function Component() {
6 const { handleImageOnLoad, css } = useImageOnLoad()
7
8 const style: { [key: string]: CSSProperties } = {
9 wrap: {
10 position: 'relative',
11 width: 600,
12 height: 600,
13 },
14 image: {
15 position: 'absolute',
16 width: `100%`,
17 height: `100%`,
18 },
19 }
20
21 return (
22 <div style={style.wrap}>
23 {/* Small image load fast */}
24 <img
25 style={{ ...style.image, ...css.thumbnail }}
26 src="https://via.placeholder.com/150"
27 alt="thumbnail"
28 />
29 {/* Full size image */}
30 <img
31 onLoad={handleImageOnLoad}
32 style={{ ...style.image, ...css.fullSize }}
33 src="https://via.placeholder.com/600"
34 alt="fullImage"
35 />
36 </div>
37 )
38}

Created:
10 months ago