Show a loading state while images load
Websites load in multiple steps.
First, the server sends the HTML to the browser. Then, the browser reads the HTML to find out what other files it needs to load, and fetches them.
If your website is like most, you probably have some images on your page. Users will see a delay between the time the page loads and when the images actually appear.
The browser won't start to fetch the images until they appear in the HTML. There are a few reasons an image might not be in the initial HTML response.
- It could be hidden until a user interaction, like expanding an accordion.
- It might be on a different page, users get there with client side navigations that don't pull more HTML from the server.
In those cases, you can tell the browser to start fetching the image immediately with link tags in the head of your HTML.
<link rel="preload" as="image" href="https://example.com/image.jpg"/>
The more common case is that we want to show images at the same time as the rest of the content that surrounds them.
Showing a placeholder until the image loads
If we have a placeholder image already loaded, we can use React to show it until the real image is ready, then swap them out.
function ImageWithPlaceholder({ src, placeholderSrc, onLoad, ...props}: { onLoad?: () => void placeholderSrc?: string} & DetailedHTMLProps< ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>) { const [imgSrc, setImgSrc] = useState( placeholderSrc || src, ) // Store the onLoad prop in a ref to stop new Image() from re-running const onLoadRef = useRef(onLoad) useEffect(() => { onLoadRef.current = onLoad }, [onLoad]) useEffect(() => { const img = new Image() img.onload = () => { setImgSrc(src) if (onLoadRef.current) { onLoadRef.current() } } img.src = src }, [src]) return <img src={imgSrc} {...props} />}
This component works like a drop in replacement for any image.
return ( <div> <p> Some text </p> <ImageWithPlaceholder src="https://example.com/image.jpg" alt="A cool image" placeholderSrc="https://example.com/placeholder.jpg" /> <p> Some more text </p> </div>)
Hiding the content until the image loads
Another solution is to wait until the image loads to show the whole section.
Use a custom hook that returns a boolean to indicate whether the image has loaded.
If it hasn't, you can show a loading state instead of the image.
function useImageLoaded(src: string) { const [loaded, setLoaded] = useState(false) useEffect(() => { if (!src) return const img = new Image() img.src = src img.onload = function () { setLoaded(true) } }, [src]) return loaded}
Then, use that hook to conditionally render the section with the image. By the time the section is rendered, the image will have loaded and will appear at the same time as the text.
const shouldShowSection = useImageLoaded( "https://example.com/image.jpg",)if (!shouldShowSection) { return <p> Loadingā¦ </p>}return ( <div> <p> Some text </p> <img src="https://example.com/image.jpg" alt="A cool image" /> <p> Some more text </p> </div>)