Dealing with dynamic images in Gatsby

Gatsby launched a new image component that solves this problem. Más Informacion.

Showing images from Gatsby file system is very easy with a combination of GraphQL and gatsby-image, but... there is a catch.

The problem is when you get the image names dynamically from a data source or CMS, and you can't know in advance (build time) the name to do the queries in GraphQL because you can't interpolate variables in GraphQL queries in Gatsby right now.

I've seen some solutions which basically consist in query ALL the files and then inside the component match the name of the one you want to show.

This approach works like a charm when you don't have many images, but, in my case, there was ~800. Since Gatsby saves all the GraphQL queries resulting in a page-data.json file, it can be really big. For example, here is a comparison of a file with ~800 images and no images:

Gatsby JSON file with ~800 images

page-data-compare-01

No Images

page-data-compare-02

My solution

Even though I tried anything I could think of using local images on Gatsby, I couldn't get an acceptable solution for my case, so I decided to go another route, an image hosting service, and I picked Cloudinary.

Cloudinary is a service to host media, but you can't apply transformations like resize, crop, and even change the format when you serve them; all this by building a URL with parameters.

I uploaded all the images to Cloudinary using their Web UI, drag and drop, and I was up and running with my custom folder structure.

After that, I created a simple React component which takes: name, category, and image size, and it built Cloudinary URL and the last touch: LazyLoad, never forget to LazyLoad your images, for this I just used react-lazy-load-image-component, the final code:

import React from 'react'
import { LazyLoadImage } from 'react-lazy-load-image-component'

const getCloudinaryImageUrl = ({ category, name, size }) =>
    `https://res.cloudinary.com/CLOUDINARY_CLOUD/image/upload/f_auto,q_auto,c_pad,w_${size},h_${size}/lapulpe/${category}/${name}`

const CloudinaryImage = ({ category, name, size }) => {
    const url = getCloudinaryImageUrl({ category, name, size })
    return (
        <LazyLoadImage
            style={{ marginBottom: '0' }}
            alt={name}
            height={size}
            src={url}
            width={size}
        />
    )
}

export default CloudinaryImage

The result

  1. Less build time since Gatsby doesn't have to process the images
  2. Much smaller JSON bundle
  3. No extra GraphQL queries
  4. Being able to publish new images without having to rebuild

Copyright © 2024. Design and code by myself with Next.js. Fork it and create yours