{"id":27848,"date":"2022-06-09T08:01:49","date_gmt":"2022-06-09T08:01:49","guid":{"rendered":"http:\/\/infinite-scroll-image-cards-in-gatsbyjs"},"modified":"2022-06-09T08:01:49","modified_gmt":"2022-06-09T08:01:49","slug":"infinite-scroll-image-cards-in-gatsbyjs","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/","title":{"rendered":"Infinite-Scroll Image Cards in GatsbyJS"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Infinite scrolling is a web design technique where the entire content of a website or app does not load on the page at once but loads incrementally as a user scrolls downward, giving the website a sense of being infinite. This technique creates a seamless user experience and keeps users active.<\/p>\n<p>In this article, you will learn how to implement infinite-scroll in a <a href=\"https:\/\/www.gatsbyjs.com\/\">Gatsby.js<\/a> website using JavaScript\u2019s Intersection Observer API.<\/p>\n<h2>CodeSandbox &amp; GitHub Repo<\/h2>\n<p>The complete demo for this article is on <a href=\"https:\/\/codesandbox.io\/embed\/gatsby-infinite-scroll-o6o4dc?fontsize=14&amp;hidenavigation=1&amp;theme=dark\">CodeSandbox<\/a>. Fork and run it to quickly get started.<\/p>\n<\/div>\n  \n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/gatsby-infinite-scroll-o6o4dc?theme=dark&amp;codemirror=1&amp;highlights=&amp;editorsize=50&amp;fontsize=14&amp;expanddevtools=0&amp;hidedevtools=0&amp;eslint=0&amp;forcerefresh=0&amp;hidenavigation=0&amp;initialpath=%2F&amp;module=&amp;moduleview=0&amp;previewwindow=&amp;view=&amp;runonclick=1\"\n      height=\"500\"\n      style=\"width: 100%;\"\n      title=\"Infinite Scroll Image Cards in Gatsby.js using Intersection Observer\"\n      loading=\"lazy\"\n      allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n      sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n    ><\/iframe>\n  <\/div>\n\n  <div class=\"wp-block-cloudinary-markdown \"><p>To view its source code on GitHub, click <a href=\"https:\/\/github.com\/dpkreativ\/gatsby-infinite-scroll\">this GitHub link<\/a>.<\/p>\n<h2>Prerequisites<\/h2>\n<p>Understanding this article requires the following:<\/p>\n<ul>\n<li>Knowledge of JavaScript and React, especially React Hooks<\/li>\n<li>A Cloudinary account for storing and delivering your images (<a href=\"https:\/\/cloudinary.com\/console\">create a free account here<\/a>)<\/li>\n<li>Installation of <a href=\"https:\/\/nodejs.org\/\">Node.js<\/a> on your local machine<\/li>\n<li>Installation of <a href=\"https:\/\/www.gatsbyjs.com\/docs\/tutorial\/part-0\/#gatsby-cli\">Gatsby CLI<\/a> on your local machine, knowledge of Gatsby.js is preferable but not strictly required<\/li>\n<\/ul>\n<h2>Setting up the Project<\/h2>\n<p>Open your terminal and run the command below in your preferred directory:<\/p>\n<pre><code>gatsby new \n<\/code><\/pre>\n<p>Set the recommended options below in the series of prompts from Gatsby\u2019s CLI, and hit \u201cEnter\u201d on your keyboard to proceed with the project setup:<\/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_D32190432EA27520BF5FB5C6B00389EAD352EE5B66F702B0CB7F2A4371253241_1653408584761_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1094\" height=\"903\"\/><\/p>\n<p>After a successful project setup, view your project\u2019s codebase in your preferred code editor.<\/p>\n<h2>Setting up your Images on Cloudinary<\/h2>\n<p>In your browser, open your <a href=\"https:\/\/cloudinary.com\/console\">Cloudinary dashboard<\/a> and click on the \u201cMedia Library\u201d tab:<\/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_D32190432EA27520BF5FB5C6B00389EAD352EE5B66F702B0CB7F2A4371253241_1653410612833_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1041\" height=\"313\"\/><\/p>\n<p>Upload your chosen images for this project in a new folder:<\/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_D32190432EA27520BF5FB5C6B00389EAD352EE5B66F702B0CB7F2A4371253241_1653410762822_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"767\" height=\"369\"\/><\/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_D32190432EA27520BF5FB5C6B00389EAD352EE5B66F702B0CB7F2A4371253241_1653413351194_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1462\" height=\"896\"\/><\/p>\n<p>You can get some images from <a href=\"https:\/\/picsum.photos\/images\">Lorem Picsum<\/a>.<\/p>\n<h2>Installing Dependencies for your Project<\/h2>\n<p>In your project\u2019s terminal, run the command below to install the dependencies required for this project:<\/p>\n<pre><code>npm i dotenv gatsby-source-cloudinary\n<\/code><\/pre>\n<ul>\n<li>\n<code>dotenv<\/code> gives you access to any data in a <code>.env<\/code> file<\/li>\n<li>\n<code>gatsby-source-cloudinary<\/code> queries media files from your Cloudinary account into <code>CloudinaryMedia<\/code> nodes in your Gatsby project<\/li>\n<\/ul>\n<h2>Configuring Plugins and Dependencies<\/h2>\n<p>In your code editor, open your <code>gatsby-config.js<\/code> file and update it with the code below:<\/p>\n<pre><code>require('dotenv').config({\n  path: `.env.${process.env.NODE_ENV}`,\n});\n\nmodule.exports = {\n  siteMetadata: {\n    siteUrl: `https:\/\/www.yourdomain.tld`,\n  },\n  plugins: [\n    {\n      resolve: `gatsby-source-cloudinary`,\n      options: {\n        cloudName: process.env.CLOUDINARY_CLOUD_NAME,\n        apiKey: process.env.CLOUDINARY_API_KEY,\n        apiSecret: process.env.CLOUDINARY_API_SECRET,\n        resourceType: `image`,\n        prefix: `gatsby-infinite-scroll-images\/`,\n        maxResults: 2000,\n      },\n    },\n  ],\n};\n<\/code><\/pre>\n<p>In the code above, you did the following:<\/p>\n<ul>\n<li>Imported <code>dotenv<\/code> and configured it<\/li>\n<li>Exported <code>siteMetadata<\/code> and your installed <code>plugins<\/code> with their configurations<\/li>\n<li>\n<code>siteMetadata<\/code> contains metadata for your Gatsby site, add your site\u2019s <code>title<\/code>, <code>siteUrl<\/code>, and <code>description<\/code>\n<\/li>\n<li>In <code>gatsby-source-cloudinary<\/code>, the <code>cloudName<\/code>, <code>apiKey<\/code>, and <code>apiSecret<\/code> will contain your Cloudinary credentials gotten from a <code>.env<\/code> file<\/li>\n<li>\n<code>options<\/code>: <code>resourceType<\/code>, <code>prefix<\/code>, and <code>maxResults<\/code> will tell the plugin what kind of media file you want, where to get it, and how many should be returned, you get a maximum of 2000 images from the <code>gatsby-infinite-scroll-images\/<\/code> folder created on Cloudinary<\/li>\n<\/ul>\n<h2>Storing your Cloudinary Credentials in a <code>.env<\/code> file<\/h2>\n<p>In your project\u2019s root folder, create a <code>.env.development<\/code> file and fill it with the data below:<\/p>\n<pre><code>CLOUDINARY_CLOUD_NAME=&lt;Your Cloudinary Cloud Name here&gt;\nCLOUDINARY_API_KEY=&lt;Your Cloudinary API Key here&gt;\nCLOUDINARY_API_SECRET=&lt;Your Cloudinary API Secret here&gt;\n<\/code><\/pre>\n<p>Navigate to your <a href=\"https:\/\/cloudinary.com\/console\">Cloudinary Dashboard<\/a> in your browser to get your Cloud Name, API Key, and API Secret:<\/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_D32190432EA27520BF5FB5C6B00389EAD352EE5B66F702B0CB7F2A4371253241_1653417702300_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1395\" height=\"717\"\/><\/p>\n<p>In your code editor, update the <code>.gitignore<\/code> file with the code below to prevent your <code>.env<\/code> files from storing on Git:<\/p>\n<pre><code>node_modules\/\n.cache\/\npublic\n*env.*\n<\/code><\/pre>\n<h2>Getting Cloudinary Media through Gatsby\u2019s GraphQL Layer<\/h2>\n<p>In your code editor, update <code>src\/pages\/index.js<\/code> with the code below:<\/p>\n<pre><code>import { graphql, useStaticQuery } from 'gatsby';\nimport React from 'react';\n\nexport default function Home() {\n  \/\/ === CLOUDINARY MEDIA ===\n  \/\/ Get images from Cloudinary with Gatsby's useStaticQuery hook\n  const data = useStaticQuery(graphql`\n    query CloudinaryImages {\n      allCloudinaryMedia {\n        edges {\n          node {\n            public_id\n            secure_url\n          }\n        }\n      }\n    }\n  `);\n  const cldImages = data.allCloudinaryMedia.edges;\n  console.log(cldImages);\n\n  return (\n    &lt;div style={{ width: '100%', maxWidth: '425px', margin: '0 auto' }}&gt;\n      &lt;h1&gt;Home&lt;\/h1&gt;\n    &lt;\/div&gt;\n  );\n}\n<\/code><\/pre>\n<p>In the code above, you did the following:<\/p>\n<ul>\n<li>Queried <code>CloudinaryImages<\/code> in Gatsby\u2019s GraphQL layer using <code>useStaticQuery<\/code> to access an array of nodes in <code>allCloudinaryMedia<\/code>\n<\/li>\n<li>Got the <code>public_id<\/code> and <code>secure_url<\/code> of each Cloudinary media file present in its node<\/li>\n<li>Stored the array of nodes in a <code>cldImages<\/code> variable and displayed it in your browser\u2019s console<\/li>\n<\/ul>\n<p>In your terminal, start the development server with the command below:<\/p>\n<pre><code>npm run develop\n<\/code><\/pre>\n<p>Once the server is up and running, open <code>localhost:8000<\/code> on your browser and check the console:<\/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_D32190432EA27520BF5FB5C6B00389EAD352EE5B66F702B0CB7F2A4371253241_1653456569075_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"669\" height=\"431\"\/><\/p>\n<h2>Creating an Image Component<\/h2>\n<p>In your project\u2019s <code>src\/<\/code> folder, create an <code>Image.js<\/code> file inside a <code>components\/<\/code> folder and fill it with the code below:<\/p>\n<pre><code>import React from \u2018react\u2019;\n\nexport default function Image({ image }) {\n  return (\n    &lt;div style={{ width: '100%', maxWidth: '425px', height: '425px' }}&gt;\n      &lt;img\n        src={image.node.secure_url}\n        alt={image.node.public_url}\n        style={{ objectFit: 'cover', width: '100%', height: '100%' }}\n      \/&gt;\n    &lt;\/div&gt;\n  );\n}\n<\/code><\/pre>\n<p>In the code above, you did the following:<\/p>\n<ul>\n<li>Created an <code>Image<\/code> component with an <code>image<\/code> prop<\/li>\n<li>Used the <code>image<\/code> prop to add your <code>secure_url<\/code> to <code>src<\/code> and <code>public_url<\/code> to <code>alt<\/code>\n<\/li>\n<\/ul>\n<p>In your <code>src\/pages\/index.js<\/code> file, import your newly created <code>Image<\/code> component and update the <code>return<\/code> statement with the code below:<\/p>\n<pre><code>return (\n  &lt;div style={{ width: '100%', maxWidth: '425px', margin: '0 auto' }}&gt;\n    &lt;h1&gt;Home&lt;\/h1&gt;\n\n    &lt;section style={{ display: 'grid', gap: '2rem' }}&gt;\n      {cldImages &amp;&amp;\n        cldImages.map((imageNode, index) =&gt; (\n          &lt;Image key={`${index}-cld`} image={imageNode} \/&gt;\n       ))}\n    &lt;\/section&gt;\n  &lt;\/div&gt;\n);\n<\/code><\/pre>\n<p>In the code above, you did the following:<\/p>\n<ul>\n<li>Created a <code>&lt;section&gt;<\/code> element and mapped the contents of your <code>cldImages<\/code> array to create an <code>Image<\/code> component for each element in the array<\/li>\n<li>Added an <code>imageNode<\/code> parameter to the <code>image<\/code> attribute of each <code>Image<\/code> component, which enables you to access <code>{node: {public_id: &lt;value&gt;, secure_url: &lt;value&gt;}}<\/code> from the array<\/li>\n<\/ul>\n<p>When you refresh <code>localhost:8000<\/code> on your browser, you should see something similar to this below:<\/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_D32190432EA27520BF5FB5C6B00389EAD352EE5B66F702B0CB7F2A4371253241_1653459239278_image.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1139\" height=\"899\"\/><\/p>\n<h2>Setting a Limit to the Number of Images Displayed while Scrolling<\/h2>\n<p>In your code editor, update <code>src\/pages\/index.js<\/code> with the code below:<\/p>\n<pre><code>import { graphql, useStaticQuery } from 'gatsby';\nimport React, { useEffect, useState } from 'react';\nimport Image from '..\/components\/Image';\n\nexport default function Home() {\n  \/\/ === CLOUDINARY MEDIA ===\n  \/\/ Get images from Cloudinary with Gatsby's useStaticQuery hook\n  \/\/ (Do not modify the pre-existing code here, just add the new ones below.)\n\n  \/\/ === INFINITE SCROLL LOGIC ===\n  \/\/ Incrementally increase the number of images to display while scrolling\n  const [imagesList, setImagesList] = useState([]);\n  const [limit, setLimit] = useState(4);\n  const start = imagesList.length;\n\n  function newLimit() {\n    const blip = cldImages.length - start;\n    if (blip &gt; 5) {\n      setLimit(limit + 5);\n    } else {\n      setLimit(limit + blip);\n    }\n  }\n\n  useEffect(() =&gt; {\n    const temp = [];\n    for (let i = limit; i &gt;= start; i--) {\n      temp.push(cldImages[i]);\n    }\n    setImagesList((prev) =&gt; [...prev, ...temp]);\n  }, [cldImages, limit, start]);\n\nreturn (\n    &lt;div style={{ width: '100%', maxWidth: '425px', margin: '0 auto' }}&gt;\n      &lt;h1&gt;Home&lt;\/h1&gt;\n      &lt;section style={{ display: 'grid', gap: '2rem' }}&gt;\n        {imagesList &amp;&amp;\n          imagesList.map((imageNode, index) =&gt; (\n            &lt;Image key={`${index}-cld`} image={imageNode} \/&gt;\n          ))}\n      &lt;\/section&gt;\n    &lt;\/div&gt;\n  );\n}\n<\/code><\/pre>\n<p>In the code above, you did the following:<\/p>\n<ul>\n<li>Created an <code>imagesList<\/code> with <code>useState([])<\/code> and a <code>limit<\/code> variable with <code>useState(4)<\/code>\n<\/li>\n<li>Implemented a <code>useEffect()<\/code> that loops through <code>cldImages<\/code> and pushes each element into a <code>temp<\/code> array, then adds the array to your existing <code>imagesList<\/code> array after the loop is complete<\/li>\n<li>Mapped and displayed each element of <code>imagesList<\/code> as an <code>Image<\/code> component<\/li>\n<li>Created a <code>newLimit()<\/code> function that updates the <code>limit<\/code> variable when triggered<\/li>\n<\/ul>\n<h2>Checking When the Last Image in the Array is in the Viewport using Intersection Observer<\/h2>\n<p>Update <code>src\/components\/Image.js<\/code> with the code below:<\/p>\n<pre><code>import React, { useEffect, useRef } from 'react';\n\nexport default function Image({ image, isLast, newLimit }) {\n  const imageRef = useRef();\n\n  useEffect(() =&gt; {\n    if (!imageRef?.current) return;\n    const observer = new IntersectionObserver(([entry]) =&gt; {\n      if (isLast &amp;&amp; entry.isIntersecting) {\n        newLimit();\n        observer.unobserve(entry.target);\n      }\n    });\n    observer.observe(imageRef.current);\n  }, [isLast]);\n\n  return (\n    &lt;div style={{ width: '100%', maxWidth: '425px', height: '425px' }}&gt;\n      &lt;img\n        ref={imageRef}\n        src={image.node.secure_url}\n        alt={image.node.public_url}\n        style={{ objectFit: 'cover', width: '100%', height: '100%' }}\n      \/&gt;\n    &lt;\/div&gt;\n  );\n}\n<\/code><\/pre>\n<p>In the code above, you did the following:<\/p>\n<ul>\n<li>Added two new props &#8211; <code>isLast<\/code> checks if the last image in your <code>imagesList<\/code> array is in the viewport and <code>newLimit<\/code> triggers the <code>newLimit()<\/code> function in <code>src\/pages\/index.js<\/code>\n<\/li>\n<li>Created an <code>imageRef<\/code> variable to access your <code>&lt;img&gt;<\/code> tag with <code>useRef<\/code>\n<\/li>\n<li>Created an <code>observer<\/code> variable that is an instance of <code>IntersectionObserver<\/code>, if <code>isLast<\/code> and <code>entry.isIntersecting<\/code> is true, it triggers the <code>newLimit()<\/code> function<\/li>\n<li>After the <code>newLimit()<\/code> is triggered, <code>observer.unobserve(entry.target)<\/code> ensures that Intersection Observer does not track the target image any further<\/li>\n<li>Initialized the Intersection Observer using <code>observer.observe(imageRef.current)<\/code>\n<\/li>\n<li>If <code>imageRef<\/code> does not exist or its <code>current<\/code> value is <code>null<\/code>, the <code>useEffect<\/code> hook ends without running the <code>observer<\/code>\n<\/li>\n<\/ul>\n<p>In your <code>src\/pages\/index.js<\/code>, update the <code>&lt;section&gt;<\/code> tag in your <code>return<\/code> statement with the code below:<\/p>\n<pre><code>&lt;section style={{ display: 'grid', gap: '2rem' }}&gt;\n  {imagesList &amp;&amp;\n    imagesList.map((imageNode, index) =&gt; (\n      &lt;Image\n        key={`${index}-cld`}\n        image={imageNode}\n        isLast={index === imagesList.length - 1}\n        newLimit={newLimit}\n      \/&gt;\n    ))}\n&lt;\/section&gt;\n<\/code><\/pre>\n<p>In the code above, you did the following:<\/p>\n<ul>\n<li>Set your <code>isLast<\/code> prop to check if the current <code>index<\/code> equals the last element in the <code>imagesList<\/code> array<\/li>\n<li>Set your <code>newLimit()<\/code> function as a value in your <code>newLimit<\/code> prop<\/li>\n<\/ul>\n<p>In your browser, refresh <code>localhost:8000<\/code> and scroll down to see the infinite scroll in action:<\/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_D32190432EA27520BF5FB5C6B00389EAD352EE5B66F702B0CB7F2A4371253241_1653470570400_ezgif.com-gif-maker+2.gif\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"400\" height=\"225\"\/><\/p>\n<h2>Conclusion<\/h2>\n<p>In this article, you learned how to implement infinite scroll in your Gatsby.js website using Intersection Observer. To explore the full capabilities of Gatsby.js in building fast, performant web applications, check out the resources below:<\/p>\n<h2>Resources<\/h2>\n<ul>\n<li>\n<a href=\"http:\/\/\">Gatsby-Source-Cloudinary | Gatsby Plugin Library<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.youtube.com\/watch?v=t2ypzz6gJm0\">Learn useRef in 11 minutes | Web Dev Simplified<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/www.gatsbyjs.com\/docs\/tutorial\/\">Gatsby Tutorial | Gatsby Docs<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":27849,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[332,378,379,134,370,177,371],"class_list":["post-27848","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-api","tag-gatsbyjs","tag-graphql","tag-guest-post","tag-image","tag-javascript","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>Infinite-Scroll Image Cards in GatsbyJS<\/title>\n<meta name=\"description\" content=\"Infinite scrolling is a web design technique where the entire content of a website or app does not load on the page at once but loads incrementally as a user scrolls downward, giving the website a sense of being infinite. This technique creates a seamless user experience and keeps users active. In this article, you will learn how to implement infinite-scroll in a Gatsby.js website using JavaScript\u2019s Intersection Observer API.\" \/>\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\/infinite-scroll-image-cards-in-gatsbyjs\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Infinite-Scroll Image Cards in GatsbyJS\" \/>\n<meta property=\"og:description\" content=\"Infinite scrolling is a web design technique where the entire content of a website or app does not load on the page at once but loads incrementally as a user scrolls downward, giving the website a sense of being infinite. This technique creates a seamless user experience and keeps users active. In this article, you will learn how to implement infinite-scroll in a Gatsby.js website using JavaScript\u2019s Intersection Observer API.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-06-09T08:01:49+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"730\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\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\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Infinite-Scroll Image Cards in GatsbyJS\",\"datePublished\":\"2022-06-09T08:01:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/\"},\"wordCount\":5,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA\",\"keywords\":[\"API\",\"GatsbyJS\",\"GraphQL\",\"Guest Post\",\"Image\",\"Javascript\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/\",\"name\":\"Infinite-Scroll Image Cards in GatsbyJS\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA\",\"datePublished\":\"2022-06-09T08:01:49+00:00\",\"description\":\"Infinite scrolling is a web design technique where the entire content of a website or app does not load on the page at once but loads incrementally as a user scrolls downward, giving the website a sense of being infinite. This technique creates a seamless user experience and keeps users active. In this article, you will learn how to implement infinite-scroll in a Gatsby.js website using JavaScript\u2019s Intersection Observer API.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA\",\"width\":1200,\"height\":730},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Infinite-Scroll Image Cards in GatsbyJS\"}]},{\"@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":"Infinite-Scroll Image Cards in GatsbyJS","description":"Infinite scrolling is a web design technique where the entire content of a website or app does not load on the page at once but loads incrementally as a user scrolls downward, giving the website a sense of being infinite. This technique creates a seamless user experience and keeps users active. In this article, you will learn how to implement infinite-scroll in a Gatsby.js website using JavaScript\u2019s Intersection Observer API.","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\/infinite-scroll-image-cards-in-gatsbyjs\/","og_locale":"en_US","og_type":"article","og_title":"Infinite-Scroll Image Cards in GatsbyJS","og_description":"Infinite scrolling is a web design technique where the entire content of a website or app does not load on the page at once but loads incrementally as a user scrolls downward, giving the website a sense of being infinite. This technique creates a seamless user experience and keeps users active. In this article, you will learn how to implement infinite-scroll in a Gatsby.js website using JavaScript\u2019s Intersection Observer API.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-06-09T08:01:49+00:00","og_image":[{"width":1200,"height":730,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/"},"author":{"name":"","@id":""},"headline":"Infinite-Scroll Image Cards in GatsbyJS","datePublished":"2022-06-09T08:01:49+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/"},"wordCount":5,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA","keywords":["API","GatsbyJS","GraphQL","Guest Post","Image","Javascript","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/","name":"Infinite-Scroll Image Cards in GatsbyJS","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA","datePublished":"2022-06-09T08:01:49+00:00","description":"Infinite scrolling is a web design technique where the entire content of a website or app does not load on the page at once but loads incrementally as a user scrolls downward, giving the website a sense of being infinite. This technique creates a seamless user experience and keeps users active. In this article, you will learn how to implement infinite-scroll in a Gatsby.js website using JavaScript\u2019s Intersection Observer API.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA","width":1200,"height":730},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/infinite-scroll-image-cards-in-gatsbyjs\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Infinite-Scroll Image Cards in GatsbyJS"}]},{"@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\/v1681926114\/Web_Assets\/blog\/infinite-scroll-cards\/infinite-scroll-cards.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27848","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=27848"}],"version-history":[{"count":0,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27848\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/27849"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=27848"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=27848"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=27848"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}