Serving Remote Images in Gatsby.js using Gatsby-image w/o GraphQL

William Imoh

You probably want to use a hosted image with Gatsby-image in your Gatsby.js project. You don’t need to have this image stored locally, and you won't need to fetch it with a GraphQL query. We’ll see how in this jam.

Part 1 of this project chronicles using remote images from Cloudinary in Gatsby.js projects through GraphQL queries. In this part, we’ll discuss how to —

  • Utilize a remote image stored on Cloudinary with gatsby-image.
  • Serve fluid or fixed images with gatsby-image without a GraphQL query.
  • Render optimized, responsive images with gatsby-image.
  • Transform those images with Cloudinary.

You don’t need to read part 1 to go through this post.


We completed this project in a Codesandbox, and you can fork it to see all the code in action. To use the sandbox, you need to create a .env file, and add your Cloudinary API key and secret. Follow the format specified in the .env.example in the root directory of the projects.

Suppose you don’t need to upload local images to your Cloudinary account. In that case you don’t need to specify your API key and secret in the plugin configuration.

We have an image on Cloudinary which we will source and transform from


Specifically, this post guides you through two processes:

  1. Fetch an image from Cloudinary with the getFluidImageObject API of gatsby-transformer-cloudinary and render that image with gatsby-image. You can fetch any image from your Cloudinary account. Also, even after applying the Cloudinary transformations that you specify, the API optimizes that image with gatsby-image during the build process.
  2. Create a user interface and typography with Chakra-ui.

Project Installation

We implement this feature on an existing Gatsby.js project. You can fork this base Codesandbox containing the dependencies installed, configuration, and layout. Otherwise, we install Gatsby.js globally and create a new project with

1npm install -g gatsby-cli && gatsby new gtc-demo

We proceed to install the required dependencies with

1cd gtc-demo && npm i --save gatsby-transformer-cloudinary dotenv @chakra-ui/gatsby-plugin @chakra-ui/react @emotion/react @emotion/styled framer-motion

Project setup

Sign up for a Cloudinary account. Cloudinary offers a free tier, which is more than adequate for small to medium projects. Once signed up, note your cloud name, API key, and API secret for later use.

With the required dependencies installed, we need to configure the gatsby plugins in gatsby-config.js - a Gatsby file in the project’s root directory to manage plugin configurations.

First, we import dontenv, a required dependency to setup environment variables. Also, we update the site meta data to match the current project.

2 module.exports = {
3 siteMetadata: {
4 title: `Gatsby x Cloudinary`,
5 description: `Serve remote images in your Gatsby app using gatsby-image`,
6 author: `@gatsbyjs`
7 },
8 }

Next, we configure installed plugins including react-helmet, gatsby-source-filesystem, @chakra-ui/gatsby-plugin and gatsby-transformer-cloudinary.

1module.exports = {
2 {other config goes in here},
4 plugins: [
5 `gatsby-plugin-react-helmet`,
6 {
7 resolve: `gatsby-source-filesystem`,
8 options: {
9 name: `cloudinary-images`,
10 path: `${__dirname}/src/remote-images`
11 }
12 },
13 {
14 resolve: "@chakra-ui/gatsby-plugin",
15 options: {
16 isUsingColorMode: true
17 }
18 },
19 {
20 resolve: "gatsby-transformer-cloudinary",
21 options: {
22 cloudName: process.env.CLOUDINARY_CLOUD_NAME,
23 apiKey: process.env.CLOUDINARY_API_KEY,
24 apiSecret: process.env.CLOUDINARY_API_SECRET,
25 uploadFolder: "gtc-art-gallery"
26 }
27 }
28 ]
29 }

In the configuration file, gatsby-source-filesystem, a source plugin, sources file nodes into the Gatsby data layer. Here, we’ve sourced all the images in a folder, which are uploaded once to Cloudinary on build.

We created the remote-images folder in the src directory of the project. We uploaded all the images into the folder.

We proceed to create a .env file in the project’s root directory, and add our Cloudinary credentials as specified below:

1# Find this at
4 # Generate an API key pair at
5 CLOUDINARY_API_KEY=xxxxxxxxxxxxxx
6 CLOUDINARY_API_SECRET=xxxxxxxxxxxxxxxxxxx

Next, we start the development server on localhost:8000 by running the command:

1gatsby develop

All the images in the remote-images folder are uploaded to Cloudinary and added to file nodes for the returned URLs are created in Gatsby’s GraphQL layer.

Image fetching with getFluidImageObject and getFixedImageObject

Even though both getfluidImageObject and getFixedImageObject fetch images from Cloudinary accounts with multiple or chained transformations, the two APIs vary as follows:

getFluidImageObject returns a fluid image object with the breakpoints you specified. If you did not set any, it returns breakpoints with a maximum width of 1000.

getFixedImageObject returns a fixed-width image object.

Passing the response from the asynchronous calls of the functions to the fluid or fixed property of the gatsby-image <Image/> component renders the image. Both APIs fetch images with a single asynchronous operation, hence no need for GraphQL queries.

Page creation for a single image

We create a webpage that contains an image fetched with getFluidImageObject. We apply the same layout from part 1 (you can use any layout).

Gatsby.js builds pages from the JavaScript (JS) files in the src/pages directory.

We do the following:

  1. Create a JavaScript file titled single.js in src/pages. Gatsby.js adds single.js to the route /single.

  2. Import the required modules into single.js with this code:

1import React, { useEffect, useState } from "react"
2 import Layout from "../components/layout"
3 import { getFluidImageObject } from "gatsby-transformer-cloudinary/api"
4 import Image from "gatsby-image"
5 import { Box, Heading, Text } from "@chakra-ui/react";
  1. Create and export a functional component titled SinglePage. In the function, fetch a single Cloudinary image with a public ID of your choice. Below is the code, in which the public ID is gatsby-source-cloudinary/penguin:
1const SinglePage = () => {
2 const [fluid, setFluid] = useState(undefined)
4 useEffect(() => {
5 async function getData() {
6 const res = await getFluidImageObject({
7 public_id: "gatsby-source-cloudinary/penguin",
8 cloudName: "chuloo",
9 originalHeight: 400,
10 originalWidth: 500,
11 maxWidth: 800,
12 transformations: ["e_replace_color:purple", "a_hflip", "f_auto", "q_auto"],
13 })
14 setFluid(res)
15 }
17 getData()
18 }, [])
20 return (
21 // return statement goes here
22 )
23 }
25 export default SinglePage

In the above code, getFluidImageObject is an asynchronous function called on page load using useEffect. Once that function returns the image object, we store it in the component’s fluid state with useState.

Next, we pass the following keys to getFluidImageObjects in its object argument:

  • public_id: The public ID of the Cloudinary image
  • cloudName: The cloud name of your Cloudinary account
  • originalHeight: The height of the image to be fetched
  • originalWidth: The width of the image to be fetched
  • transformations: The transformations Cloudinary applies to the returned image

See the complete keys along with their optional, and required, arguments.

getFluidImageObject passes Cloudinary transformations in the transformation’s key to add a purple effect, horizontally flip the image with e_replace_color:purple and a_hflip, and apply optimization transformations for quality and format.

With getFluidImageObjects, you can fetch with gatsby-image any image in your Cloudinary account for display on the page.

Gatsby-image lazy-loads images that we sourced and transformed with getFluidImageObject.

In the SinglePage component, we'll proceed to return the JSX elements to render the page. The global layout is used, <SEO/> element to provide search engine optimization data for the page, and the gatsby-image <Image/> component to render the fluid image data returned by getFluidImageObjects.

1return (
2 <Layout>
3 <SEO title={"single"} />
4 <Box>
5 <Heading as={"h1"} size={"lg"} m={5} textAlign={"center"}>
6 Single Fluid Image
7 </Heading>
8 <Box
9 maxWidth={[350, 400, 500]}
10 mx={"auto"}
11 shadow="md"
12 borderWidth="1px"
13 rounded={"lg"}
14 p={3}
15 >
16 {fluid ? <Image fluid={fluid} /> : "loading..."}
17 </Box>
18 <Box my={30}>
19 <Text>
20 This is a single image sourced directly from Cloudinary. This image
21 can be any image in your Cloudinary account, the public ID of the
22 image is required to source this images for use in gatsby-image{" "}
23 </Text>
24 </Box>
25 </Box>
26 </Layout>

Chakra-ui styles the components for a responsive layout and responsive typography in the above code, with breakpoints for mobile, tablet, and desktop.

Now we restart the development server for the updated look of the webpage.


You now know how to fetch images with getFluidImageObject from Cloudinary into gatsby-image for Gatsby.js projects. getFixedImageObject conveys fixed images for gatsby-image in a similar manner.

Coming up is part 3, which describes how to add a dark mode as a toggle for the website and convert it to a progressive web app (PWA) with only a few code lines. Pretty amazing.

You can find the links below useful

William Imoh

Creating tech solutions and talking about them

William is a developer, developer advocate, and product manager. When he's not working on technology, he's organizing game nights, making drinks, or playing basketball.