{"id":27783,"date":"2022-03-21T19:23:35","date_gmt":"2022-03-21T19:23:35","guid":{"rendered":"http:\/\/Create-an-Image-Gallery-with-Remote-Images-in-Gatsby.js"},"modified":"2025-10-26T02:50:39","modified_gmt":"2025-10-26T09:50:39","slug":"create-an-image-gallery-with-remote-images-in-gatsby-js","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\/","title":{"rendered":"Create a Gallery With Remote Images Using Cloudinary"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Gatsby, an open-source tool for building high-performance JAMstack apps, boasts numerous plugins that extend its capabilities. For example, by leveraging Gatsby\u2019s source plugins, we can source data from multiple services, APIs, and even Excel spreadsheets.<\/p>\n<p><a href=\"https:\/\/cloudinary.com\/?utm_source=scotchio&amp;utm_medium=sponsored_placement&amp;utm_campaign=gatsby-source-plugin\">Cloudinary<\/a> is a cloud-based, end-to-end media-management platform on which you can store, dynamically optimize, and responsively deliver images and videos for both desktop or mobile apps.<\/p>\n<p>The <a href=\"https:\/\/github.com\/Chuloo\/gatsby-source-cloudinary\">Gatsby-Source-Cloudinary plugin<\/a> fetches optimized media assets from a folder on Cloudinary in a Gatsby project. Additionally:<\/p>\n<ul>\n<li>You can declaratively query Cloudinary\u2019s media assets alongside other application data with GraphQL.<\/li>\n<li>Cloudinary delivers media assets through a content delivery network (CDN), reducing the app- bundle size on app build.<\/li>\n<li>Gatsby-Source-Cloudinary automatically optimizes the quality and format of the media files delivered by Cloudinary.<\/li>\n<\/ul>\n<p>Thus, you have the best of two worlds: high-performance apps along with optimized delivery of media assets.<\/p>\n<p>This tutorial takes you through building a <a href=\"https:\/\/cloudinary.com\/guides\/responsive-images\/responsive-image-gallery\">responsive image gallery<\/a> with Gatsby and images from Cloudinary. Because Gatsby is written in React.js, you must have a working knowledge of JavaScript and React.js.<\/p>\n<h2>Sandbox<\/h2>\n<p>We completed this project in <a href=\"https:\/\/codesandbox.io\/s\/gallery-images-in-gatsbyjs-using-gatsby-source-cloudinary-tnc1l?file=\/gatsby-config.js\">Codesandbox<\/a>. You can fork it to run the project. To run the project, you need to specify your Cloudinary account API Key, Secret, and cloud name. You can do this using a .env file in the project\u2019s root directory, or add the credentials directly in Codesandbox using the \u201cServer control panel\u201d menu option on the left.<\/p>\n<p>See the .env.example file in the project\u2019s root &#8211; a template for creating environment variables.<\/p>\n<p><a href=\"https:\/\/codesandbox.io\/s\/gallery-images-in-gatsbyjs-using-gatsby-source-cloudinary-tnc1l?file=\/gatsby-config.js\">https:\/\/codesandbox.io\/s\/gallery-images-in-gatsbyjs-using-gatsby-source-cloudinary-tnc1l?file=\/gatsby-config.js<\/a><\/p>\n<h2><strong>Node.js and Gatsby.js installation<\/strong><\/h2>\n<p>We need Node.js, and its package manager, npm, for this project. To check if we have them installed on our system, we run the following command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    node -v &amp;&amp; npm -v\n<\/code><\/span><\/pre>\n<p>If the output shows no version numbers, go to <a href=\"https:\/\/nodejs.org\/\">Nodejs.org<\/a> to download and install Node.js, which ships with npm.<\/p>\n<p>Next, we install the Gatsby.js CLI using the terminal command.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    npm install -g gatsby-cli\n<\/code><\/span><\/pre>\n<h2><strong>Cloudinary account creation<\/strong><\/h2>\n<p>To source images from Cloudinary, we must first <a href=\"https:\/\/bit.ly\/2v3sy4N\">set up<\/a> <a href=\"https:\/\/cloudinary.com\/users\/register\/free?utm_source=scotchio&amp;utm_medium=sponsored_placement&amp;utm_campaign=gatsby-source-plugin\">an account<\/a> there. Cloudinary offers a generous, free tier account that\u2019s largely adequate for starter projects, and scales with the project.<\/p>\n<p>Afterward, we do the following:<\/p>\n<ol>\n<li>Upload the images for the image gallery to a folder in our media library on Cloudinary. We give the folder a name of our choice, e.g., <em>gallery<\/em>.<\/li>\n<li>We obtain our API key and API secret from the Cloudinary dashboard.<\/li>\n<\/ol>\n<h2>Project creation<\/h2>\n<p>Gatsby.js ships starters used to generate new projects with a base setup quickly. To create a new Gatsby.js project in a folder of our choice with the default starter, we use this command:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    gatsby <span class=\"hljs-keyword\">new<\/span> cloudinary-site\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Next, we navigate to the project directory and start a new development server on <a href=\"http:\/\/localhost:8000\">http:\/\/localhost:8000<\/a> with this command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    cd cloudinary-site &amp;&amp; gatsby develop\n<\/code><\/span><\/pre>\n<p>Because it contains GraphQL, Gatsby simultaneously creates a GraphQL IDE on <code>http:\/\/localhost:8000\/___graphql<\/code>, with which we\u2019ll build GraphQL queries later.<\/p>\n<p>Now, we go to <a href=\"http:\/\/localhost:8000\/\">http:\/\/localhost:8000<\/a> to see the new Gatsby project, which looks something like this:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/media_jams\/s_6BBEC4479471BE6C6EC8AF9E9E07B7BE07332AA6CC9D2F1E261F544B690F6C3B_1615198236735_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"1155\"\/><\/p>\n<h2><strong>Installation of Additional Packages<\/strong><\/h2>\n<p>We need two other packages for this project:<\/p>\n<ul>\n<li>The <a href=\"https:\/\/www.npmjs.com\/package\/dotenv\">dotenv<\/a> module loads environment variables from a <code>.env<\/code> file.<\/li>\n<li>The <a href=\"https:\/\/www.npmjs.com\/package\/gatsby-source-cloudinary\">gatsby-source-cloudinary<\/a> plugin fetches optimized images from Cloudinary.<\/li>\n<\/ul>\n<p>We install them with NPM, using the command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    npm i --save dotenv gatsby-source-cloudinary\n<\/code><\/span><\/pre>\n<h2><strong>Plugin configuration<\/strong><\/h2>\n<p>We configure all plugins, including those that accompany the default starter, in the <em>gatsby-config.js<\/em> file, which resides in the root directory of Gatsby projects. We do the following:<\/p>\n<p>We import the <em>dotenv<\/em> module we installed earlier to the <em>gatsby-config.js<\/em> file, and configured the <em>gatsby-source-cloudinary<\/em> plugin with this code:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'dotenv'<\/span>).config();\n    \n    <span class=\"hljs-built_in\">module<\/span>.exports = {\n      ...\n      plugins:&#91;\n      ...\n      {\n          <span class=\"hljs-attr\">resolve<\/span>: <span class=\"hljs-string\">`gatsby-source-cloudinary`<\/span>,\n          <span class=\"hljs-attr\">options<\/span>: {\n            <span class=\"hljs-attr\">cloudName<\/span>: process.env.CLOUDINARY_CLOUD_NAME,\n            <span class=\"hljs-attr\">apiKey<\/span>: process.env.CLOUDINARY_API_KEY,\n            <span class=\"hljs-attr\">apiSecret<\/span>: process.env.CLOUDINARY_API_SECRET,\n            <span class=\"hljs-attr\">resourceType<\/span>: <span class=\"hljs-string\">`image`<\/span>,\n            <span class=\"hljs-attr\">prefix<\/span>: <span class=\"hljs-string\">`gatsby-source-cloudinary\/`<\/span> \n          }\n        }\n      ]\n    } \n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The gatsby-source-cloudinary plugin takes these options:<\/p>\n<ul>\n<li>\n<code>cloudName<\/code>, <code>apiKey<\/code>, and <code>apiSecret<\/code>: These are credentials from our Cloudinary console, stored as three separate environment variables for security.<\/li>\n<li>\n<code>resourceType<\/code>: This is the media assets\u2019 resource type: either an image or a video.<\/li>\n<li>\n<code>prefix<\/code>: This is the folder (in our Cloudinary account) in which the files reside. In the example above, I named this folder <code>gatsby-source-cloudinary<\/code> . Assign a name of your choice.<\/li>\n<\/ul>\n<p>Other options are <code>type<\/code>, <code>tags<\/code>, and <code>maxResult<\/code>.<\/p>\n<p>In the root directory of our project,  we create a .env file to hold our Cloudinary credentials.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    CLOUDINARY_API_KEY=xxxxxxxxxxxxxx\n    CLOUDINARY_API_SECRET=xxxxxxxxxxxxxxxxxxxx\n    CLOUDINARY_CLOUD_NAME=xxxxx\n<\/code><\/span><\/pre>\n<p>The <code>dotenv<\/code> package exposes those environment variables in the project.<\/p>\n<p>We restart the Gatsby development server to see the plugin in action, as in this example:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/media_jams\/s_F2DB697C20DB2900E952897A6582F157DAE4391AB0ADA1EBD0D8E2F67307CC1F_1579734262094_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1736\" height=\"338\"\/><\/p>\n<p>Note the <code>info<\/code> log, which displays the CloudinaryMedia nodes. Those images are ready to be queried in Gatsby components.<\/p>\n<h2><strong>Image Gallery Creation<\/strong><\/h2>\n<p>In the <code>src\/components<\/code> folder, we create a component file called <code>ImageGallery.js<\/code> and add the React functional component to the file to create the image gallery.<\/p>\n<p>First, we import all required dependencies and then query all the Cloudinary images sourced into the <code>CloudinaryMedia<\/code> nodes with the <code>useStaticQuery<\/code> hook.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react'<\/span>\n    <span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'.\/gallery.css'<\/span>\n    <span class=\"hljs-keyword\">import<\/span> {useStaticQuery, graphql} <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'gatsby'<\/span>\n    <span class=\"hljs-keyword\">const<\/span> ImageGallery = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> data = useStaticQuery(graphql<span class=\"hljs-string\">`query CloudinaryImage {\n            allCloudinaryMedia {\n              edges {\n                node {\n                  secure_url\n                }\n              }\n            }\n          }`<\/span>\n        )\n      <span class=\"hljs-keyword\">return<\/span>(...)\n        <span class=\"hljs-keyword\">const<\/span> clImages = data.allCloudinaryMedia.edges\n      <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> ImageGallery\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Following this, we loop through those image URLs to create a gallery with the component.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">const<\/span> ImageGallery = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n      <span class=\"hljs-comment\">\/\/ data fetching goes in here<\/span>\n    \n      <span class=\"hljs-keyword\">return<\/span> (\n            <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"image-grid\"<\/span>&gt;<\/span>\n                {clImages.map((image, index) =&gt; (\n                      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"image-item\"<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{<\/span>`${<span class=\"hljs-attr\">index<\/span>}<span class=\"hljs-attr\">-cl<\/span>`}&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{image.node.secure_url}<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">no<\/span> <span class=\"hljs-attr\">alt<\/span> <span class=\"hljs-attr\">:<\/span>(\"} \/&gt;<\/span>\n                      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    ))\n                }\n              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n          )\n      }\n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> ImageGallery\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>To style the grid gallery component, we create a file called <code>.\/gallery.css<\/code> in <code>src\/components<\/code> . We add the CSS styles to the file, as follows:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\">    <span class=\"hljs-selector-class\">.image-grid<\/span> {\n        <span class=\"hljs-attribute\">display<\/span>: grid;\n        <span class=\"hljs-attribute\">grid-gap<\/span>: <span class=\"hljs-number\">10px<\/span>;\n        <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(auto-fill, minmax(<span class=\"hljs-number\">250px<\/span>, <span class=\"hljs-number\">1<\/span>fr));\n        <span class=\"hljs-attribute\">grid-auto-rows<\/span>: <span class=\"hljs-built_in\">minmax<\/span>(<span class=\"hljs-number\">50px<\/span>, auto);\n      }\n      <span class=\"hljs-selector-class\">.image-grid<\/span> <span class=\"hljs-selector-class\">.image-item<\/span><span class=\"hljs-selector-pseudo\">:nth-child(5n)<\/span> {\n        <span class=\"hljs-attribute\">grid-column-end<\/span>: span <span class=\"hljs-number\">2<\/span>;\n      }\n      <span class=\"hljs-selector-class\">.image-grid<\/span> <span class=\"hljs-selector-tag\">img<\/span> {\n        <span class=\"hljs-attribute\">display<\/span>: flex;\n        <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\n        <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">100%<\/span>;\n        <span class=\"hljs-attribute\">object-fit<\/span>: cover;\n      }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The above styling renders your image gallery into a beautiful, responsive masonry grid.<\/p>\n<p>We replace the existing content of the <code>index.js<\/code> file in the <code>src\/pages<\/code> folder with the newly created <code>ImageGallery<\/code> component.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> Layout <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/layout\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> SEO <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/seo\"<\/span>\n    <span class=\"hljs-keyword\">import<\/span> ImageGallery <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/ImageGallery\"<\/span>\n    <span class=\"hljs-keyword\">const<\/span> IndexPage = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> (\n      <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Layout<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">SEO<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">\"Home\"<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>Image Gallery<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Here's a Gatsby site with optimized images in a masonry grid, served from <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"https:cloudinary.com\"<\/span> <span class=\"hljs-attr\">target<\/span>=<span class=\"hljs-string\">\"_blank\"<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"noopener noreferrer\"<\/span>&gt;<\/span>Cloudinary<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">marginBottom:<\/span> `<span class=\"hljs-attr\">1.45rem<\/span>` }}&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ImageGallery<\/span>\/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Layout<\/span>&gt;<\/span><\/span>\n    )\n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> IndexPage\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Gatsby automatically creates dynamic routes for single pages in the  <code>src\/pages<\/code> folder, with <code>index.js<\/code> being the root or home page.<\/p>\n<blockquote>\n<p><strong>Optional:<\/strong>. Feel like tweaking the styles a bit? In the <code>src\/components\/header.js<\/code> file, change the <code>background<\/code> style property of the <code>&lt;header&gt;<\/code> element to <code>#002954<\/code>.<\/p>\n<\/blockquote>\n<p>We rewrote the site title, pulled from the site metadata (the <code>title<\/code> property) specified in <code>gatsby-config.js<\/code> to \u201cGatsby-Cloudinary Image Gallery\u201d.<\/p>\n<p>We restart the development server and have a look at the updated page. Here\u2019s an example:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/media_jams\/s_F2DB697C20DB2900E952897A6582F157DAE4391AB0ADA1EBD0D8E2F67307CC1F_1579735865260_gsc-sample.gif\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"640\" height=\"444\"\/><\/p>\n<p>To create a deployable, optimized build, we run the following terminal command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    gatsby build\n<\/code><\/span><\/pre>\n<h2>Summary<\/h2>\n<p>In this post, we learned how to leverage the performance benefits of Gatsby.js\u2019s build process to deliver remote images stored on Cloudinary. We built an image gallery with photos from Cloudinary.<\/p>\n<p>You can check out the <a href=\"https:\/\/www.npmjs.com\/package\/gatsby-transformer-cloudinary\">Gatsby-Transformer-Cloudinary plugin<\/a>, with which you can upload media assets to Cloudinary and handle Cloudinary transformations right in the GraphQL queries.<\/p>\n<p>You may find these links useful:<\/p>\n<ul>\n<li>\n<a href=\"https:\/\/www.npmjs.com\/package\/gatsby-transformer-cloudinary\">Gatsby-Transformer-Cloudinary<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.npmjs.com\/package\/gatsby-source-cloudinary\">Gatsby-source-cloudinary<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.gatsbyjs.com\/docs\">Gatsby.js Docs<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":27784,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[378,134,370,246,371],"class_list":["post-27783","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-gatsbyjs","tag-guest-post","tag-image","tag-react","tag-under-review"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Create a Gallery With Remote Images Using Cloudinary<\/title>\n<meta name=\"description\" content=\"Build an image gallery with remote optimized images sourced from Cloudinary in a Gatsby.js site.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Create a Gallery With Remote Images Using Cloudinary\" \/>\n<meta property=\"og:description\" content=\"Build an image gallery with remote optimized images sourced from Cloudinary in a Gatsby.js site.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-03-21T19:23:35+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-10-26T09:50:39+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Create a Gallery With Remote Images Using Cloudinary\",\"datePublished\":\"2022-03-21T19:23:35+00:00\",\"dateModified\":\"2025-10-26T09:50:39+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\/\"},\"wordCount\":8,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA\",\"keywords\":[\"GatsbyJS\",\"Guest Post\",\"Image\",\"React\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\",\"name\":\"Create a Gallery With Remote Images Using Cloudinary\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA\",\"datePublished\":\"2022-03-21T19:23:35+00:00\",\"dateModified\":\"2025-10-26T09:50:39+00:00\",\"description\":\"Build an image gallery with remote optimized images sourced from Cloudinary in a Gatsby.js site.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA\",\"width\":5886,\"height\":3924},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Create a Gallery With Remote Images Using Cloudinary\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Create a Gallery With Remote Images Using Cloudinary","description":"Build an image gallery with remote optimized images sourced from Cloudinary in a Gatsby.js site.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js","og_locale":"en_US","og_type":"article","og_title":"Create a Gallery With Remote Images Using Cloudinary","og_description":"Build an image gallery with remote optimized images sourced from Cloudinary in a Gatsby.js site.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js","og_site_name":"Cloudinary Blog","article_published_time":"2022-03-21T19:23:35+00:00","article_modified_time":"2025-10-26T09:50:39+00:00","twitter_card":"summary_large_image","twitter_image":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\/"},"author":{"name":"","@id":""},"headline":"Create a Gallery With Remote Images Using Cloudinary","datePublished":"2022-03-21T19:23:35+00:00","dateModified":"2025-10-26T09:50:39+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\/"},"wordCount":8,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA","keywords":["GatsbyJS","Guest Post","Image","React","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js","name":"Create a Gallery With Remote Images Using Cloudinary","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA","datePublished":"2022-03-21T19:23:35+00:00","dateModified":"2025-10-26T09:50:39+00:00","description":"Build an image gallery with remote optimized images sourced from Cloudinary in a Gatsby.js site.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA","width":5886,"height":3924},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/create-an-image-gallery-with-remote-images-in-gatsby-js#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Create a Gallery With Remote Images Using Cloudinary"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926296\/Web_Assets\/blog\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8\/027c4fd4d247020c46d9967fd60f02331cad78b9-5886x3924-1_27784518d8.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27783","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=27783"}],"version-history":[{"count":3,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27783\/revisions"}],"predecessor-version":[{"id":38990,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27783\/revisions\/38990"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/27784"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=27783"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=27783"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=27783"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}