{"id":33587,"date":"2024-04-18T07:00:00","date_gmt":"2024-04-18T14:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=33587"},"modified":"2025-03-08T14:15:44","modified_gmt":"2025-03-08T22:15:44","slug":"building-image-gallery-using-contentful-cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary","title":{"rendered":"Building an Image Gallery Using Contentful and Cloudinary"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Managing media and content across various platforms and tools can be a complex task. Creators often find themselves juggling multiple interfaces, formats, and workflows. This fragmentation can lead to inefficiencies, inconsistencies, and wasted time.<\/p>\n<p>In this blog post, we\u2019ll explore how to create an image gallery that leverages the integration of two tools: Contentful for our content management and Cloudinary to host our images. Both tools have their own set of features, but integrating them can provide a unified and efficient workflow for creators.<\/p>\n<h2>Prerequisites<\/h2>\n<ul>\n<li>A Contentful account<\/li>\n<li>A Cloudinary account<\/li>\n<\/ul>\n<h2>Setting Up Cloudinary<\/h2>\n<p>To integrate Cloudinary into our Contentful workflow, we\u2019ll start by signing in to our Cloudinary account. Then we\u2019ll go to <strong>Settings<\/strong>, <strong>Access Keys<\/strong>, and click <strong>Generate New Access Key<\/strong>. After the key is generated, we can rename it so we can recognize it later.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-1.png\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1956\" height=\"682\"\/><\/p>\n<p>We we\u2019ll need the cloud name while configuring the integration. If you\u2019re unsure what your cloud name is, you can find it under <strong>Accounts<\/strong> in <strong>Settings<\/strong>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-2.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1394\" height=\"468\"\/><\/p>\n<h2>Integrating Cloudinary With Contentful<\/h2>\n<p>After generating your Cloudinary API key, go to your Contentful dashboard while keeping the Cloudinary app open as we\u2019ll need the keys. Look for <strong>Marketplace<\/strong> under the <strong>Apps<\/strong> menu. Once there, search for Cloudinary and select it.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-3.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1474\" height=\"638\"\/><\/p>\n<p>When configuring the app, we\u2019ll need the Cloud Name, the API key, and the API secret from Cloudinary.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-4.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1820\" height=\"1062\"\/><\/p>\n<p>Other settings include the maximum number of images per field, quality, and format, but we\u2019ll keep the defaults.<\/p>\n<h2>Using Cloudinary With Our Content Models<\/h2>\n<p>Now that our app is set up, let\u2019s create a new Content model and add a Cloudinary media field.<\/p>\n<p>For this demo, I\u2019ll display a list of animals in an image gallery. Our model will be called Animal, and it will contain different properties like the name, credits for the author of the image, and an image hosted in Cloudinary.<\/p>\n<p><strong>Important note:<\/strong> While creating our model\u2019s fields, they must be the type JSON Object, not Media.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-5.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1766\" height=\"1294\"\/><\/p>\n<p>Once you start creating the field, under <strong>Appearance<\/strong>, you can pick <strong>Cloudinary App<\/strong>. Select it and then <strong>Confirm<\/strong>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-6.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1666\" height=\"1024\"\/><\/p>\n<p>One important detail to remember is that data isn\u2019t synced with Cloudinary if updated. If you need them synced, a feature called external references must be enabled. We can do it by checking the <strong>Resolve content on delivery<\/strong> checkbox.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-7.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1628\" height=\"610\"\/><\/p>\n<h2>Adding Data<\/h2>\n<p>Now that our model is complete, we need to add some data. I picked some images from Unsplash to add to our list of animals. Let\u2019s create a new entry.<\/p>\n<p>We\u2019ll add the required information, then click the button to add a new image to Cloudinary.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-8.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1684\" height=\"864\"\/><\/p>\n<p>A new modal will pop up. Upload your images as you\u2019d normally do on Cloudinary. Click the refresh button if you don\u2019t see them after uploading. Select the images you want to add to your entry and click <strong>Insert<\/strong>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-9.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1999\" height=\"941\"\/><\/p>\n<p>You should now see your entry with the attached images. Publish it to make it available.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-10.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1748\" height=\"1128\"\/><\/p>\n<h2>Setting Up Contentful<\/h2>\n<p>Now that the Cloudinary app is connected, our model is created, and we have some data,  we need to create an API key to make requests from our application.<\/p>\n<p>To do that, go to <strong>Settings<\/strong>, select <strong>API keys<\/strong>, and click <strong>Add API Keys<\/strong>. Give it a name and description. You should now have a new API key.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-11.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1999\" height=\"478\"\/><\/p>\n<p>Finally, we\u2019ll need the Space ID that you can find in your <strong>General Settings<\/strong>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-12.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1999\" height=\"588\"\/><\/p>\n<h2>Building the Image Gallery<\/h2>\n<p>For this demo, I\u2019ll use SvelteKit, but similar rules apply to other frameworks.<\/p>\n<p>Let\u2019s start by creating a new project:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">pnpm<\/span> <span class=\"hljs-selector-tag\">create<\/span> <span class=\"hljs-selector-tag\">svelte<\/span><span class=\"hljs-keyword\">@latest<\/span> svelte-image-gallery\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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>When prompted, choose the skeleton project. I\u2019ll use Typescript.<\/p>\n<p>Navigate to the newly generated project and install the required dependencies.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">cd svelte-image-gallery\npnpm i\n<\/code><\/span><\/pre>\n<p>Let\u2019s set our environment variables to be able to fetch data from Contentful.\nCreate a new <code>.env<\/code>  in the root of your project and add your Space ID and your Access Token from Contentful.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>CONTENTFUL_SPACE_ID = &lt;YOUR_SPACE_ID&gt;\nCONTENTFUL_ACCESS_TOKEN = &lt;YOUR_ACCESS_TOKEN&gt;\n<\/code><\/pre>\n<p>With this information, we should be ready to make requests to the contentful GraphQL API.<\/p>\n<p>Let\u2019s create a helper function to make these requests. Create a new file in <code>\/stc\/lib\/contentful.server.ts<\/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-keyword\">import<\/span> { CONTENTFUL_SPACE_ID, CONTENTFUL_ACCESS_TOKEN } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'$env\/static\/private'<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getFromContentful<\/span>(<span class=\"hljs-params\">query: string<\/span>) <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> fetch(<span class=\"hljs-string\">`https:\/\/graphql.contentful.com\/content\/v1\/spaces\/<span class=\"hljs-subst\">${CONTENTFUL_SPACE_ID}<\/span>`<\/span>, {\n        <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">'POST'<\/span>,\n        <span class=\"hljs-attr\">headers<\/span>: {\n            <span class=\"hljs-string\">'Content-Type'<\/span>: <span class=\"hljs-string\">'application\/json'<\/span>,\n            <span class=\"hljs-attr\">Authorization<\/span>: <span class=\"hljs-string\">`Bearer <span class=\"hljs-subst\">${CONTENTFUL_ACCESS_TOKEN}<\/span>`<\/span>,\n        },\n        <span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify({ query }),\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>We\u2019re using the environment variables previously set up and wrapping fetch to make a request to the desired endpoint.<\/p>\n<p>We\u2019ll have a single page in our application to keep things simple.<\/p>\n<p>We need to fetch data from Contentful. Let\u2019s create a new file that will load data for our page (<code>\/src\/routes\/+page.server.ts<\/code>)<\/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> { error } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@sveltejs\/kit'<\/span>\n<span class=\"hljs-keyword\">import<\/span> { getFromContentful } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'$lib\/contentful.server'<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> query = <span class=\"hljs-string\">`\n{\n\tanimalCollection {\nitems {\nname\nImages\ncredit\n}\n}\n}\n`<\/span>\n\ntype Animal = {\n  <span class=\"hljs-attr\">name<\/span>: string,\n  <span class=\"hljs-attr\">credit<\/span>: string,\n  <span class=\"hljs-attr\">images<\/span>: { <span class=\"hljs-attr\">public_id<\/span>: string }&#91;]\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">load<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> getFromContentful(query)\n\n  <span class=\"hljs-keyword\">if<\/span> (!response.ok) {\n    error(<span class=\"hljs-number\">404<\/span>, {\n      <span class=\"hljs-attr\">message<\/span>: response.statusText,\n    });\n  }\n\n  <span class=\"hljs-keyword\">const<\/span> { data } = <span class=\"hljs-keyword\">await<\/span> response.json()\n  <span class=\"hljs-keyword\">const<\/span> { items } = data.animalCollection\n\n  <span class=\"hljs-keyword\">return<\/span> {\n    <span class=\"hljs-attr\">animals<\/span>: items <span class=\"hljs-keyword\">as<\/span> Animal&#91;]\n  }\n}\n\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>In this file, we\u2019ll set up our query and use the helper function to make the request. I created a type with the expected response as well. We\u2019ll also handle errors in case it fails to fetch the data.<\/p>\n<p>Now, we need to display the data on our page. We\u2019ll show the returned JSON from the request.<\/p>\n<p>Let\u2019s modify our page (<code>src\/routes\/+page.svelte<\/code>)<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"ts\"<\/span>&gt;<\/span><span class=\"javascript\">\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> data;\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"grid\"<\/span>&gt;<\/span>\n\t{JSON.stringify(data, null, 2)}\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-13.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1362\" height=\"634\"\/><\/p>\n<p>We successfully fetched data from Contentful. Now we just need to display the images. The best way to take advantage of Cloudinary features in Svelte is to use the <code>svelte-cloudinary<\/code> package, a community-built library that will automatically optimize images and videos, among other things.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">pnpm i svelte-cloudinary\n<\/code><\/span><\/pre>\n<p>Once the component is installed, we can update our page to iterate over the different animals and images and display them on a grid.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"ts\"<\/span>&gt;<\/span><span class=\"javascript\">\n\t<span class=\"hljs-keyword\">import<\/span> { CldImage } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'svelte-cloudinary'<\/span>;\n\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> data;\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"grid\"<\/span>&gt;<\/span>\n\t{#each data.animals as animal}\n\t\t{#each animal.images as image}\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"img-container\"<\/span>&gt;<\/span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CldImage<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{image.public_id}<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">{600}<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">{400}<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"\"<\/span> \/&gt;<\/span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"credits\"<\/span>&gt;<\/span>\n\t\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>{animal.credit}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\t\t\t<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\t\t{\/each}\n\t{\/each}\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">style<\/span>&gt;<\/span><span class=\"css\">\n\t<span class=\"hljs-selector-class\">.grid<\/span> {\n\t\t<span class=\"hljs-attribute\">display<\/span>: grid;\n\t\t<span class=\"hljs-attribute\">gap<\/span>: <span class=\"hljs-number\">16px<\/span>;\n\t\t<span class=\"hljs-attribute\">align-items<\/span>: start;\n\t\t<span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">100px<\/span>;\n\t}\n\n\t<span class=\"hljs-keyword\">@media<\/span> (<span class=\"hljs-attribute\">min-width:<\/span> <span class=\"hljs-number\">768px<\/span>) {\n\t\t<span class=\"hljs-selector-class\">.grid<\/span> {\n\t\t\t<span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(<span class=\"hljs-number\">2<\/span>, minmax(<span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">1<\/span>fr));\n\t\t}\n\t}\n\n\t<span class=\"hljs-keyword\">@media<\/span> (<span class=\"hljs-attribute\">min-width:<\/span> <span class=\"hljs-number\">1024px<\/span>) {\n\t\t<span class=\"hljs-selector-class\">.grid<\/span> {\n\t\t\t<span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(<span class=\"hljs-number\">3<\/span>, minmax(<span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">1<\/span>fr));\n\t\t}\n\t}\n\n\t<span class=\"hljs-selector-pseudo\">:global(.img-container<\/span> &gt; <span class=\"hljs-selector-tag\">img<\/span>) {\n\t\t<span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">0.5rem<\/span>;\n\t}\n\n\t<span class=\"hljs-selector-class\">.credits<\/span> {\n\t\t<span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">8px<\/span>;\n\t}\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">style<\/span>&gt;<\/span>\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\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here we\u2019ll use the <code>CldImage<\/code>  component that requires the public_id from the image. Internally, it will take care of providing the best possible image, depending on the screen size and our settings.<\/p>\n<p>Our page is finally displaying images.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-14.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1348\" height=\"1108\"\/><\/p>\n<p>Our gallery is only complete with more data. Here\u2019s the final result after adding more entries.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1712607551\/Blog-Building_an_Image_Gallery_Using_Contentful_and_Cloudinary-15.jpg\" alt=\"alt text\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"640\" height=\"393\"\/><\/p>\n<h2>Conclusion<\/h2>\n<p>In this blog post, we set up a successful integration of Contentful and Cloudinary, leveraging the best of both. We managed content creation through Contentful, and the media storage and provision through Cloudinary. We developed a demo application using Sveltekit, that fetches data from Contentful and uses a component from the svelte-cloudinary package to display images in a performant way.<\/p>\n<p>If you found this blog post helpful and want to discuss it in more detail, head over to the <a href=\"https:\/\/community.cloudinary.com\/\">Cloudinary Community forum<\/a> and its associated <a href=\"https:\/\/community.cloudinary.com\/\">Discord<\/a>.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":87,"featured_media":33691,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[370],"class_list":["post-33587","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-image"],"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>Integrating Contentful and Cloudinary to Build an Image Gallery<\/title>\n<meta name=\"description\" content=\"Integrate Contentful and Cloudinary to create a dynamic image gallery. From setting up Cloudinary to using SvelteKit to display optimized images.\" \/>\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\/building-image-gallery-using-contentful-cloudinary\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building an Image Gallery Using Contentful and Cloudinary\" \/>\n<meta property=\"og:description\" content=\"Integrate Contentful and Cloudinary to create a dynamic image gallery. From setting up Cloudinary to using SvelteKit to display optimized images.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2024-04-18T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-08T22:15:44+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Building an Image Gallery Using Contentful and Cloudinary\",\"datePublished\":\"2024-04-18T14:00:00+00:00\",\"dateModified\":\"2025-03-08T22:15:44+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary\"},\"wordCount\":8,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog.jpg?_i=AA\",\"keywords\":[\"Image\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2024\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary\",\"url\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary\",\"name\":\"Integrating Contentful and Cloudinary to Build an Image Gallery\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog.jpg?_i=AA\",\"datePublished\":\"2024-04-18T14:00:00+00:00\",\"dateModified\":\"2025-03-08T22:15:44+00:00\",\"description\":\"Integrate Contentful and Cloudinary to create a dynamic image gallery. From setting up Cloudinary to using SvelteKit to display optimized images.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Building an Image Gallery Using Contentful and 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\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Integrating Contentful and Cloudinary to Build an Image Gallery","description":"Integrate Contentful and Cloudinary to create a dynamic image gallery. From setting up Cloudinary to using SvelteKit to display optimized images.","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\/building-image-gallery-using-contentful-cloudinary","og_locale":"en_US","og_type":"article","og_title":"Building an Image Gallery Using Contentful and Cloudinary","og_description":"Integrate Contentful and Cloudinary to create a dynamic image gallery. From setting up Cloudinary to using SvelteKit to display optimized images.","og_url":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary","og_site_name":"Cloudinary Blog","article_published_time":"2024-04-18T14:00:00+00:00","article_modified_time":"2025-03-08T22:15:44+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog-jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Building an Image Gallery Using Contentful and Cloudinary","datePublished":"2024-04-18T14:00:00+00:00","dateModified":"2025-03-08T22:15:44+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary"},"wordCount":8,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog.jpg?_i=AA","keywords":["Image"],"inLanguage":"en-US","copyrightYear":"2024","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary","url":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary","name":"Integrating Contentful and Cloudinary to Build an Image Gallery","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog.jpg?_i=AA","datePublished":"2024-04-18T14:00:00+00:00","dateModified":"2025-03-08T22:15:44+00:00","description":"Integrate Contentful and Cloudinary to create a dynamic image gallery. From setting up Cloudinary to using SvelteKit to display optimized images.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/building-image-gallery-using-contentful-cloudinary#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Building an Image Gallery Using Contentful and 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":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1714500054\/Web_Assets\/blog\/image_gallery_contentful_Cloudinary-blog\/image_gallery_contentful_Cloudinary-blog.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/33587","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\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=33587"}],"version-history":[{"count":5,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/33587\/revisions"}],"predecessor-version":[{"id":37152,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/33587\/revisions\/37152"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/33691"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=33587"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=33587"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=33587"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}