<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Gatsby - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Gatsby - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 17 May 2026 11:36:38 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/gatsby/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Learn Gatsby, a Static Site Generator ]]>
                </title>
                <description>
                    <![CDATA[ Gatsby is a website generator. It is a free and open source framework based on React that helps developers build fast websites and apps. We just published a 9-hour Gatsby course on the freeCodeCamp.org YouTube channel that will teach you how to use t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-gatsby-version-3/</link>
                <guid isPermaLink="false">66b20451a2135cc2539a21bf</guid>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 16 Sep 2021 15:34:47 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/09/maxresdefault.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Gatsby is a website generator. It is a free and open source framework based on React that helps developers build fast websites and apps.</p>
<p>We just published a 9-hour Gatsby course on the freeCodeCamp.org YouTube channel that will teach you how to use the newest version of the framework.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/giphy.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Here's to learning Gatsby.</em></p>
<p>During this course, you will learn how to create and deploy an interactive recipe website.</p>
<p>The course contains the following sections:</p>
<ul>
<li>Gatsby Info </li>
<li>Course Structure </li>
<li>Course Requirements </li>
<li>Vs Code </li>
<li>Module Intro </li>
<li>Install Gatsby-Cli </li>
<li>Setup New Gatsby Project </li>
<li>Folder Structure </li>
<li>First Page </li>
<li>Error Page </li>
<li>Nested Structure </li>
<li>Links </li>
<li>Navbar </li>
<li>Layout Component </li>
<li>CSS Module Intro</li>
<li>Inline CSS</li>
<li>Global CSS</li>
<li>CSS Naming Issues</li>
<li>CSS Modules</li>
<li>Styled-Components</li>
<li>House Cleaning</li>
<li>Styles</li>
<li>Footer</li>
<li>Error Page</li>
<li>Contact Page</li>
<li>Assets And Icons</li>
<li>Navbar Setup</li>
<li>Navbar Logic</li>
<li>Gatsby Image Info</li>
<li>Sandbox Setup</li>
<li>Install Plugin</li>
<li>Static Image Setup</li>
<li>Shared Props And Options</li>
<li>Options Example</li>
<li>All Layouts</li>
<li>Height</li>
<li>About Page</li>
<li>Hero Page</li>
<li>Gatsby And GraphQL Intro</li>
<li>Gatsby DataLayer In A Nutshell</li>
<li>GraphiQL Interface</li>
<li>SiteMetadata</li>
<li>First Query</li>
<li>Explorer</li>
<li>Static Query Vs Page Query</li>
<li>UseStaticQuery Hook - Code Exporter</li>
<li>UseStaticQuery, GraphQL - From Scratch</li>
<li>Field Alias</li>
<li>Query Keyword, Name And Gatsby Clean</li>
<li>Page Query</li>
<li>Install SOURCE-FILESYSTEM Plugin</li>
<li>AllFile Field</li>
<li>Query Arguments</li>
<li>Static Path Fix</li>
<li>File - Field</li>
<li>SourceInstanceName - Argument</li>
<li>Gallery Setup</li>
<li>GatsbyImageData - Field</li>
<li>Render Gallery</li>
<li>GetImage - Helper Function</li>
<li>Local VS External Data</li>
<li>Headless CMS</li>
<li>Contentful</li>
<li>Setup Contentful Account</li>
<li>Content-Type</li>
<li>Content</li>
<li>Connect Gatsby - Contentful</li>
<li>ENV Variables</li>
<li>AllContentfulRecipe - Field</li>
<li>AllRecipes Component</li>
<li>RecipesList Component</li>
<li>Featured Recipes</li>
<li>Utils Setup</li>
<li>Helper Function</li>
<li>TagsList</li>
<li>Tags Page</li>
<li>Recipe Template Page Setup</li>
<li>Recipe Template Page Walkthrough</li>
<li>Slugify</li>
<li>Query Variables</li>
<li>Recipe Template Query</li>
<li>Recipe Template Return</li>
<li>GATSBY-NODE.JS Setup</li>
<li>Create Tag Pages Programmatically</li>
<li>Tag Template Return</li>
<li>Possible Bug Fix</li>
<li>Fonts</li>
<li>Contact Form</li>
<li>FAVICON</li>
<li>SEO Setup</li>
<li>SEO - Props</li>
<li>SEO - Complete</li>
<li>Netlify Info</li>
<li>Netlify - Drag And Drop</li>
<li>Continuous Deployment</li>
<li>Webhooks</li>
</ul>
<p>Watch the full course below or <a target="_blank" href="https://youtu.be/RaTpreA0v7Q">on the freeCodeCamp.org YouTube channel</a> (9-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/RaTpreA0v7Q" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Webmentions with Gatsby.js – A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ Webmention is a simple protocol developed by the IndieWeb Community that you can use to request notifications when your URLs are mentioned on the web. When you post on your own site and syndicate elsewhere (POSSE), Webmention lets you enable rich int... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-webmentions-with-gatsby-beginners-guide/</link>
                <guid isPermaLink="false">66d4615c4a0edd9b48e83599</guid>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Spruce Emmanuel ]]>
                </dc:creator>
                <pubDate>Thu, 22 Jul 2021 16:33:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/07/Web-capture_19-7-2021_222358_iamspruce.dev-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://indieweb.org/webmention">Webmention</a> is a simple protocol developed by the IndieWeb Community that you can use to request notifications when your URLs are mentioned on the web.</p>
<p>When you post on your own site and syndicate elsewhere (<a target="_blank" href="https://indieweb.org/POSSE">POSSE</a>), Webmention lets you enable rich interactions on your syndicated posts. This article will walk you through the simplest way to turn your social media interactions into Webmentions and display them on your Gatsby.js site.</p>
<p>This tutorial will implement Webmentions on an already existing site, so you should have at least some basic knowledge of React and Gatsby.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>To help you better understand how Webmentions work, check out the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/new-webmentions-cycle-1.jpg" alt="Stages of sending and receiving webmentions" width="600" height="400" loading="lazy"></p>
<p><em>Different stages of Webmention process</em></p>
<p>Let's break down this these three stages and learn how to implement them.</p>
<h2 id="heading-how-to-send-webmentions">How to Send Webmentions</h2>
<p>A Webmention Sender is an implementation that send's Webmentions. <a target="_blank" href="https://brid.gy/">Bridgy</a> is a good example of a sender.</p>
<p>Bridgy is an open source tool that pulls your social media interactions from popular social sites and blog engines like Twitter, Facebook, Instagram, and Medium and magically turns them into Webmentions.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/bridy-line-1.jpg" alt="Bridgy turning Social media Interactions to Webmentions" width="600" height="400" loading="lazy"></p>
<p><em>Bridgy turning social interactions to Webmentions</em></p>
<p>To use Bridgy, your domain needs to support the <a target="_blank" href="https://indieauth.com/">indieAuth Login process</a>.</p>
<p>In your <code>layout.js</code> component (or from whatever component you are managing your <code>&lt;head&gt;</code> with React Helmet), add the following code to link to your social profiles:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Helmet } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-helmet"</span>
<span class="hljs-comment">// other imports</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Layout</span>(<span class="hljs-params">{ children}</span>) </span>{

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"wrapper"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Helmet</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"me"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://twitter.com/sprucekhalifa"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"me"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://github.com/iamspruce"</span> /&gt;</span>

      <span class="hljs-tag">&lt;/<span class="hljs-name">Helmet</span>&gt;</span>
...
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>On each of the social services you just linked to, make sure your profile has a link leading back to your homepage like you can see below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/twitter-link-back.jpg" alt="Back links to your website" width="600" height="400" loading="lazy"></p>
<p><em>Adding a link back to you homepage on Twitter</em></p>
<p>That's it! You are done with the IndieAuth login Process. Now head over to <a target="_blank" href="https://brid.gy/">Bridgy</a> and sign in with Twitter (if you used Twitter for your IndieAuth process).</p>
<p>From now on Bridgy will periodically analyze your tweets (<a target="_blank" href="https://brid.gy/about#privacy">I promise it doesn't do anything with your data</a>). For each tweet that has a link to your site, it will get all the replies, likes, retweets, and so on and send them as Webmentions.</p>
<h2 id="heading-how-to-receive-webmentions">How to Receive Webmentions</h2>
<p>A Webmention receiver is an implementation that receives Webmentions to one or more target URLs.</p>
<p>This implementation can be a script executed on your server, but in the case of GatsbyJs which has no server we'll rely on a third party tool called <a target="_blank" href="https://webmention.io/">webmention.io</a> created by <a target="_blank" href="https://aaronparecki.com/">Aaron Parecki</a>.</p>
<p>This Tool receives your Webmentions and stores and organizes them. It also provides an API that you can use to easily grab your Webmentions and display them on your site.</p>
<p>To use webmention.io make sure you followed the IndieAuth Login process in the "How to Send Webmentions" section. Then go to <a target="_blank" href="https://webmention.io/">webmention.io</a> and sign in with your domain name.</p>
<p>Once you have signed up, add the following to the <code>&lt;head&gt;</code> of your site (and just replace <code>username</code> with the username you got when you logged in):</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Helmet } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-helmet"</span>
<span class="hljs-comment">// other imports</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Layout</span>(<span class="hljs-params">{ children}</span>) </span>{

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"wrapper"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Helmet</span>&gt;</span>
        ...
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"webmention"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://webmention.io/username/webmention"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"pingback"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://webmention.io/username/xmlrpc"</span> /&gt;</span>

      <span class="hljs-tag">&lt;/<span class="hljs-name">Helmet</span>&gt;</span>
...
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>When you start getting Webmentions from your target URLs, you should see them in your dashboard like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Web-capture_21-7-2021_51043_webmention.io.jpeg" alt="webmention.io dashboard" width="600" height="400" loading="lazy"></p>
<p><em>webmention.io dashboard</em></p>
<p>You can easily monitor your Webmentions in your dashboard.</p>
<h2 id="heading-how-to-pull-webmentions-data-to-your-website">How to Pull Webmentions Data to Your Website</h2>
<p>This is the fun part where you'll pull your Webmentions data from the Webmention.io API. To do this, I created a Gatsby plugin called <code>[gatsby-source-webmentions](https://www.npmjs.com/package/gatsby-source-webmentions)</code></p>
<p>NOTE: there's another plugin called <code>[gatsby-plugin-webmentions](https://github.com/ChristopherBiscardi/gatsby-plugin-webmention)</code> which pulls in Webmentions data from webmention.io API and makes them available in Graphql.</p>
<h3 id="heading-spruce-if-theres-already-a-gatsby-plugin-why-create-another-one">Spruce, if there's already a Gatsby plugin why create another one?</h3>
<p>I created this plugin for two reasons:</p>
<ol>
<li><p>First because I can, and why not.</p>
</li>
<li><p>Second, for image optimization – this plugin lets you use the gatsby-image plugin to optimize the images returned by the API.</p>
</li>
</ol>
<h3 id="heading-how-to-install-the-plugin">How to Install the Plugin</h3>
<p>To install the plugin, open your system terminal or the VS Code integrated terminal and run the following:</p>
<pre><code class="lang-js">npm install gatsby-source-webmentions
</code></pre>
<p>Next you'll need to add the plugin to your <code>gatsby-config.js</code> plugins array:</p>
<pre><code class="lang-js">   { 
      <span class="hljs-attr">resolve</span>: <span class="hljs-string">"gatsby-source-webmentions"</span>,
      <span class="hljs-attr">options</span>: {
        <span class="hljs-attr">DOMAIN</span>: <span class="hljs-string">"example.com"</span>, <span class="hljs-comment">// without https and any slashes</span>
        <span class="hljs-attr">TOKEN</span>: process.env.WEBMENTIONS_TOKEN, <span class="hljs-comment">// token from webmention.io</span>
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">100</span>, <span class="hljs-comment">// optional</span>
      },
</code></pre>
<p>The plugin takes a few options:</p>
<ol>
<li><p>DOMAIN: the domain name you used to sign in to <a target="_blank" href="http://webmention.io">webmention.io</a></p>
</li>
<li><p>TOKEN: the token you got from your <a target="_blank" href="http://webmention.io">webmention.io</a> dashboard</p>
</li>
<li><p>perPage: the number of Webmentions you want to fetch per page (this is completely optional)</p>
</li>
</ol>
<p>To avoid pushing your secret token to GitHub add it as an <a target="_blank" href="https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/">environmental variable</a>.</p>
<h3 id="heading-how-to-display-webmentions-on-the-client-side">How to Display Webmentions on the Client Side</h3>
<p>If you're dynamically creating your pages with the <a target="_blank" href="https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/#createPages">createPage Node API</a> there's a high chance that you passed the <code>slug</code> variable to all your site pages. If you are unsure or you named it something else just check your site <code>gatsby-node.js</code> file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/graphql-variable.jpg" alt="GatsbyJs generating slug" width="600" height="400" loading="lazy"></p>
<p><em>Using Create pages to generate a slug for site pages</em></p>
<p>In your <code>src/templates/blog.js</code> or wherever your page templates are, you'll query Webmentions only if the <code>wm_slug</code> matches the page <code>slug</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { graphql } <span class="hljs-keyword">from</span> <span class="hljs-string">"gatsby"</span>
<span class="hljs-keyword">import</span> Layout <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Layout"</span>
<span class="hljs-keyword">import</span> Comment <span class="hljs-keyword">from</span> <span class="hljs-string">"./Comment"</span>


<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
        totalCount
        edges {
          node {
            id
            published
            publishedFormated: published(formatString: "MMM Do, YYYY")
            author {
                name
                photo
                url
            }
            url
            wm_id
            content {
              html
            }
          }
        }
      }
    }
`</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">BlogPost</span>(<span class="hljs-params">{ data, location }</span>) </span>{
...
  const mentions = data.allWebmention

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      ...
      // display mentions in a react component
      <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<p><code>wm_slug</code> is a node I created to grab the slug from the <code>wm_target</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/wm_slug.jpg" alt="Image showing wm_target and wm_slug" width="600" height="400" loading="lazy"></p>
<p>Now you can map over all the Webmentions for that target <code>slug</code> and display them in a React Component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { graphql } <span class="hljs-keyword">from</span> <span class="hljs-string">"gatsby"</span>
<span class="hljs-keyword">import</span> Layout <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Layout"</span>
<span class="hljs-keyword">import</span> Comment <span class="hljs-keyword">from</span> <span class="hljs-string">"./Comment"</span>


<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
    // graphql queries
      }
    }
`</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">BlogPost</span>(<span class="hljs-params">{ data }</span>) </span>{
...
  const mentions = data.allWebmention

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      ...
          <span class="hljs-tag">&lt;<span class="hljs-name">ol</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"webmentions__list"</span>&gt;</span>
      {mentions.edges.map(edge =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">Comment</span>
          <span class="hljs-attr">key</span>=<span class="hljs-string">{edge.node.wm_id}</span>
          <span class="hljs-attr">imageUrl</span>=<span class="hljs-string">{edge.node.author.photo}</span>
          <span class="hljs-attr">authorUrl</span>=<span class="hljs-string">{edge.node.author.url}</span>
          <span class="hljs-attr">authorName</span>=<span class="hljs-string">{edge.node.author.name}</span>
          <span class="hljs-attr">dtPublished</span>=<span class="hljs-string">{edge.node.published}</span>
          <span class="hljs-attr">dtPublishedFormated</span>=<span class="hljs-string">{edge.node.publishedFormated}</span>
          <span class="hljs-attr">content</span>=<span class="hljs-string">{edge.node.content</span> &amp;&amp; <span class="hljs-attr">edge.node.content.html</span>}
          <span class="hljs-attr">url</span>=<span class="hljs-string">{edge.node.url}</span>
        /&gt;</span>
      ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ol</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<h3 id="heading-how-to-group-the-webmentions-by-type">How to Group the Webmentions by Type</h3>
<p>Although this is completely optional, it is a good idea to group your Webmentions by type:</p>
<ol>
<li><p><code>"in_reply_to"</code> – for replies</p>
</li>
<li><p><code>"like_of"</code> – for likes</p>
</li>
<li><p><code>"retweet_of"</code> – for retweets, etc.</p>
</li>
</ol>
<pre><code class="lang-js">...

export <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
      likes: group(field: like_of) {
        totalCount
        edges {
          node {
             // node queries
          }
        }
      }

      replies: group(field: in_reply_to) {
        totalCount
        edges {
          node {
            // node queries
        }
      }
    }
}
...</span>
</code></pre>
<h3 id="heading-how-to-optimize-the-webmentions-author-images">How to Optimize the Webmentions Author Images</h3>
<p>I mentioned earlier that the <code>gatsby-source-plugin</code> allows us to optimize the images returned from the Webmentions query.</p>
<p>To be able to optimize the images you must have <code>[gatsby-plugin-image](https://www.gatsbyjs.com/plugins/gatsby-plugin-image/)</code>, <code>gatsby-plugin-sharp</code>, <code>gatsby-transformer-sharp</code> and <code>gatsby-source-filesystem</code> installed:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
      likes: group(field: like_of) {
        totalCount
        edges {
          node {
            // other node queries
            author {
                photoSharp {
                  childImageSharp {
                    gatsbyImageData(
                      width: 38
                      placeholder: BLURRED
                      formats: [AUTO, WEBP, AVIF]
                )
              }
             }
            }
          }
        }
      }
    }
`</span>
</code></pre>
<p>For other optimizations and image processing, refer to the <code>gatsby-plugin-image</code> <a target="_blank" href="https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/">reference guide</a>.</p>
<h3 id="heading-how-to-add-custom-pagination">How to Add Custom Pagination</h3>
<p>You can paginate your Webmentions with one of the many Gatsby pagination plugins. But all I wanted was a simple "Load More" button, and thankfully smarter people like <a target="_blank" href="https://www.erichowey.dev/">Eric Howey</a> have already thought about this.</p>
<p>The code below is from the article <a target="_blank" href="https://www.erichowey.dev/writing/load-more-button-and-infinite-scroll-in-gatsby/"><strong>Load more button and infinite scroll in Gatsby</strong></a> (although I made a few adjustments to it):</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { graphql } <span class="hljs-keyword">from</span> <span class="hljs-string">"gatsby"</span>
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"./Button"</span>
<span class="hljs-keyword">import</span> Comment <span class="hljs-keyword">from</span> <span class="hljs-string">"./Comment"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query($slug: String!) {
    allWebmention(filter: { wm_slug: { eq: $slug } }) {
        totalCount
        edges {
          node {
          // node queries
         }
        }
       }
      }

export default function BlogPost({ data }) {
...
  const replies = data.allWebmention

  const [state, setState] = useState({
    list: [...replies.slice(0, 5)],
    Load_more: false,
    has_more: replies.length &gt; 5,
  })
  const handleState = () =&gt; {
    state.Load_more = true
  }
  //handle loading more mentions
  useEffect(() =&gt; {
    if (state.Load_more &amp;&amp; state.has_more) {
      const currentLength = state.list.length
      const is_more = currentLength &lt; replies.length
      const new_list = is_more
        ? replies.slice(currentLength, currentLength + 5)
        : []
      setState.list = [...state.list, ...new_list]
      setState.Load_more = false
    }
  }, [state.Load_more, state.has_more, replies, state.list])

  useEffect(() =&gt; {
    const is_more = state.list.length &lt; replies.length
    setState.has_more = is_more

  }, [state.list,replies.length])
  return (
    &lt;div className="webmentions-wrapper"&gt;
      {replies.length &gt; 0 ? (
        &lt;&gt;
    &lt;h4&gt;Comments &lt;span className="webmentions-counter"&gt;{replies[0].totalCount}&lt;/span&gt; &lt;/h4&gt;
    &lt;ol className="webmentions__list"&gt;
      {state.list.edges.map(edge =&gt; (
        &lt;Comment
          key={edge.node.wm_id}
          imageUrl={edge.node.authorImg}
          authorUrl={edge.node.authorUrl}
          authorName={edge.node.authorName}
          dtPublished={edge.node.published}
          dtPublishedFormated={edge.node.publishedFormated}
          content={edge.node.content &amp;&amp; edge.node.content.html}
          url={edge.node.url}
        /&gt;
      ))}
      &lt;/ol&gt;
      &lt;div className="webmentions-load text-center"&gt;
      {state.has_more ? (
          &lt;Button
            event={handleState}
            name="Load More"
            label="Load More Webmentions"
            btnSize="small"
            btnType="primary"
          /&gt;
      ) : (
        &lt;p&gt;No More Mentions...&lt;/p&gt;
      )}
    &lt;/div&gt;
        &lt;/&gt;
      ) : (
        &lt;p&gt;No Webmentions found&lt;/p&gt;
      )}
    &lt;/div&gt;
  )
}</span>
</code></pre>
<h2 id="heading-continuous-deployment-with-webmentions">Continuous Deployment with Webmentions</h2>
<p>As you might have noticed, your Webmentions data are pulled at build time. This means that users won't see new Webmentions unless your site has been built.</p>
<p>I host my site in Gatsby Cloud and to avoid crawling out of bed at midnight to build my site. It provides us with a WEBHOOK that can trigger a build for your site even while you are sleeping.</p>
<p>If you are using Gatsby cloud, go to your <a target="_blank" href="https://www.gatsbyjs.com/dashboard/">dashboard</a> and copy the Webhook:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Web-capture_21-7-2021_7512_www.gatsbyjs.com.jpeg" alt="Gatsby Cloud dashboard" width="600" height="400" loading="lazy"></p>
<p><em>Webhooks from Gatsby Cloud dashboard</em></p>
<p>Once you have copied the Webhook, head over to your webmention.io dashboard, click on <a target="_blank" href="https://webmention.io/settings/webhooks">Web hooks</a>, and paste the copied Webhook into the form:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Web-capture_21-7-2021_72655_webmention.io.jpeg" alt="webmention.io web hook" width="600" height="400" loading="lazy"></p>
<p><em>webmention.io web hooks</em></p>
<p>That's it – you are done. Now any time you get a new Webmention the Web hook will build your site automatically.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In this tutorial we learned how to implement Webmentions on your Gatsby site. If you have any questions or you found this tutorial useful, please feel free to connect with me on Twitter <a target="_blank" href="https://twitter.com/sprucekhalifa">@sprucekhalifa</a>. Thank you.</p>
<p>Happy Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Website Theming? How to Use CSS Custom Properties and Gatsby.js to Customize Your Site ]]>
                </title>
                <description>
                    <![CDATA[ In this article, I'm going to show you how to theme your website so users can customize certain elements to their tastes. We'll talk about website themes, how theming works, and we'll end with a demo so you can see it in action. Let's dive in. Table ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/website-theming-with-css-custom-properties-and-gatsbyjs/</link>
                <guid isPermaLink="false">66d4616c73634435aafcefef</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ css properties ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ themes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Spruce Emmanuel ]]>
                </dc:creator>
                <pubDate>Thu, 01 Jul 2021 18:19:21 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/theming-website-preview-1-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, I'm going to show you how to theme your website so users can customize certain elements to their tastes.</p>
<p>We'll talk about website themes, how theming works, and we'll end with a demo so you can see it in action. Let's dive in.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-theme">What is a Website Theme?</a><br>  What is theming?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-should-you-care-about-website-theming">Why Should You Care About Theming?</a><br>  “It’s their screen, machine and software”<br>  Theming increases readability<br>  All the cool cats are using it - theming in the wild</p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-theme-properties">How to Use Theme Properties</a><br>  What are theme properties?<br>  What are CSS custom properties?<br>  How to set up theme properties in Gatsby.js<br>  How to store the theme properties<br>  How to transform theme properties to CSS custom properties</p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-the-theme-switcher-component">How to Use the Theme Switcher Component</a><br>  The markup<br>  How to set up the state<br>  How to update the state<br>  How to persist state to <code>LocalStorage</code></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-who-is-this-article-for">Who is this article for?</h2>
<p>This article is aimed at developers who already have a basic knowledge of CSS, React, and Gatsby who want to learn how to create a user theme-able Gatsby or React app.</p>
<p>By the end of this article, you should understand how theming works and how to implement theming on your Gatsby sites.</p>
<h2 id="heading-what-is-a-theme">What is a theme?</h2>
<p>In order to understand what website theming is, let's first look at what a <strong>website theme</strong> is and what make up a theme.</p>
<p>A theme in the context of a website is the overall look, feel, and style of your <strong>website.</strong> A theme may include:</p>
<ul>
<li><p>fonts</p>
</li>
<li><p>font size</p>
</li>
<li><p>color schemes</p>
</li>
<li><p>layouts</p>
</li>
<li><p>aesthetics</p>
</li>
</ul>
<p>A theme controls the design of your website. It determines what your website looks like from the surface, and it is the part of your website that has a direct impact on your users.</p>
<p>A theme is also a set of styles worn by a website.</p>
<h2 id="heading-what-is-theming">What is Theming?</h2>
<p>Theming is to a website what clothes are to our bodies. Imagine wearing the same clothes to a meeting, a wedding, and a farm - sounds funny right? Of course you probably wouldn't do that if you had the choice.</p>
<p>For each occasion you would wear the appropriate type or style of dress. That’s what website theming is – it allows our users to choose the look and feel of our website with a set of styles based on different occasions.</p>
<p>Theming is simply giving users the ability to make customizations to our websites and apps. You can also think about theming as a set of customizations users can make to our websites or applications based on their choices.</p>
<p>Theming happens when the user is able to tell your website what they prefer to see, for example:</p>
<ul>
<li><p>Clicking a button to change the background of a website to red or black</p>
</li>
<li><p>Increasing or decreasing the font size of a site website/application</p>
</li>
<li><p>Clicking a button to remove content not relevant to the user.</p>
</li>
</ul>
<p>Here’s a tip: letting or asking your users to determine your website theme from scratch is a bad idea. You or your team should provide users with an accessible and usable default theme, since in most cases many users will never customize “their view” on your website no matter how easy it is. |</p>
<h2 id="heading-why-should-you-care-about-website-theming">Why should you care about website theming?</h2>
<p>Apart from letting users know you care about their personal preferences, there are other reasons to let your users theme your website. Some of them include:</p>
<h3 id="heading-its-their-screen-machine-and-software">“It’s their Screen, Machine and Software”</h3>
<p>This is a quote from Jakob Nielsen's 2002 article: <a target="_blank" href="https://www.nngroup.com/articles/let-users-control-font-size/">Let users control font size</a>.</p>
<p>The fact that your website is running on the user’s screen, machine, and software (and probably draining their batteries too) is enough reason them to be able to customize their experience on your site.</p>
<h3 id="heading-theming-improves-website-readability">Theming improves website readability</h3>
<p>Quoting D. Bnonn from the article: <a target="_blank" href="https://www.smashingmagazine.com/2011/10/16-pixels-body-copy-anything-less-costly-mistake/">16 Pixels Font Size: For Body Copy. Anything Less Is A Costly Mistake</a></p>
<blockquote>
<p>Fact: Most Web Users Hate The “Normal” Font Size.</p>
</blockquote>
<p>With this fact in mind, theming can help readers out by allowing them to choose the font size that suites their eyes best.</p>
<p>Oh and here is another quote from the same article.</p>
<blockquote>
<p>Readership = Revenue.</p>
</blockquote>
<h3 id="heading-all-the-cool-cats-are-using-it-theming-in-the-wild">All the cool cats are using it – theming in the wild</h3>
<p>A lot of developers have used the idea of theming to create dark mode versions of their websites. Other’s have taken this idea further to allow users change font-size, colors, and background based on individual preferences.</p>
<p>Here's an example of this kind of customization in the Twitter web app:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/twitter.com_home.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Twitter customize theme UI</em></p>
<p>Still not feeling motivated yet? If you still need more proof that theming is a good idea, <a target="_blank" href="https://darkmodelist.com/">here’s a whole list of websites, apps, and software</a> that use theming to provide dark and light modes for their users.</p>
<h2 id="heading-how-to-use-theme-properties">How to Use Theme Properties</h2>
<p>Now that you know what theming is and have seen sites that use this idea of theming in their websites and applications, let’s learn what theme properties are.</p>
<h3 id="heading-what-are-theme-properties">What are theme properties?</h3>
<p>Theme properties are a set of CSS custom properties that make up a theme. Remember that “a theme is a set of styles worn by a website” – so theme properties are all the properties that make up the styles a site wears. For example:</p>
<pre><code class="lang-css"><span class="hljs-selector-attr">[data-theme=<span class="hljs-string">"default"</span>]</span> {
  <span class="hljs-attribute">--font-size</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">--background</span>: red;
}
</code></pre>
<p>In the example above, <code>[data-theme="default"]</code> is our theme, while all the CSS custom properties inside are the theme properties. You get the idea, right?</p>
<p>Here’s a tip: your theme properties don’t have to be just CSS custom properties. They can also be any valid CSS properties that you want to apply to a specific theme.</p>
<p>Before we move forward, let’s first understand what CSS custom properties are</p>
<h3 id="heading-what-are-css-custom-properties-also-known-as-css-variables">What are CSS Custom Properties (also known as CSS variables)?</h3>
<p>CSS custom properties are entities which hold values that you can reuse throughout an entire site or document.</p>
<p>For the sake of this tutorial we are not going to cover CSS custom properties in depth. You can <a target="_blank" href="https://www.freecodecamp.org/news/css-customs-properties-cheatsheet-c86778541f7d/">read more about custom properties here</a>.</p>
<p>Also there are a lot of great tutorials out there that cover CSS custom properties and how to use them for theming, so we’ll leave the theory to those other articles.</p>
<p>For a strategic guide on how to use CSS custom properties for theming check out this awesome article: <a target="_blank" href="https://www.smashingmagazine.com/2018/05/css-custom-properties-strategy-guide/">A Strategy Guide To CSS Custom Properties</a>.</p>
<p>Although we are not covering CSS custom properties in depth, I want to point out a few reasons why CSS custom properties are ideal for website theming:</p>
<ul>
<li><p>They are reusable – you can use them throughout your CSS</p>
</li>
<li><p>They reduce the complexity of our code, since you no longer need to create different stylesheet to achieve a theme-able website</p>
</li>
<li><p>They are available at runtime, which means you can update their value in the browser, via JavaScript, with immediate results.</p>
</li>
</ul>
<h2 id="heading-how-to-set-up-theme-properties-in-gatsbyjs">How to Set Up Theme Properties in Gatsby.js</h2>
<p>Of course you can hard code theme properties directly inside your CSS file like any other CSS properties. But having to scroll up a few lines of your CSS code anytime you want to make a few changes to your themes sounds tedious, right?</p>
<p><a target="_blank" href="https://mxb.dev/about/">Max Böck</a> in his article <a target="_blank" href="https://mxb.dev/blog/color-theme-switcher/">“Color Theme Switcher”</a> advises defining our themes in a central location.</p>
<p>Having a central location (file) where you can easily access and manage your themes sounds like an interesting idea. And this is the kind of thing Gatsby was made for.</p>
<p>Quoting the Gatsby docs:</p>
<blockquote>
<p>“A core feature of Gatsby.js is it’s ability to load data from anywhere.”</p>
</blockquote>
<p>This means you can source data from a JSON file which will be available at build time. When you import this data you can then iterate over it with the <code>Array.map</code> method and render it in a React component.</p>
<h3 id="heading-how-to-store-theme-properties">How to Store Theme Properties</h3>
<p>In your Gatsby project folder, create a directory called content if it doesn't already exists. Then add a new file called <code>themes.json</code> with the following content:</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"default"</span>,
    <span class="hljs-attr">"colors"</span>: {
      <span class="hljs-attr">"primary-color"</span>: <span class="hljs-string">"#0250bb"</span>,
      <span class="hljs-attr">"text"</span>: <span class="hljs-string">"#20123a"</span>,
      <span class="hljs-attr">"text-alt"</span>: <span class="hljs-string">"#42425a"</span>,
      <span class="hljs-attr">"border"</span>: <span class="hljs-string">"#ededf0"</span>,
      <span class="hljs-attr">"background"</span>: <span class="hljs-string">"#ffffff"</span>,
      <span class="hljs-attr">"background-alt"</span>: <span class="hljs-string">"#f9f9fa"</span>,
      <span class="hljs-attr">"color-scheme"</span>: <span class="hljs-string">"light"</span>
    }
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"dark"</span>,
    <span class="hljs-attr">"colors"</span>: {
      <span class="hljs-attr">"primary-color"</span>: <span class="hljs-string">"#7f5af0"</span>,
      <span class="hljs-attr">"text"</span>: <span class="hljs-string">"#fffffe"</span>,
      <span class="hljs-attr">"text-alt"</span>: <span class="hljs-string">"#94a1b2"</span>,
      <span class="hljs-attr">"border"</span>: <span class="hljs-string">"#010101"</span>,
      <span class="hljs-attr">"background"</span>: <span class="hljs-string">"#16161a"</span>,
      <span class="hljs-attr">"background-alt"</span>: <span class="hljs-string">"#242629"</span>,
      <span class="hljs-attr">"color-scheme"</span>: <span class="hljs-string">"dark"</span>
    }
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"warm"</span>,
    <span class="hljs-attr">"colors"</span>: {
      <span class="hljs-attr">"primary-color"</span>: <span class="hljs-string">"#ff8e3c"</span>,
      <span class="hljs-attr">"text"</span>: <span class="hljs-string">"#0d0d0d"</span>,
      <span class="hljs-attr">"text-alt"</span>: <span class="hljs-string">"#2a2a2a"</span>,
      <span class="hljs-attr">"background"</span>: <span class="hljs-string">"#eff0f3"</span>,
      <span class="hljs-attr">"background-alt"</span>: <span class="hljs-string">"#fff"</span>,
      <span class="hljs-attr">"border"</span>: <span class="hljs-string">"rgba(0,0,0,.1)"</span>,
      <span class="hljs-attr">"color-scheme"</span>: <span class="hljs-string">"light"</span>
    }
  },
<span class="hljs-comment">// Add other themes here</span>
]
</code></pre>
<p>Each theme gets an <code>id</code>, a set of theme properties, and a CSS <code>color-scheme</code> property.</p>
<p>Here’s a tip – we use the CSS <code>color-scheme</code> property to tell which color scheme (light/dark) our webpage should be rendered in. For a better understanding of <code>color-scheme</code> please refer to this <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme">color scheme</a> guide.</p>
<h3 id="heading-how-to-transform-theme-properties-to-css-custom-properties">How to Transform Theme Properties to CSS Custom Properties</h3>
<p>Right now, the color themes stored in our <code>content/themes.json</code> files are just raw <strong>data</strong>. They need to be transformed into CSS custom properties before they can actually do anything meaningful.</p>
<blockquote>
<p><strong>Data</strong> is a collection of facts, such as numbers, <strong>words</strong>, measurements, observations or just descriptions of things.</p>
</blockquote>
<p>We are going to need our CSS custom properties to be dynamically generated and added as an inline <code>&lt;style&gt;</code> to the <code>&lt;head&gt;</code> of all our site pages.</p>
<p>You need to install two important plugins for this tutorial: react-helmet, a document head manager for React, and gatsby-plugin-react-helmet to allow server rendering of data that's added with React Helmet.</p>
<p>Install these plugins with this command:</p>
<pre><code class="lang-plaintext">npm installl gatsby-plugin-react-helmet react-helmet
</code></pre>
<p>To use these plugins you need to add it to the plugin array in your gatsby-config.js file located at the root of the project directory:</p>
<pre><code class="lang-plaintext">plugins: [gatsby-plugin-react-helmet]
</code></pre>
<p>Since you are going to use React helmet on all your pages, it makes sense to use it in your <code>Layout.js</code> file. In your <code>layout.js</code> file add the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { Helmet } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-helmet"</span>
<span class="hljs-keyword">import</span> themes <span class="hljs-keyword">from</span> <span class="hljs-string">"../../content/themes.json"</span>
<span class="hljs-comment">// other imports</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Layout</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">colors</span>(<span class="hljs-params">theme</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">`
          --primary-color: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"primary-color"</span>]}</span>;
          --text: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"text"</span>]}</span>;
          --text-alt: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"text-alt"</span>]}</span>;
          --background: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"background"</span>]}</span>;
          --background-alt: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"background-alt"</span>]}</span>;
          --border: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"border"</span>]}</span>;
          --shadow: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"shadow"</span>]}</span>;
          color-scheme: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"color-scheme"</span>]}</span>;
    `</span>
  }

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Helmet</span>&gt;</span>
        // other head meta tags

        <span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span>&gt;</span>{`
    ${themes
      .map(theme =&gt; {
        if (theme.id === "default") {
          return `
          :root {
            ${colors(theme)}
          }
        `
        } else if (theme.id === "dark") {
          return `
          @media (prefers-color-scheme: dark) {
            ${colors(theme)}
          }
        `
        }
      })
      .join("")}
    ${themes
      .map(theme =&gt; {
        return `
        [data-theme="${theme.id}"] {
          ${colors(theme)}
        }
      `
      })
      .join("")}
  `}
        <span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Helmet</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"main"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Footer</span> /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<p>Let's break this down a bit.</p>
<p>First, the themes and react-helmet are imported from <code>content/themes.json</code> and React respectively:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { Helmet } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-helmet"</span>
<span class="hljs-keyword">import</span> themes <span class="hljs-keyword">from</span> <span class="hljs-string">"../../content/themes.json"</span>
<span class="hljs-comment">// other imports</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Layout</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> (

  )
}
</code></pre>
<p>It creates a function which will transform our themes to CSS custom properties:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">colors</span>(<span class="hljs-params">theme</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">`
          --primary-color: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"primary-color"</span>]}</span>;
          --text: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"text"</span>]}</span>;
          --text-alt: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"text-alt"</span>]}</span>;
          --background: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"background"</span>]}</span>;
          --background-alt: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"background-alt"</span>]}</span>;
          --border: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"border"</span>]}</span>;
          --shadow: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"shadow"</span>]}</span>;
          color-scheme: <span class="hljs-subst">${theme.colors[<span class="hljs-string">"color-scheme"</span>]}</span>;
    `</span>
  }
</code></pre>
<p>Inside our <code>&lt;Helmet&gt;</code> we add a <code>&lt;style&gt;</code> tag to our document’s head.</p>
<p>Here’s a tip – if you need to add a style to the document’s head, you have to render the style as a string within curly braces.</p>
<p>In the first <code>Array.map</code> method, we check if there’s a theme with <code>id</code> equal to <code>default</code>. If there is, we set it as our default color scheme in the <code>:root{}</code>. We also check if there’s a theme with <code>id</code> equal to <code>dark</code>. If there is, we use it when the <code>prefers-color-scheme</code> of the user is dark:</p>
<pre><code class="lang-js">${themes
      .map(<span class="hljs-function"><span class="hljs-params">theme</span> =&gt;</span> {
        <span class="hljs-keyword">if</span> (theme.id === <span class="hljs-string">"default"</span>) {
          <span class="hljs-keyword">return</span> <span class="hljs-string">`
          :root {
            <span class="hljs-subst">${colors(theme)}</span>
          }
        `</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (theme.id === <span class="hljs-string">"dark"</span>) {
          <span class="hljs-keyword">return</span> <span class="hljs-string">`
          @media (prefers-color-scheme: dark) {
            <span class="hljs-subst">${colors(theme)}</span>
          }
        `</span>
        }
      })
      .join(<span class="hljs-string">""</span>)}
</code></pre>
<p>In the last <code>Array.map</code> method, we iterate over our themes and each theme gets a <code>[data-theme=""]</code> attribute selector:</p>
<pre><code class="lang-js"> ${themes
      .map(<span class="hljs-function"><span class="hljs-params">theme</span> =&gt;</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-string">`
        [data-theme="<span class="hljs-subst">${theme.id}</span>"] {
          <span class="hljs-subst">${colors(theme)}</span>
        }
      `</span>
      })
      .join(<span class="hljs-string">""</span>)}
</code></pre>
<p>Now if you inspect the head of your site you should see all the theme properties in your <code>content/themes.json</code> file nicely generated as CSS custom properties. In fact if you add the attribute <code>data-theme="name of your theme"</code> to your <code>html</code> tag via the dev tools, your theme should work perfectly well.</p>
<h2 id="heading-how-to-use-the-theme-switcher-component">How to Use the Theme Switcher Component</h2>
<p>Well, we can't have users manually editing our site via dev tools anytime they want to use a different theme on our site. So all that’s left in this tutorial is to create a UI so that users can easily <strong>Theme</strong> our website.</p>
<h3 id="heading-the-markup">The Markup</h3>
<p>Create a new file called <code>themes.js</code> in your components directory and add the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> themes <span class="hljs-keyword">from</span> <span class="hljs-string">"../../content/theme.json"</span>

<span class="hljs-keyword">const</span> Theme = <span class="hljs-function">() =&gt;</span> {

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"theme"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"theme-close text-right"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>x<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"theme-wrapper__inner"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"theme-header text-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">strong</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"theme-title"</span>&gt;</span>Select Theme<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
            Please Note that Changes made here will affect other pages across
            the entire site.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"theme-content"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"schemes"</span>&gt;</span>
            {theme.map(data =&gt; {
              return (
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"scheme"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"scheme-btn js-scheme-btn"</span>
                    <span class="hljs-attr">aria-label</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">data.id</span>}`}
                    <span class="hljs-attr">name</span>=<span class="hljs-string">"scheme"</span>
                    <span class="hljs-attr">value</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">data.id</span>}`}
                    <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">backgroundColor:</span> `${<span class="hljs-attr">data.colors</span>["<span class="hljs-attr">background</span>"]}` }}
                  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              )
            })}
          <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"theme-content"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"theme-range"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"font"</span> <span class="hljs-attr">title</span>=<span class="hljs-string">{state.font}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xsmall"</span>&gt;</span>Aa<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"font"</span>
                <span class="hljs-attr">min</span>=<span class="hljs-string">"10"</span>
                <span class="hljs-attr">max</span>=<span class="hljs-string">"20"</span>
                <span class="hljs-attr">step</span>=<span class="hljs-string">"2"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"theme-range__slider"</span>
              /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-large"</span>&gt;</span>Aa<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Theme
</code></pre>
<p>Let’s break down this code a bit so we know what's going on.</p>
<p>First we import our themes from content/themes.js and iterate over it with a <code>Array.map</code> method. For each theme, I created a button with a background color equal to its <code>background-color</code> with a value equal to its <code>id</code>.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"schemes"</span>&gt;</span>
            {theme.map(data =&gt; {
              return (
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"scheme"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"scheme-btn js-scheme-btn"</span>
                    <span class="hljs-attr">aria-label</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">data.id</span>}`}
                    <span class="hljs-attr">name</span>=<span class="hljs-string">"scheme"</span>
                    <span class="hljs-attr">value</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">data.id</span>}`}
                    <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">backgroundColor:</span> `${<span class="hljs-attr">data.colors</span>["<span class="hljs-attr">background</span>"]}` }}
                  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              )
            })}
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<p>To change the font size of our text, I also added an <code>input</code> field of type <code>range</code> with a <code>min</code> value of <code>10px</code> and <code>max</code> value of <code>20px</code>.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
  <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span>
  <span class="hljs-attr">name</span>=<span class="hljs-string">"font"</span>
  <span class="hljs-attr">min</span>=<span class="hljs-string">"10"</span>
  <span class="hljs-attr">max</span>=<span class="hljs-string">"20"</span>
  <span class="hljs-attr">step</span>=<span class="hljs-string">"2"</span>
  <span class="hljs-attr">className</span>=<span class="hljs-string">"theme-range__slider"</span>
  /&gt;</span>
</code></pre>
<p>With some added CSS (which we won't cover in this tutorial) we now have a UI that looks like the one below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/localhost_8000_--13-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>iamspruce.dev customize theme UI</em></p>
<h3 id="heading-how-to-set-up-the-state">How to Set Up the State</h3>
<p>We'll start by importing the <code>useState()</code> hook from React:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { useState} <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>

<span class="hljs-keyword">const</span> Theme = <span class="hljs-function">() =&gt;</span> {

  <span class="hljs-keyword">return</span> (
  )
}
</code></pre>
<p>We use React <a target="_blank" href="https://reactjs.org/docs/hooks-reference.html#lazy-initial-state">Lazy Initialization</a>, which lets us pass a function to <code>useState()</code> that we'll use during the initial render.</p>
<p>Quoting the React docs:</p>
<blockquote>
<p>"If the initial state is the result of an expensive calculation, you may provide a function instead, which will be executed only in the initial render."</p>
</blockquote>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { useState} <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> themes <span class="hljs-keyword">from</span> <span class="hljs-string">"../../content/theme.json"</span>

<span class="hljs-keyword">const</span> Theme = <span class="hljs-function">() =&gt;</span> {
 <span class="hljs-keyword">const</span> [state, setState] = useState(<span class="hljs-function">() =&gt;</span> {
     <span class="hljs-keyword">const</span> localVal =
       <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">"undefined"</span> &amp;&amp; <span class="hljs-built_in">window</span>.localStorage.getItem(<span class="hljs-string">"theme"</span>)
     <span class="hljs-keyword">let</span> obj = {
       <span class="hljs-attr">font</span>: <span class="hljs-number">15</span>,
       <span class="hljs-attr">scheme</span>: <span class="hljs-string">"default"</span>,
     }
     <span class="hljs-keyword">return</span> localVal !== <span class="hljs-literal">null</span> ? <span class="hljs-built_in">JSON</span>.parse(localVal) : obj
   })
  <span class="hljs-keyword">return</span> (

  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Theme
</code></pre>
<p>In our case, we’re using it to check for the value in <code>localStorage()</code>. If the value exists, will use that as our initial value. Otherwise, will use the default <code>obj</code>.</p>
<p>We’re checking if the window object exists <code>(typeof window !== “undefined”)</code> because at build time the window’s object does not exist. If you run <code>gatsby build</code> without checking if the windows object exists or not, you’ll get an error that looks like this:</p>
<p><code>WebpackError: ReferenceError: localStorage is not defined</code></p>
<h3 id="heading-how-to-update-the-state">How to Update the State</h3>
<p>The next step is to have an <code>onClick</code> and <code>onChange</code> eventListener update our state. For that we are going to create a function:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { useState} <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> themes <span class="hljs-keyword">from</span> <span class="hljs-string">"../../content/theme.json"</span>

<span class="hljs-keyword">const</span> Theme = <span class="hljs-function">() =&gt;</span> {
 <span class="hljs-keyword">const</span> [state, setState] = useState(<span class="hljs-function">() =&gt;</span> {
     <span class="hljs-keyword">const</span> localVal =
       <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">"undefined"</span> &amp;&amp; <span class="hljs-built_in">window</span>.localStorage.getItem(<span class="hljs-string">"theme"</span>)
     <span class="hljs-keyword">let</span> obj = {
       <span class="hljs-attr">font</span>: <span class="hljs-number">15</span>,
       <span class="hljs-attr">scheme</span>: <span class="hljs-string">"default"</span>,
     }
     <span class="hljs-keyword">return</span> localVal !== <span class="hljs-literal">null</span> ? <span class="hljs-built_in">JSON</span>.parse(localVal) : obj
   })
<span class="hljs-comment">// the update function</span>
  <span class="hljs-keyword">const</span> update = <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> { name, value } = e.target
    setState(<span class="hljs-function"><span class="hljs-params">prevState</span> =&gt;</span> ({
      ...prevState,
      [name]: value,
    }))
  }

  <span class="hljs-keyword">return</span> (

  )
}
</code></pre>
<p>We passed in a Object as an initial value for our <code>useState</code> because we can update multiple states with one <code>useState</code> hook. We now need to set the update function on our UI:</p>
<pre><code class="lang-js">...
{theme.map(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"scheme"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
       <span class="hljs-attr">onClick</span>=<span class="hljs-string">{update}</span> // <span class="hljs-attr">set</span> <span class="hljs-attr">the</span> <span class="hljs-attr">update</span> <span class="hljs-attr">function</span> <span class="hljs-attr">to</span> <span class="hljs-attr">an</span> <span class="hljs-attr">Onclick</span> <span class="hljs-attr">event</span> 
       <span class="hljs-attr">className</span>=<span class="hljs-string">"scheme-btn js-scheme-btn"</span>
       <span class="hljs-attr">aria-label</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">data.id</span>}`}
       <span class="hljs-attr">name</span>=<span class="hljs-string">"scheme"</span>
       <span class="hljs-attr">value</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">data.id</span>}`}
       <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">backgroundColor:</span> `${<span class="hljs-attr">data.colors</span>["<span class="hljs-attr">background</span>"]}` }}
       &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span>
   )
})}

&lt;input
  type=<span class="hljs-string">"range"</span>
  name=<span class="hljs-string">"font"</span>
  min=<span class="hljs-string">"10"</span>
  max=<span class="hljs-string">"20"</span>
  step=<span class="hljs-string">"2"</span>
  className=<span class="hljs-string">"theme-range__slider"</span>
  onChange={update} <span class="hljs-comment">// set the update function to an Onchange event</span>
  value={state.font}
/&gt;
</code></pre>
<h3 id="heading-how-to-persist-our-changes-in-localstorage">How to Persist Our Changes In LocalStorage</h3>
<p>The final step is to make sure we update <code>localStorage</code> and our website with the current values from our state whenever the state value changes. For that we’ll use the <code>useEffect</code> Hook, which lets us <strong>run some code after React has updated the DOM.</strong></p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { useState} <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> themes <span class="hljs-keyword">from</span> <span class="hljs-string">"../../content/theme.json"</span>

<span class="hljs-keyword">const</span> Theme = <span class="hljs-function">() =&gt;</span> {
 <span class="hljs-keyword">const</span> [state, setState] = useState(<span class="hljs-function">() =&gt;</span> {
     <span class="hljs-keyword">const</span> localVal =
       <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">"undefined"</span> &amp;&amp; <span class="hljs-built_in">window</span>.localStorage.getItem(<span class="hljs-string">"theme"</span>)
     <span class="hljs-keyword">let</span> obj = {
       <span class="hljs-attr">font</span>: <span class="hljs-number">15</span>,
       <span class="hljs-attr">scheme</span>: <span class="hljs-string">"default"</span>,
     }
     <span class="hljs-keyword">return</span> localVal !== <span class="hljs-literal">null</span> ? <span class="hljs-built_in">JSON</span>.parse(localVal) : obj
   })

  <span class="hljs-keyword">const</span> update = <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> { name, value } = e.target
    setState(<span class="hljs-function"><span class="hljs-params">prevState</span> =&gt;</span> ({
      ...prevState,
      [name]: value,
    }))
  }

<span class="hljs-comment">// persisting state to localStorage</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">window</span>.localStorage.setItem(<span class="hljs-string">"theme"</span>, <span class="hljs-built_in">JSON</span>.stringify(state))
    <span class="hljs-keyword">let</span> root = <span class="hljs-built_in">document</span>.documentElement
    root.setAttribute(<span class="hljs-string">"data-theme"</span>, state.scheme)
    root.style.setProperty(<span class="hljs-string">"--font-size"</span>, <span class="hljs-string">`<span class="hljs-subst">${state.font}</span>px`</span>)
  }, [state])

  <span class="hljs-keyword">return</span> (

  )
}
</code></pre>
<p>Congratulations! If you made it this far you now have a complete user theme-able website. The overall design of our <strong>switch theme UI</strong> now looks like this:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/cMboQU-qwyE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>For a live preview of the site, visit <a target="_blank" href="https://www.iamspruce.dev/">https://www.iamspruce.dev/</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>There's really no limit with what you can do with <strong>theming</strong>. Although this tutorial uses Gatsby.js, you can easily apply these concepts to other React-based static site generators.</p>
<p>If you found this tutorial useful, kindly follow me on Twitter <a target="_blank" href="https://twitter.com/sprucekhalifa">@sprucekhalifa</a> .</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Gatsby Starter Blog: How to Add Header Images to Posts with Support for Twitter Cards ]]>
                </title>
                <description>
                    <![CDATA[ By David Good If you're like me, you used Gatsby Starter Blog to kickstart your personal blog, made a few customizations, and then just rolled with it.  Adding new posts in the form of markdown is great. But it also means you rarely have a reason to ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/gatsby-blog-header-image-twitter-card/</link>
                <guid isPermaLink="false">66d45e01680e33282da25e4d</guid>
                
                    <category>
                        <![CDATA[ blog ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ open graph ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Twitter ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 29 Dec 2020 00:33:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/12/freeCodeCamp-GatsbyBlogImageTwitterCard-5.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By David Good</p>
<p>If you're like me, you used <a target="_blank" href="https://www.gatsbyjs.com/starters/gatsbyjs/gatsby-starter-blog">Gatsby Starter Blog</a> to kickstart your personal blog, made a few customizations, and then just rolled with it. </p>
<p>Adding new posts in the form of markdown is great. But it also means you rarely have a reason to look at the code. So when I decided to add header images to my posts with support for <a target="_blank" href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards">Twitter Cards</a>, I felt lost.</p>
<p>My requirements were to be able to add a large header image with a caption to a post as you can see here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://davidagood.com/dynamodb-enhanced-client-java-heterogeneous-item-collections/">https://davidagood.com/dynamodb-enhanced-client-java-heterogeneous-item-collections/</a></div>
<p>Furthermore, a tweet which contains a link to the post should "expand" into <a target="_blank" href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary-card-with-large-image">Twitter's Summary Card with Large Image</a>, like this:</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/helloworldless/status/1336323721254948864"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>And finally, for posts which do not specify an image, a default image should be shown using <a target="_blank" href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary">Twitter's Summary Card</a>. Here's what that looks like where I've used my website logo as the default image:</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/helloworldless/status/1338482084445347844"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p><strong>Note:</strong> Twitter's docs state that a website logo should not be used for a card image (see <code>twitter:image</code> section <a target="_blank" href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary#reference">here</a>). I'll leave it to you to decide whether it makes sense to use a fixed image as a fallback like I have here.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>Here are the five high-level steps which I will be guiding you through. I'll attempt to explain everything in depth and provide links to other resources along the way. That way you will build up your knowledge of Gatsby which you can draw from to tackle the later, more complicated steps.</p>
<ol>
<li>Add Document Metadata Tags</li>
<li>Source Default Image using GraphQL</li>
<li>Source Post-Specific Image Properties using GraphQL</li>
<li>Add Header Image to Blog Post Template</li>
<li>Add New Properties to Post's Frontmatter</li>
</ol>
<p>The tools which we'll be using to accomplish this all come out of the box with <a target="_blank" href="https://www.gatsbyjs.com/starters/gatsbyjs/gatsby-starter-blog">Gatsby Starter Blog</a>!</p>
<ul>
<li><a target="_blank" href="https://github.com/nfl/react-helmet">React Helmet</a> - Used in the <code>SEO</code> component to add meta tags to the document head to support Twitter Cards and other <a target="_blank" href="https://ogp.me/">Open Graph</a> tags</li>
<li><a target="_blank" href="https://www.gatsbyjs.com/plugins/gatsby-source-filesystem/">Gatsby Source Filesystem</a> - A "plugin for sourcing data into your Gatsby application from your local filesystem", images in our case</li>
<li><a target="_blank" href="https://www.gatsbyjs.com/plugins/gatsby-image/">Gatsby Image</a> - "a React component specially designed to work seamlessly with Gatsby’s GraphQL queries. It combines <a target="_blank" href="https://image-processing.gatsbyjs.org/">Gatsby’s native image processing</a> capabilities with advanced image loading techniques to easily and completely optimize image loading for your sites. <code>gatsby-image</code> uses <a target="_blank" href="https://www.gatsbyjs.com/packages/gatsby-plugin-sharp/">gatsby-plugin-sharp</a> to power its image transformations."</li>
<li><a target="_blank" href="https://www.gatsbyjs.com/plugins/gatsby-plugin-sharp/">Gatsby Plugin Sharp</a> - "Exposes several image processing functions built on the <a target="_blank" href="https://github.com/lovell/sharp">Sharp image processing library</a>". We use this for resizing images.</li>
</ul>
<h2 id="heading-how-to-add-document-metadata-tags">How to Add Document Metadata Tags</h2>
<p>First, we will wire up the HTML metadata tags which can be read by Twitter and any other platform or tool which understands <a target="_blank" href="https://ogp.me/">Open Graph</a> such as Google, Facebook, and WhatsApp. </p>
<p>Learn more about document metadata here: <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML">What’s in the head? Metadata in HTML</a>.</p>
<p>Open the <code>SEO</code> component in <code>src/components/seo.js</code>. The first thing to notice is that this is using <a target="_blank" href="https://github.com/nfl/react-helmet">React Helmet</a>, and it already has many Open Graph and Twitter meta tags like <code>og:title</code>, <code>twitter:description</code>. It even has a <code>twitter:card</code> tag with a value of "summary" which enables a basic Twitter Summary Card with no image:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/seo.js</span>
<span class="hljs-keyword">const</span> SEO = <span class="hljs-function">(<span class="hljs-params">{ description, lang, meta, title }</span>) =&gt;</span> { 
<span class="hljs-comment">// Details omitted for brevity </span>
<span class="hljs-keyword">return</span> ( 
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Helmet</span> 
        <span class="hljs-attr">htmlAttributes</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">lang</span> }} 
        <span class="hljs-attr">title</span>=<span class="hljs-string">{title}</span> 
        <span class="hljs-attr">titleTemplate</span>=<span class="hljs-string">{</span>`%<span class="hljs-attr">s</span> | ${<span class="hljs-attr">site.siteMetadata.title</span>}`} 
        <span class="hljs-attr">meta</span>=<span class="hljs-string">{[</span> 
            { <span class="hljs-attr">name:</span> `<span class="hljs-attr">description</span>`, <span class="hljs-attr">content:</span> <span class="hljs-attr">metaDescription</span>, }, 
            { <span class="hljs-attr">property:</span> `<span class="hljs-attr">og:title</span>`, <span class="hljs-attr">content:</span> <span class="hljs-attr">title</span>, }, 
            { <span class="hljs-attr">property:</span> `<span class="hljs-attr">og:description</span>`, <span class="hljs-attr">content:</span> <span class="hljs-attr">metaDescription</span>, }, 
            { <span class="hljs-attr">property:</span> `<span class="hljs-attr">og:type</span>`, <span class="hljs-attr">content:</span> `<span class="hljs-attr">website</span>`, }, 
            { <span class="hljs-attr">property:</span> `<span class="hljs-attr">twitter:card</span>`, <span class="hljs-attr">content:</span> `<span class="hljs-attr">summary</span>`, }, 
            { <span class="hljs-attr">property:</span> `<span class="hljs-attr">twitter:creator</span>`, 
              <span class="hljs-attr">content:</span> <span class="hljs-attr">site.siteMetadata.social.twitter</span>, }, 
            // <span class="hljs-attr">...</span></span></span>
</code></pre>
<p>Let's update this component:</p>
<ol>
<li>Add <code>imageUrl</code> and <code>imageAlt</code> parameters. These will be passed as props by the <code>BlogPostTemplate</code> component as we will see later. Note that that I've used "URL" in the prop name to convey the fact that this must be a fully-qualified URL. Relative paths are not supported for the OG image!</li>
<li>Construct the default image URL, <code>defaultImageUrl</code>. I've written a tiny utility function, <code>constructUrl</code>, to concatenate a base URL with a relative path. We will see where <code>data.ogImageDefault</code> comes from in the next section.</li>
<li>Add an <code>ogImageUrl</code> variable which takes the <code>imageSrcUrl</code> prop or, if that's not provided, defaults to <code>defaultImageUrl</code>.</li>
<li>Add objects to the <code>meta</code> array passed to the <code>Helmet</code> component: <code>og:image</code>, <code>twitter:card</code>, and <code>twitter:image:alt</code></li>
</ol>
<p>A few things to note here:</p>
<ol>
<li>Twitter does have its own <code>twitter:image</code> meta tag, but per the <a target="_blank" href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/guides/getting-started#twitter-cards-and-open-graph">docs</a>, we don't need to add both the <code>og:image</code> and the <code>twitter:image</code> tag since Twitter's parser will fall back to the Open Graph tags.</li>
<li>Open Graph specifies the <code>meta</code> attributes <code>property</code> and <code>content</code> whereas Twitter specifies <code>name</code> and <code>content</code>, respectively. But again, the Twitter docs state that their parser will fall back to the Open Graph attributes. This is nice because we can maintain consistency and don't need a bunch of repetitive properties with the same values which we have to keep in sync.</li>
<li>Notable exceptions to using the <code>property</code> attribute on <code>meta</code> tags are any non-Open Graph tags like <code>description</code> which must use the <code>name</code> attribute. I encourage you to use <a target="_blank" href="https://developers.google.com/web/tools/lighthouse">Lighthouse</a> which will identify basic issues with your SEO.</li>
</ol>
<pre><code class="lang-js"><span class="hljs-comment">// util.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> constructUrl = <span class="hljs-function">(<span class="hljs-params">baseUrl, path</span>) =&gt;</span>
  (!baseUrl || !path) ? <span class="hljs-literal">null</span> : <span class="hljs-string">`<span class="hljs-subst">${baseUrl}</span><span class="hljs-subst">${path}</span>`</span>;

<span class="hljs-comment">// src/components/seo.js</span>
<span class="hljs-comment">// Step 1: Add props</span>
<span class="hljs-keyword">const</span> SEO = <span class="hljs-function">(<span class="hljs-params">{ description, lang, meta, title, imageUrl, imageAlt }</span>) =&gt;</span> { 

    <span class="hljs-keyword">const</span> data = useStaticQuery(
        <span class="hljs-comment">// This is explained next</span>
    );

    <span class="hljs-comment">// Step 2: Construct default image URL</span>
    <span class="hljs-comment">// ogImageDefault is explained next</span>
    <span class="hljs-keyword">const</span> defaultImageUrl = constructUrl(data.site.siteMetadata.siteUrl, data.ogImageDefault?.childImageSharp?.fixed?.src)

    <span class="hljs-comment">// Step 3: Add this</span>
    <span class="hljs-keyword">const</span> ogImageUrl = imageUrl || defaultImageUrl; 

    <span class="hljs-keyword">return</span> ( 
        <span class="hljs-comment">// Step 4: Add new meta objects</span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Helmet</span> 
            <span class="hljs-attr">htmlAttributes</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">lang</span> }} 
            <span class="hljs-attr">title</span>=<span class="hljs-string">{title}</span> 
            <span class="hljs-attr">titleTemplate</span>=<span class="hljs-string">{</span>`%<span class="hljs-attr">s</span> | ${<span class="hljs-attr">site.siteMetadata.title</span>}`} 
            <span class="hljs-attr">meta</span>=<span class="hljs-string">{[</span>
                { <span class="hljs-attr">property:</span> `<span class="hljs-attr">og:image</span>`, <span class="hljs-attr">content:</span> <span class="hljs-attr">ogImageUrl</span>, }, 

                // <span class="hljs-attr">If</span> <span class="hljs-attr">a</span> <span class="hljs-attr">post</span> <span class="hljs-attr">has</span> <span class="hljs-attr">an</span> <span class="hljs-attr">image</span>, <span class="hljs-attr">use</span> <span class="hljs-attr">the</span> <span class="hljs-attr">larger</span> <span class="hljs-attr">card.</span> 
                // <span class="hljs-attr">Otherwise</span> <span class="hljs-attr">the</span> <span class="hljs-attr">default</span> <span class="hljs-attr">image</span> <span class="hljs-attr">is</span> <span class="hljs-attr">just</span> 
                // <span class="hljs-attr">a</span> <span class="hljs-attr">small</span> <span class="hljs-attr">logo</span>, <span class="hljs-attr">so</span> <span class="hljs-attr">use</span> <span class="hljs-attr">the</span> <span class="hljs-attr">smaller</span> <span class="hljs-attr">card.</span>
                { <span class="hljs-attr">property:</span> `<span class="hljs-attr">twitter:card</span>`, <span class="hljs-attr">content:</span> <span class="hljs-attr">imageUrl</span> ? `<span class="hljs-attr">summary_large_image</span>` <span class="hljs-attr">:</span> `<span class="hljs-attr">summary</span>`, }, 

                // <span class="hljs-attr">Add</span> <span class="hljs-attr">image</span> <span class="hljs-attr">alt</span> <span class="hljs-attr">text</span>
                // <span class="hljs-attr">Falls</span> <span class="hljs-attr">back</span> <span class="hljs-attr">to</span> <span class="hljs-attr">default</span> <span class="hljs-attr">which</span> <span class="hljs-attr">describes</span> <span class="hljs-attr">the</span> <span class="hljs-attr">site</span> <span class="hljs-attr">logo</span>
                { <span class="hljs-attr">property:</span> `<span class="hljs-attr">twitter:image:alt</span>`, <span class="hljs-attr">content:</span> <span class="hljs-attr">imageAlt</span> || "<span class="hljs-attr">davidagood.com</span> <span class="hljs-attr">logo</span>", }, 
                // <span class="hljs-attr">...</span></span></span>
</code></pre>
<h2 id="heading-how-to-source-default-image-using-graphql">How to Source Default Image using GraphQL</h2>
<p>This is where Gatsby's filesystem and image processing capabilities come into play. Below is the <code>useStaticQuery</code> call GraphQL query from the <code>SEO</code> component. I've added the <code>ogImageDefault</code> portion and the <code>siteUrl</code> which is needed for the <code>constructUrl</code> call shown above.</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/seo.js</span>
<span class="hljs-keyword">const</span> data = useStaticQuery(
    graphql<span class="hljs-string">`
      query {
        site {
          siteMetadata {
            title
            description
            social {
              twitter
            }
            # Add this
            siteUrl
          }
        }
        # Add this
        ogImageDefault: file(relativePath: {eq: "icon.png"}) { 
          childImageSharp {
            fixed(height: 260, width: 260) {
              src
            }
          }
        }
      }
    `</span>,
);
</code></pre>
<h3 id="heading-graphql-file-and-image-processing-query-explained">GraphQL File and Image Processing Query Explained</h3>
<p>The top level node is <code>ogImageDefault</code>. This is a <a target="_blank" href="https://graphql.org/learn/queries/#aliases">GraphQL alias</a> for the <code>file</code> query which is applying a filter to find a file with relative path equal to <code>icon.png</code>. The name I've chosen, <code>ogImageDefault</code>, is completely arbitrary.</p>
<p>One key thing to understand here is what the <code>relativePath</code> is relative to. In other words, where is this file, <code>icon.png</code>? </p>
<p>Let me start by telling you the location of the file relative to the project root: <code>./content/assets/icon.png</code>. In the query, I haven't specified any relative path, just the filename. So how does Gatsby know where to find it? </p>
<p>Enter <code>[gatsby-source-filesystem](https://www.gatsbyjs.com/plugins/gatsby-source-filesystem/)</code>. If you look in <code>gatsby-config.js</code> you will see some config like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// gatsby-config.js </span>
<span class="hljs-built_in">module</span>.exports = { 
    <span class="hljs-comment">// siteMetadata: {...}, </span>
    <span class="hljs-attr">plugins</span>: [ 
        <span class="hljs-comment">// Other plugins omitted </span>
        { 
            <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-source-filesystem`</span>, 
            <span class="hljs-attr">options</span>: { 
                <span class="hljs-attr">path</span>: <span class="hljs-string">`<span class="hljs-subst">${__dirname}</span>/content/blog`</span>, 
                <span class="hljs-attr">name</span>: <span class="hljs-string">`blog`</span>, 
            }, 
        }, 
        { 
            <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-source-filesystem`</span>, 
            <span class="hljs-attr">options</span>: { 
                <span class="hljs-attr">path</span>: <span class="hljs-string">`<span class="hljs-subst">${__dirname}</span>/content/assets`</span>, 
                <span class="hljs-attr">name</span>: <span class="hljs-string">`assets`</span>, 
            }, 
        }, 
        <span class="hljs-comment">// ...</span>
</code></pre>
<p>What this is doing is registering these paths as "content roots" and giving them a name. So the name <code>blog</code> refers to <code>./content/blog</code> relative to the project root. And the name <code>assets</code> refers to <code>./content/assets</code> relative to the project root. You can use these names in queries by filtering on <code>sourceInstanceName</code>:</p>
<pre><code class="lang-graphql"><span class="hljs-comment"># http://localhost:8000/___graphql </span>
{ 
    allFile(<span class="hljs-symbol">filter:</span> {<span class="hljs-symbol">sourceInstanceName:</span> {<span class="hljs-symbol">eq:</span> <span class="hljs-string">"blog"</span>}}) { 
        edges { 
            node { 
                absolutePath 
                publicURL 
                sourceInstanceName 
            } 
        } 
    } 
}
</code></pre>
<p>The result of this query:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Result of allFiles query with sourceInstanceName filter </span>
{ 
    <span class="hljs-string">"data"</span>: { 
        <span class="hljs-string">"allFile"</span>: { 
            <span class="hljs-string">"edges"</span>: [{ 
                <span class="hljs-string">"node"</span>: { 
                    <span class="hljs-string">"absolutePath"</span>: <span class="hljs-string">"/home/dgood/IdeaProjects/davidagood.com/content/blog/clean-code-and-architecture/index.md"</span>, 
                    <span class="hljs-string">"publicURL"</span>: <span class="hljs-string">"/static/40bb02d938c4faf7f977dd66c1a399d2/index.md"</span>, 
                    <span class="hljs-string">"sourceInstanceName"</span>: <span class="hljs-string">"blog"</span> 
                } 
            }, 
            <span class="hljs-comment">// additional results...</span>
</code></pre>
<p>So back to <code>ogImageDefault</code>: the <code>relativePath</code> we provided was just <code>icon.png</code>, but the file is actually located at <code>./content/assets/icon.png</code>. </p>
<p>Gatsby was able to resolve to the file because we configured a "content root" at <code>./content/assets</code>. We could have specified the <code>sourceInstanceName</code> to remove any ambiguity as to which "content root" this file is located in. </p>
<p>In fact, I'm not sure how Gatsby would behave if the same relative path existed in multiple "content roots". </p>
<p>This would be a good opportunity to dig into the Gatsby's source code to understand how this all works, but I'll leave that to you!</p>
<p>Next up: what is <code>childImageSharp</code>? "Child" refers to this being a child node of a <code>File</code> node. "Image" is just like it sounds. "Sharp" is referring to the <a target="_blank" href="https://github.com/lovell/sharp">Sharp</a> image processing tool and corresponding Gatsby plugin, <a target="_blank" href="https://www.gatsbyjs.com/plugins/gatsby-plugin-sharp/">gatsby-plugin-sharp</a>, which enables these image processing features.</p>
<p><code>fixed</code> means we want transform the image into an image of a fixed size. We specify the dimensions by passing parameters like this: <code>fixed(height: 260, width: 260)</code>. There are a few alternatives to <code>fixed</code> which we could use, one of which we will see below.</p>
<p>Finally, we only need the <code>src</code> property for the purposes of the Open Graph image meta tag.</p>
<h2 id="heading-how-to-source-post-specific-image-properties-using-graphql">How to Source Post-Specific Image Properties using GraphQL</h2>
<p>Following from above, we must update the <code>BlogPostTemplate</code> component to pass the <code>imageUrl</code> and <code>imageAlt</code> props to the <code>SEO</code> component. Again, we use the <code>constructUrl</code> utility to convert the relative path, <code>src</code>, into a URL. I explain the origin of these props' values below.</p>
<pre><code class="lang-js"><span class="hljs-comment">// util.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> constructUrl = <span class="hljs-function">(<span class="hljs-params">baseUrl, path</span>) =&gt;</span>
  (!baseUrl || !path) ? <span class="hljs-literal">null</span> : <span class="hljs-string">`<span class="hljs-subst">${baseUrl}</span><span class="hljs-subst">${path}</span>`</span>;

<span class="hljs-comment">// src/templates/blog-post.js</span>
<span class="hljs-keyword">const</span> BlogPostTemplate = <span class="hljs-function">(<span class="hljs-params">{ data, pageContext, location }</span>) =&gt;</span> { 
    <span class="hljs-comment">// Details omitted for brevity</span>
    <span class="hljs-keyword">return</span> ( 
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span> <span class="hljs-attr">location</span>=<span class="hljs-string">{location}</span> <span class="hljs-attr">title</span>=<span class="hljs-string">{data.site.siteMetadata.title}</span>&gt;</span> 
            <span class="hljs-tag">&lt;<span class="hljs-name">SEO</span> 
                <span class="hljs-attr">title</span>=<span class="hljs-string">{data.markdownRemark.frontmatter.title}</span> 
                <span class="hljs-attr">description</span>=<span class="hljs-string">{data.markdownRemark.frontmatter.description</span> || <span class="hljs-attr">data.markdownRemark.excerpt</span>} 
                <span class="hljs-attr">imageUrl</span>=<span class="hljs-string">{</span>
                    <span class="hljs-attr">constructUrl</span>(
                        <span class="hljs-attr">data.site.siteMetadata.siteUrl</span>, <span class="hljs-attr">data.markdownRemark.frontmatter.image</span>?<span class="hljs-attr">.childImageSharp</span>?<span class="hljs-attr">.fixed</span>?<span class="hljs-attr">.src</span>
                )} 
                <span class="hljs-attr">imageAlt</span>=<span class="hljs-string">{data.markdownRemark.frontmatter.imageAlt}</span> /&gt;</span>
        // ...</span>
</code></pre>
<p>Sourcing the image alt text is straightforward: we add <code>imageAlt</code> as a property to the <code>frontmatter</code> portion of our <code>BlogPostTemplate</code> component's GraphQL query. This query is exported as a GraphQL tagged template. </p>
<p>The name of the exported constant is arbitrary. In my case it's <code>const pageQuery</code>. </p>
<p>This query gets executed for us by Gatsby, and the results are passed to the <code>BlogPostTemplate</code> component in the <code>data</code> prop. </p>
<p>This is explained in the Gatsby docs here: <a target="_blank" href="https://www.gatsbyjs.com/docs/how-to/querying-data/page-query/">Querying Data in Pages with GraphQL</a>.</p>
<p>In order to source the actual image, we use <code>childImageSharp</code> again but in a slightly different way than we saw above:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/templates/blog-post.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> pageQuery = graphql<span class="hljs-string">`
    query BlogPostBySlug($slug: String!) {
      site {
        siteMetadata {
          title
          siteUrl
        }
      }
      markdownRemark(fields: {slug: {eq: $slug}}) {
        id
        excerpt(pruneLength: 160)
        html
        frontmatter {
          title
          date(formatString: "MMMM DD, YYYY")
          description
          # Add this
          image {
            childImageSharp {
              fixed(height: 600, width: 1200) {
                src
              }
              fluid(maxWidth: 700, maxHeight: 500) {
                ...GatsbyImageSharpFluid
              }
            }
          }
          # Add these
          imageAlt
          imageTitleHtml
        }
      }
    }
`</span>;
</code></pre>
<p>Here, <code>image</code> must match the name of the property we intend to set in the post's frontmatter. And the value of this property must be a path to a file <strong>relative to the post markdown file</strong>. </p>
<p>This is similar to what we did above using a GraphQL alias and the <code>file</code> query, but here it's implicit and being handled behind the scenes by Gatsby.</p>
<p>We specify the dimensions in the parameters to the <code>fixed</code> field. When choosing the dimensions, make sure any image you use is at least as big as the dimensions you specify here, and use these guideline from the <a target="_blank" href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary-card-with-large-image#reference">docs</a>:</p>
<blockquote>
<p>Images for this Card support an aspect ratio of 2:1 with minimum dimensions of 300x157 or maximum of 4096x4096 pixels</p>
</blockquote>
<p>We have also added the <code>fluid</code> property and a <a target="_blank" href="https://graphql.org/learn/queries/#fragments">GraphQL fragment</a>, <code>...GatsbyImageSharpFluid</code>, which retrieves all of the properties available on this node without having to enumerate them one by one. </p>
<p>The Gatsby Image component is <a target="_blank" href="https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-image/#images-that-stretch-across-a-fluid-container">designed to be used this way</a> in order to provide a responsive image experience using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">HTML's native responsive image capabilities</a>.</p>
<h2 id="heading-how-to-add-a-header-image-to-your-blog-post-template">How to Add a Header Image to your Blog Post Template</h2>
<p>With the GraphQL query updated and the results being passed to our component by Gatsby, we're ready to add the Gatsby Image import and the JSX for the header image and caption:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/templates/blog-post.js</span>
<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"gatsby-image"</span>;

<span class="hljs-comment">// Details omitted for brevity</span>

{data.markdownRemark.frontmatter.image?.childImageSharp?.fluid &amp;&amp;
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Image</span>
            <span class="hljs-attr">fluid</span>=<span class="hljs-string">{data.markdownRemark.frontmatter.image.childImageSharp.fluid}</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">{data.markdownRemark.frontmatter.imageAlt}</span> 
        /&gt;</span>
        <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">textAlign:</span> "<span class="hljs-attr">center</span>",
                <span class="hljs-attr">fontSize:</span> "<span class="hljs-attr">14px</span>",
                <span class="hljs-attr">lineHeight:</span> "<span class="hljs-attr">28px</span>",
            }}
            <span class="hljs-attr">dangerouslySetInnerHTML</span>=<span class="hljs-string">{{</span> 
                <span class="hljs-attr">__html:</span> <span class="hljs-attr">data.markdownRemark.frontmatter.imageTitleHtml</span> 
            }} 
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
}
</code></pre>
<p>If the <code>image</code> or <code>imageAlt</code> properties are not set in a post's frontmatter, it won't cause any issues. Those properties will just be <code>null</code> in the post's <code>data</code> prop, for example <code>data.markdownRemark.frontmatter.image</code> and <code>data.markdownRemark.frontmatter.imageAlt</code>. </p>
<p>For that reason, I've used <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining">optional chaining</a> when passing the <code>imageUrl</code> prop to the <code>SEO</code> component: <code>data.markdownRemark.frontmatter.image?.childImageSharp?.fixed?.src</code> and when optionally adding the header image component tree: <code>data.markdownRemark.frontmatter.image?.childImageSharp?.fluid</code>.</p>
<h2 id="heading-how-to-add-new-properties-to-a-posts-frontmatter">How to Add New Properties to a Post's Frontmatter</h2>
<p>Now all that's left is to add the actual image file, typically in the same directory as the markdown where we want to use it. Then we add the <code>image</code>, <code>imageAlt</code>, and <code>imageTitleHtml</code> properties to the post's frontmatter. </p>
<p>I've taken the suggested attribution HTML directly from <a target="_blank" href="https://unsplash.com/">Unsplash</a> and used it for the <code>imageTitleHtml</code>.</p>
<p>Remember: in this case, the image path is relative to the post markdown file.</p>
<pre><code class="lang-md">--- 
title: "Working with Heterogeneous Item Collections in the DynamoDB Enhanced Client for Java" 
date: "2020-12-07T01:51:34.815Z"
description: "Working with heterogeneous item collections with the Java SDKs can be tricky. Here we see how to handle 
them with the AWS SDK v2 for Java's Enhanced Client."
image: "./kevin-mueller-gGUiw8GNIFE-unsplash.jpg"
imageAlt: "Water droplets on black background"
imageTitleHtml: '<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span></span>Photo by <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://unsplash.com/@kevinmueller?utm_source=unsplash<span class="hljs-symbol">&amp;amp;</span>utm_medium=referral<span class="hljs-symbol">&amp;amp;</span>utm_content=creditCopyText"</span>&gt;</span></span>Kevin Mueller<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span> on <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://unsplash.com/?utm_source=unsplash<span class="hljs-symbol">&amp;amp;</span>utm_medium=referral<span class="hljs-symbol">&amp;amp;</span>utm_content=creditCopyText"</span>&gt;</span></span>Unsplash<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>'

--- 

// Markdown here...
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That's it – you did it! We covered quite a few concepts in this article. You should now be able to add header images to your blog posts and get nice Open Graph-based preview experiences on Twitter, Facebook, Google, WhatsApp, and more.</p>
<p>You can find the completed code on GitHub here:</p>
<ul>
<li><a target="_blank" href="https://github.com/helloworldless/davidagood.com/blob/55164811e2265de754940c8432c58c2bceec8e43/src/components/seo.js">SEO</a></li>
<li><a target="_blank" href="https://github.com/helloworldless/davidagood.com/blob/55164811e2265de754940c8432c58c2bceec8e43/src/templates/blog-post.js">BlogPostTemplate</a></li>
<li><a target="_blank" href="https://github.com/helloworldless/davidagood.com/blob/55164811e2265de754940c8432c58c2bceec8e43/content/blog/dynamodb-enhanced-client-java-heterogeneous-item-collections/index.md">Example post markdown</a></li>
</ul>
<p>Once you've implemented this and deployed it, you can use the <a target="_blank" href="https://cards-dev.twitter.com/validator">Twitter Card Validator</a> to test the behavior before actually tweeting a link.</p>
<p>Coincidentally, I did experience some issues with cards not being displayed in tweets even though the Validator showed that they were working. </p>
<p>In one case, I tweeted a link in a reply, and there was no card at all—just the raw link. The next day, I tweeted the same link, and this time the card worked fine! </p>
<p>In another case, I was looking at my Twitter Profile page, and several of my tweets had the cards but the image was not being displayed. So I opened a Chrome Incognito window, and in that window the images were displayed as expected.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create an Image Gallery Using Gatsby and Cloudinary ]]>
                </title>
                <description>
                    <![CDATA[ According to Mathias Biilmann, the CEO & Co-founder of Netlify,"The JAMstack is a modern web development architecture based on client-side JavaScript, reusable APIs, and prebuilt Markup." The key aspects of a JAMstack application are the following: ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-an-image-gallery-gatsby-and-cloudinary/</link>
                <guid isPermaLink="false">66bdffd60b4523e3b8b9909e</guid>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JAMstack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tapas Adhikary ]]>
                </dc:creator>
                <pubDate>Mon, 12 Oct 2020 20:36:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/10/cover_1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>According to Mathias Biilmann, the CEO &amp; Co-founder of Netlify,"The <code>JAMstack</code> is a modern web development architecture based on client-side JavaScript, reusable APIs, and prebuilt Markup."</p>
<p>The key aspects of a <a target="_blank" href="https://blog.greenroots.info/jamstack-for-all-an-introduction-cke2fxc5800jvabs15lhn4a9x">JAMstack</a> application are the following:</p>
<ul>
<li>The entire app runs on a <strong>CDN (or ADN)</strong>. CDN stands for Content Delivery Network and an ADN is an Application Delivery Network.</li>
<li>Everything is in <strong>Git</strong>.</li>
<li><strong>Automated builds</strong> run with a workflow when developers push the code.</li>
<li>There's <strong>Automatic deployment</strong> of the pre-built markup to the CDN/ADN.</li>
<li>It's practically <strong>Serverless</strong>. To put it more clearly, we do not maintain any server-side applications but rather make use of already existing services (like email, media, database, search, and so on).</li>
</ul>
<p>And here's a fun fact: Many of the features that once required a custom back-end can now be done entirely on the front-end.</p>
<p>In this article, we will learn how to build a <a target="_blank" href="https://blog.greenroots.info/jamstack-for-all-an-introduction-cke2fxc5800jvabs15lhn4a9x">JAMstack</a> application that has:</p>
<ul>
<li>an API service to serve media files,</li>
<li>and a <a target="_blank" href="https://blog.greenroots.info/what-is-a-static-site-generator-and-how-to-select-one-cke9xtx5g006p58s11dzg2j16">Static Site Generator(SSG)</a> to create the pre-built markup.</li>
</ul>
<p>And finally we'll deploy it over a CDN.</p>
<h2 id="heading-so-what-are-we-building-today"><strong>So, what are we building today?</strong></h2>
<p>We will build an image gallery. I love movies, so I have decided to create an image gallery of my favorite actors. I have a fun name for it to: <code>imaginary</code>.</p>
<p>We will use a media management service called <a target="_blank" href="https://cloudinary.com/">Cloudinary</a> to host and manage our images. It also provides developer APIs to upload and fetch media such as images, videos, and so on</p>
<p>We will use a framework called <a target="_blank" href="https://www.gatsbyjs.com/">Gatsby</a> to build the front-end of the image gallery. <code>Gatsby</code> is a React-based open-source framework for creating websites and apps.</p>
<p>Finally, we will learn how to deploy the app to a CDN with an automatic build and deploy process. We will use <a target="_blank" href="https://www.netlify.com/">Netlify CDN</a> for this.</p>
<p>At the end of the article, our <code>imaginary</code> app will look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/snap.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Image Gallery app - Final Look</em></p>
<h2 id="heading-tldr"><strong>TL;DR</strong></h2>
<p>We will learn things with a step-by-step approach in this article. If you want to jump into the source code or demonstration sooner, here are links to them.</p>
<ul>
<li>You can access the image gallery demo from here: <a target="_blank" href="http://imaginary.netlify.app/">http://imaginary.netlify.app/</a></li>
<li>All the source code used in this article is in my GitHub repo. Feel free to follow it, as I keep updating the source code frequently. If you liked the work, please support it with a star. <a target="_blank" href="https://github.com/atapas/imaginary/tree/1-freecodecamp-poc">https://github.com/atapas/imaginary/tree/1-freecodecamp-poc</a></li>
</ul>
<p>Now let's get started.</p>
<h2 id="heading-how-to-set-up-cloudinary"><strong>How to Set Up Cloudinary</strong></h2>
<p>First, <a target="_blank" href="https://cloudinary.com/users/register/free">create an account</a> with <code>Cloudinary</code>. A free plan is more than enough for our image gallery application.</p>
<p>Login using your account credentials to get to the <code>Cloudinary</code> dashboard. Please note down the <code>Cloud name</code>, <code>API Key</code> and, <code>API Secret</code> as we'll need them in our application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/cloudinary.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, upload the images of your choice to use them in the <code>image gallery</code>. You can create a folder and call it whatever you want. In my case, I have named it <code>artists</code> and uploaded the images into it.</p>
<p>Note that we'll use this folder name later when we integrate <code>Cloudinary</code> with <code>Gatsby</code>.</p>
<p>Please select each of the images and add a <code>Title</code> and <code>Description</code>. We will use these two bits of meta information as image captions and alt-text, respectively, in our image gallery.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/cludinary_photo_management.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>That's all. Please do not share the API secret and the key with anyone. Let's now go through the required setup for <code>Gatsby</code>.</p>
<h2 id="heading-how-to-set-up-gatsby"><strong>How to Set Up Gatsby</strong></h2>
<p>A <code>gatsby</code>-based project needs <code>node.js</code> to work. Make sure you have Node.js installed. You can download and install <code>node.js</code> from <a target="_blank" href="https://nodejs.org/en/download/">here</a>.</p>
<p>A successful install will show the Node version using this command:</p>
<pre><code class="lang-shell">node -v
</code></pre>
<h3 id="heading-create-a-project-directory"><strong>Create a project directory</strong></h3>
<p>Create a project directory to organize the source code. Let's create a directory with the name <code>imaginary</code>.</p>
<pre><code class="lang-shell">mkdir imaginary

cd imaginary
</code></pre>
<h3 id="heading-initialize-the-project"><strong>Initialize the project</strong></h3>
<p>There are plenty of <a target="_blank" href="https://www.gatsbyjs.com/starters/">starter projects</a> to create a <code>gatsby</code> app. While the starter projects simplify many complexities, at the same time they may be a bit overwhelming for a simple app like ours.</p>
<p>Keeping that in mind, we will initialize a simple <code>gatsby</code> project by ourselves.</p>
<p>Open a command prompt and type the following command to initialize a Node.js supported project:</p>
<pre><code class="lang-shell">npm init -y
</code></pre>
<h3 id="heading-gatsby-install-and-initial-setup"><strong>Gatsby: Install and initial setup</strong></h3>
<p>Install the <code>gatsby-cli</code> tool globally. This tool will help us work with the Gatsby environment.</p>
<pre><code class="lang-shell"> npm install -g gatsby-cli
</code></pre>
<p>Install the Gatsby, React, and ReactDOM dependencies. You can either use the <code>npm install</code> command as above or the <code>yarn add</code> command if you have yarn installed.</p>
<pre><code class="lang-shell"> yarn add gatsby react react-dom
</code></pre>
<p>Let's also add a <code>.gitignore</code> file with the following content:</p>
<pre><code class="lang-shell">.cache
public
node_modules
*.env
</code></pre>
<p>You can optionally create a README.md and LICENSE file. At this stage, our project should have these folders and files:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-14.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><code>Gatsby</code> projects need a special configuration file called <code>gatsby-config.js</code>. At this time, we will need an empty file. Create a file named <code>gatsby-config.js</code> with the following content:</p>
<pre><code class="lang-js"> <span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-comment">// keep it empty    </span>
 }
</code></pre>
<p>Now it is time to create our first page with <code>Gatsby</code>. Create a folder named <code>src</code> at the root of the project folder. Create a sub-folder named <code>pages</code> under <code>src</code>. Create a file named <code>index.js</code> under <code>src/pages</code> with the following content:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;    

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> () =&gt; {    
   <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;&gt;</span>    
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Images to load here...<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;/&gt;</span></span>        
    )    
  }
</code></pre>
<p>At this stage, our project files and folders should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-16.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-lets-run-it"><strong>Let's run it</strong></h3>
<p>Try the following command in the command prompt to run the app locally:</p>
<pre><code class="lang-shell">gatsby develop
</code></pre>
<p>By default, a <code>gatsby</code> app runs on the <code>8000</code> port number. Open your favorite browser and access the app @ <a target="_blank" href="http://localhost:8000/">http://localhost:8000</a>.</p>
<p>You should see it running like you see in the screen-shot below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-15.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>That's all. Now, we've gotten both <code>Cloudinary</code> and <code>Gatsby</code> successfully set up. It's time to bring them together.</p>
<h2 id="heading-cloudinary-amp-gatsby-the-getting-together-story"><strong>Cloudinary &amp; Gatsby, the getting together story</strong></h2>
<p>The <code>Gatsby</code> framework has huge community support and there are many plug-ins to satisfy critical needs. In early 2020, the <code>Cloudinary</code> team <a target="_blank" href="https://cloudinary.com/blog/introducing_cloudinary_s_gatsby_plugins">introduced two super cool Gatsby plugins</a> to the dev community:</p>
<ul>
<li><a target="_blank" href="https://www.npmjs.com/package/gatsby-source-cloudinary"><code>Gatsby-Source-Cloudinary</code></a> - Helps in fetching the stored images at the build time from Cloudinary to a Gatsby app/site.</li>
<li><a target="_blank" href="https://www.npmjs.com/package/gatsby-transformer-cloudinary"><code>Gatsby-Transformer-Cloudinary</code></a> - Helps in uploading the local images from a Gatsby app/site to Cloudinary.</li>
</ul>
<p>As we are only interested in fetching the images to the image gallery here, we will just install the <code>gatsby-source-cloudinary</code> plug-in.</p>
<p>We will also install a utility package called <code>dotenv</code> to handle the environment variables (Secret Key, API key, and so on) locally.</p>
<p>Let's install them. Open a command prompt and use the following command:</p>
<pre><code class="lang-shell">yarn add dotenv gatsby-source-cloudinary
</code></pre>
<p>Now, follow these steps to fetch the images into our app.</p>
<ol>
<li>Create a <code>.env</code> file at the root of the project folder and add the following content to it. Please note that the values in the <code>key-value</code> pairs are available in the <code>Cloudinary</code> dashboard as we have seen before.</li>
</ol>
<pre><code class="lang-shell">CLOUDINARY_CLOUD_NAME=&lt;YOUR_CLOUDINARY_NAME&gt;
CLOUDINARY_API_KEY=&lt;YOUR_CLOUDINARY_API_KEY&gt;
CLOUDINARY_API_SECRET=&lt;YOUR_CLOUDINARY_API_SECRET&gt;
</code></pre>
<ol start="2">
<li>Edit the <code>gatby-config.js</code> file to add the following content:</li>
</ol>
<pre><code class="lang-js">
<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();

<span class="hljs-built_in">module</span>.exports = {

    <span class="hljs-attr">plugins</span>:[
        {
            <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-source-cloudinary`</span>,
            <span class="hljs-attr">options</span>: {
              <span class="hljs-attr">cloudName</span>: process.env.CLOUDINARY_CLOUD_NAME,
              <span class="hljs-attr">apiKey</span>: process.env.CLOUDINARY_API_KEY,
              <span class="hljs-attr">apiSecret</span>: process.env.CLOUDINARY_API_SECRET,
              <span class="hljs-attr">resourceType</span>: <span class="hljs-string">`image`</span>,
              <span class="hljs-attr">prefix</span>: <span class="hljs-string">`artists/`</span> ,
              <span class="hljs-attr">context</span>: <span class="hljs-literal">true</span>,
              <span class="hljs-attr">tags</span>: <span class="hljs-literal">true</span>,
              <span class="hljs-attr">maxResults</span>: <span class="hljs-number">50</span>
            }
          }
    ]

}
</code></pre>
<p>There are a few things going on here. We are telling <code>gatsby</code> to use the <code>gatsby-source-cloudinary</code> plug-in with a few parameters.</p>
<p>The parameters <code>cloudName</code>, <code>apiKey</code> and <code>apiSecret</code> are fetched from the <code>.env</code> file using the <code>dotenv</code> package. We also specify the <code>resourceType</code> value as <code>image</code> as we are not interested in fetching other types of media.</p>
<p>The <code>prefix</code> parameter value should be the same as the image folder name in Cloudinary.</p>
<p>We specify <code>context</code> and <code>tags</code> as true to fetch the contextual metadata and tag information of an image. We also set the <code>maxResults</code> as 50 so that we do not get constrained with the default value of 10.</p>
<ol start="3">
<li>Next, create a folder called <code>components</code> under <code>src</code> and create a file called <code>Gallery.js</code> inside it. <code>Gatsby</code> uses <code>graphQL</code> to query the data from various sources like markdown, JSON, and Excel.  </li>
</ol>
<p>In our case, we will use <code>Cludinary</code> as a source to query the images using the  <code>gatsby-source-cloudinary</code> plug-in configured already.</p>
<ol start="4">
<li>Now edit the <code>Gallery.js</code> file and add the following content:</li>
</ol>
<pre><code class="lang-js">
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {useStaticQuery, graphql} <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby'</span>;

<span class="hljs-keyword">const</span> Gallery = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> data = useStaticQuery(
        graphql<span class="hljs-string">`
        query CloudinaryImage {
            allCloudinaryMedia {
            edges {
                node {
                    secure_url
                    context {
                        custom {
                            alt
                            caption
                        }
                    }
                    resource_type
                }
            }
            }
        }`</span>
    );
    <span class="hljs-keyword">const</span> images = data.allCloudinaryMedia.edges;
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
            {images.map((image, index) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">figure</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"wave"</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">index</span>}<span class="hljs-attr">-image</span>`}&gt;</span>
                    <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">{image.node.context.custom.alt}</span> &gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">img</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">figcaption</span>&gt;</span>{image.node.context.custom.caption}<span class="hljs-tag">&lt;/<span class="hljs-name">figcaption</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span>
                ))
            }
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Gallery;
</code></pre>
<p>As we see above, we use a <code>graphQL</code> query to fetch the source image paths and the context information. We use the information to loop through and add the images with a caption.</p>
<ol start="5">
<li>The next step is to edit the <code>index.js</code> file to import the <code>Gallery.js</code> file and use it.</li>
</ol>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Gallery <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Gallery'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> () =&gt; {    
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;&gt;</span>    
        <span class="hljs-tag">&lt;<span class="hljs-name">Gallery</span> /&gt;</span>
      <span class="hljs-tag">&lt;/&gt;</span></span>        
    )    
}
</code></pre>
<p>If you are running the <code>gatsby develop</code> already, please stop it and run it again. Access the app again in the browser. You should see the app with all the images fetched from <code>Cloudinary</code>.</p>
<p>But wait, it doesn't look as elegant as was promised. We have to do some <code>css</code> work here.</p>
<p>Create a file called <code>gallery.css</code> under the folder <code>src\components</code> and add the following content in it:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#000000</span> <span class="hljs-built_in">url</span>(<span class="hljs-string">"https://res.cloudinary.com/atapas/image/upload/v1602214656/misc/6284_n48wtw.jpg"</span>) repeat-x center top;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#FFFFFF</span>;
}

<span class="hljs-selector-class">.container</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">55px</span>;
}

<span class="hljs-selector-class">.wave</span> {
    <span class="hljs-attribute">float</span>: left;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">animation</span>: wave 
               ease-in-out 
               <span class="hljs-number">1s</span> 
               infinite 
               alternate;
    <span class="hljs-attribute">transform-origin</span>: center -<span class="hljs-number">20px</span>;
}

<span class="hljs-selector-class">.wave</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">animation-play-state</span>: paused;
}

<span class="hljs-selector-class">.wave</span> <span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#f8f8f8</span>;
    <span class="hljs-attribute">display</span>: block;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">200px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">250px</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#000</span>;
}

<span class="hljs-selector-class">.wave</span> <span class="hljs-selector-tag">figcaption</span> {
    <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-class">.wave</span><span class="hljs-selector-pseudo">:after</span>{
    <span class="hljs-attribute">content</span>: <span class="hljs-string">''</span>;
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">20px</span>; 
    <span class="hljs-attribute">height</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1.5px</span> solid <span class="hljs-number">#ffffff</span>;
    <span class="hljs-attribute">top</span>: -<span class="hljs-number">10px</span>; 
    <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">z-index</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">border-bottom</span>: none;
    <span class="hljs-attribute">border-right</span>: none;
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">45deg</span>);
}

<span class="hljs-selector-class">.wave</span><span class="hljs-selector-pseudo">:before</span>{
    <span class="hljs-attribute">content</span>: <span class="hljs-string">''</span>;
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">top</span>: -<span class="hljs-number">23px</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">display</span>: block;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">44px</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">47px</span>;
    <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(https://res.cloudinary.com/atapas/image/upload/v1602212639/misc/screw-head_oglfcu.png);
    <span class="hljs-attribute">background-size</span>: <span class="hljs-number">20px</span> <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">background-repeat</span>: no-repeat;
    <span class="hljs-attribute">z-index</span>: <span class="hljs-number">16</span>;
}

<span class="hljs-keyword">@keyframes</span> wave {
    0% { <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">3deg</span>); }
    100% { <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(-<span class="hljs-number">3deg</span>); }
}
</code></pre>
<ol start="6">
<li>Import the <code>gallery.css</code> file into the <code>Gallery.js</code> file as</li>
</ol>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">'./gallery.css'</span>;
</code></pre>
<p>That's all. You should see the app looking much better than before as if the images are hanging from a wall with animation in them.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/snap-1.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-lets-deploy-the-app"><strong>Let's Deploy the App</strong></h2>
<p>The last step is to deploy the app publicly so that we show it off.</p>
<p>First, create a repository in GitHub and push the source code. Then create an account with <a target="_blank" href="https://www.netlify.com/">Netlify</a> to login.</p>
<p>Follow these simple steps to deploy your app to the Netlify CDN with built-in CI/CD support.</p>
<ul>
<li>Create a new site from Git</li>
<li>Authenticate to your <code>Github</code> account and select the image gallery project. In my case, the project name is <code>imaginary</code>.</li>
<li>In the next step, provide information about the build command as <code>gatsby build</code> and publish the directory as <code>public/</code>.</li>
<li>Next click on the <code>Site settings</code> to tell <code>netlify</code> about the <code>Cloudinary</code> cloud name, secret key, API key, and so on.</li>
<li>Browse to the <code>Environment</code> option and click on the <code>Edit variables</code> button.</li>
<li>Add three variables as shown below with the values shown in your <code>Cloudinary</code> dashboard.</li>
<li>Browse to the <code>Deploys</code> option and trigger a fresh deploy.</li>
<li>You can change the site name to something that meets your needs. In my case, it is <a target="_blank" href="https://imaginary.netlify.app/">https://imaginary.netlify.app/</a>:</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/1.netlify.png" alt="Image" width="600" height="400" loading="lazy">
<em>New site from Git</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/2.netlify.png" alt="Image" width="600" height="400" loading="lazy">
<em>Create a new site</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/3.netlify.png" alt="Image" width="600" height="400" loading="lazy">
<em>Parameters for the site</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/4.netlify.png" alt="Image" width="600" height="400" loading="lazy">
<em>Site settings</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/5.netlify.png" alt="Image" width="600" height="400" loading="lazy">
<em>Add environment variables</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/6.netlify.png" alt="Image" width="600" height="400" loading="lazy">
<em>Add all of them</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/7.netlify.png" alt="Image" width="600" height="400" loading="lazy">
<em>Trigger a fresh deploy</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/8.netlify.png" alt="Image" width="600" height="400" loading="lazy">
<em>Optionally to change the site name</em></p>
<p>Now we're done. We have the app up and running publicly.</p>
<h2 id="heading-in-summary"><strong>In Summary</strong></h2>
<p>Hope you enjoyed reading this article. In a future article we will see how to use the other gatsby-cloudinary plug-in to upload an image to the <code>Cloudinary</code> server to add to our image gallery.</p>
<p>You may also like these articles:</p>
<ul>
<li><a target="_blank" href="https://blog.greenroots.info/i-made-a-photo-gallery-with-css-animation-heres-what-i-learned-ckfzbk6v903ea2xs14l1942f7">I made a photo gallery with CSS animation. Here’s what I learned.</a></li>
<li><a target="_blank" href="https://blog.greenroots.info/jamstack-for-all-an-introduction-cke2fxc5800jvabs15lhn4a9x">JAMstack for All: An Introduction</a></li>
<li><a target="_blank" href="https://blog.greenroots.info/what-is-a-static-site-generator-and-how-to-select-one-cke9xtx5g006p58s11dzg2j16">What is a Static Site Generator and how to select one?</a></li>
</ul>
<p>If this article was useful, please share it so others can read it as well. You can @ me on Twitter (<a target="_blank" href="https://twitter.com/tapasadhikary">@tapasadhikary</a>) with comments, or feel free to follow me.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Blog with Gatsby and Netlify CMS – A Complete Guide ]]>
                </title>
                <description>
                    <![CDATA[ By Mohammed Asker In this article, we are going to build a blog with Gatsby and Netlify CMS. You will learn how to install Gatsby on your computer and use it to quickly develop a super fast blog site. You are also going to learn how to add Netlify CM... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-blog-with-gatsby-and-netlify-cms/</link>
                <guid isPermaLink="false">66d46040d14641365a050921</guid>
                
                    <category>
                        <![CDATA[ blog ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 06 Oct 2020 15:16:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c984e740569d1a4ca1946.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Mohammed Asker</p>
<p>In this article, we are going to build a blog with Gatsby and Netlify CMS. You will learn how to install Gatsby on your computer and use it to quickly develop a super fast blog site.</p>
<p>You are also going to learn how to add Netlify CMS to your site by creating and configuring files, then connecting the CMS to your site through user authentication. </p>
<p>And finally, you'll learn how to access the CMS admin so that you can write your first blog post.</p>
<p>The complete code for this project can be found <a target="_blank" href="https://github.com/mohammedasker/foodblog">here</a>.</p>
<p>Here's a brief introduction to these tools.</p>
<h2 id="heading-what-is-gatsby">What is Gatsby?</h2>
<p><a target="_blank" href="https://www.gatsbyjs.com/">Gatsby</a> is a free and open-source framework based on React that helps you build fast websites and web apps. It is also a static site generator like Next.js, Hugo, and Jekyll.</p>
<p>It includes SEO (Search Engine Optimization), accessibility, and performance optimization from the get-go. This means that it will take you less time to build production-ready web apps than if you were building with React alone.</p>
<h2 id="heading-what-is-netlify-cms">What is Netlify CMS?</h2>
<p><a target="_blank" href="https://www.netlifycms.org/">Netlify CMS</a> is a CMS (Content Management System) for static site generators. It is built by the same people who made <a target="_blank" href="https://www.netlify.com/">Netlify</a>. It allows you to create and edit content as if it was WordPress, but it's a much simpler and user-friendly interface.</p>
<p>The main benefit of Netlify CMS is you don't have to create markdown files every time you want to write a post. This is useful for content writers who don't want to deal with code, text editors, repositories, and anything to do with tech - they can just focus on writing articles.</p>
<p>Alright, without any further ado, let's start building the blog!</p>
<p><strong>But before we get going, a quick heads up</strong>: This guide requires prior knowledge of JavaScript and React. If you are not comfortable with these tools yet, I've linked the resources at the end of the article to help you brush up on those skills. </p>
<p>Even if you're new to those technologies, I tried to make this guide as simple as I was able so you can follow along.</p>
<h2 id="heading-how-to-set-up-the-environment">How to set up the environment</h2>
<p>Before we can build Gatsby sites, we have to make sure that we have installed all the right software required for the blog.</p>
<h3 id="heading-install-nodejs">Install Node.js</h3>
<p>Node.js is an environment that can run JavaScript code outside of a web browser.</p>
<p>It is a tool that allows you to write backend server code instead of using other programming languages such as Python, Java, or PHP. Gatsby is built with Node.js and that's why we need to install it on our computer.</p>
<p>To install Node.js, go to the <a target="_blank" href="https://nodejs.org/en/download/">download page</a> and download it based on your operating system. </p>
<p>When you are done following the installation prompts, open the terminal and run <code>node -v</code> to check if it was installed correctly. Currently, the version should be 12.18.4 and above.</p>
<h3 id="heading-install-git">Install Git</h3>
<p>Git is a free and open-source distributed version control system that helps you manage your coding projects efficiently. </p>
<p>Gatsby starter uses Git to download and install its required files and that's why you need to have Git on your computer.</p>
<p>To install Git, follow the instructions based on your operating system:</p>
<ul>
<li><a target="_blank" href="https://www.atlassian.com/git/tutorials/install-git#mac-os-x">Install Git on Mac OS</a></li>
<li><a target="_blank" href="https://www.atlassian.com/git/tutorials/install-git#windows">Install Git on Windows</a></li>
<li><a target="_blank" href="https://www.atlassian.com/git/tutorials/install-git#linux">Install Git on Linux</a></li>
</ul>
<h3 id="heading-install-gatsby-cli">Install Gatsby CLI</h3>
<p>Gatsby CLI (Command Line Interface) is the tool that lets you build Gatsby-powered sites. By running this command, we can install any Gatsby sites and the plugins we want.</p>
<p>To install Gatsby CLI, open the terminal and run this command:</p>
<pre><code>npm install -g gatsby-cli
</code></pre><p>Once everything is set up successfully then we are ready to build our first Gatsby site.</p>
<h2 id="heading-how-to-build-a-gatsby-site">How to build a Gatsby site</h2>
<p>In this guide, we're going to use the default Gatsby starter theme, but you're free to choose any themes on the <a target="_blank" href="https://www.gatsbyjs.com/starters/?v=2">Gatsby starter library</a>. I personally use the <a target="_blank" href="https://github.com/LekoArts/gatsby-starter-minimal-blog">Lekoart theme</a> because the design is minimalist and beautiful, and it has a dark mode.</p>
<p>In the terminal, run this command to install the new Gatsby blog:</p>
<pre><code>gatsby <span class="hljs-keyword">new</span> foodblog https:<span class="hljs-comment">//github.com/gatsbyjs/gatsby-starter-blog</span>
</code></pre><p><strong>Note for Windows users</strong>: If you encounter "Error: Command failed with exit code 1: yarnpkg" while creating Gatsby site, see <a target="_blank" href="https://github.com/gatsbyjs/gatsby/issues/26804">this page</a> to troubleshoot it. You may have to clean up dependencies of old yarn installations or follow the Gatsby on Windows instructions.</p>
<p>What's does this command line mean exactly? Let me explain.</p>
<ul>
<li><strong>new</strong> - This is the command line that creates a new Gatsby project</li>
<li><strong>foodblog</strong> - This is the name of the project. You can name it whatever you want here. I named this project <em>foodblog</em> as an example only.</li>
<li><strong>The URL</strong> (<a target="_blank" href="https://github.com/gatsbyjs/gatsby-starter-blog">https://github.com/gatsbyjs/gatsby-starter-blog</a>) - This URL specified points to a code repository that holds the starter code you want to use. In other words, I picked the theme for the project.</li>
</ul>
<p>Once the installation is complete, we'll run the <code>cd foodblog</code> command which will take us to the location of our project file.</p>
<pre><code>cd foodblog
</code></pre><p>Then we'll run <code>gatsby develop</code> that will start running on the local machine. Depending on the specs of your computer, it will take a little while before it is fully started.</p>
<pre><code>gatsby develop
</code></pre><p>Open a new tab in your browser and go to <code>http://localhost:8000/</code>. You should now see your new Gatsby site!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--33-.png" alt="Image" width="600" height="400" loading="lazy">
<em>How a Gatsby starter blog homepage looks</em></p>
<p>Now that we've created the blog, the next step is to add Netlify CMS to make writing blog posts easier.</p>
<h2 id="heading-how-to-add-netlify-cms-to-your-site">How to add Netlify CMS to your site</h2>
<p>Adding Netlify CMS to your Gatsby site involves 4 major steps:</p>
<ul>
<li>app file structure, </li>
<li>configuration, </li>
<li>authentication, and </li>
<li>accessing the CMS.</li>
</ul>
<p>Let's tackle each of these stages one at a time.</p>
<h3 id="heading-how-to-set-up-the-apps-file-structure">How to set up the app's file structure</h3>
<p>This section deals with the file structure of your project. We are going to create files that will contain all Netlify CMS codes.</p>
<p>When you open your text editor, you will see a lot of files. You can read <a target="_blank" href="https://github.com/gatsbyjs/gatsby-starter-blog#-whats-inside">this article</a> if you are curious about what each of these files does.</p>
<pre><code>├── node_modules
├── src
├── <span class="hljs-keyword">static</span>
├── .gitignore
├── .prettierrc
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
├── gatsby-ssr.js
├── LICENSE
├── package-lock.json
├── package.json
└── README.md
</code></pre><p>Do not worry about all these files — we are going to use very few of them here. </p>
<p>What we are looking for is the <code>static</code> folder. This is the folder where it will form the main structure of the Netlify CMS. </p>
<p>If your project does not have <code>Static</code> folder, then create the folder at the root directory of your project.</p>
<p>Inside the <code>static</code> folder, create an <code>admin</code> folder. Inside this folder, create two files <code>index.html</code> and <code>config.yml</code>:</p>
<pre><code>admin
 ├ index.html
 └ config.yml
</code></pre><p>The first file, <code>index.html</code>, is the entry point to your CMS admin. This is where Netlify CMS lives. You don't need to do styling or anything as it is already done for you with the script tag in the example below:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!doctype <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Content Manager<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>The second file, <code>config.yml</code>, is the main core of the Netlify CMS. It's going to be a bit complicated as we are going to write backend code. We'll talk more about it in the configuration section.</p>
<h3 id="heading-how-to-configure-the-back-end">How to configure the back end</h3>
<p>In this guide, we are using Netlify for hosting and authentication and so the backend configuration process should be relatively straightforward. Add all the code snippets in this section to your <code>admin/config.yml</code> file.</p>
<p>We'll begin by adding the following codes:</p>
<pre><code class="lang-yml"><span class="hljs-attr">backend:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">git-gateway</span>
  <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span>
</code></pre>
<p><strong>Heads up</strong>: This code above works for GitHub and GitLab repositories. If you're using Bitbucket to host your repository, follow these <a target="_blank" href="https://www.netlifycms.org/docs/bitbucket-backend/">instructions</a> instead.</p>
<p>The code we just wrote specifies your backend protocol and your publication branch (which is branch: master). Git Gateway is an open-source API that acts as a proxy between authenticated users of your site and your site repository. I'll explain more what this does in the authentication section.</p>
<p>Next up, we will write <code>media_folder: "images/uploads"</code>. This will allow you to add media files like photos directly to your CMS. Then you won't need to use a text editor to manually add media and all that.</p>
<pre><code class="lang-yml"><span class="hljs-attr">media_folder:</span> <span class="hljs-string">"images/uploads"</span>
</code></pre>
<p>Make sure you created a folder called <code>images</code> in the <code>admin</code> folder. Inside the <code>images</code> folder, create an <code>uploads</code> folder as this is the place where you'll host your images.</p>
<h3 id="heading-configure-collections">Configure Collections</h3>
<p>The collections will define the structure for the different content types on your static site. As every site can be different, how you configure the collection's settings will differ from one site to another.</p>
<p>Let's just say your site has a blog, with the posts stored in <code>content/blog</code>, and files saved in a date-title format, like <code>2020-09-26-how-to-make-sandwiches-like-a-pro.md</code>. Each post begins with settings in the YAML-formatted front matter in this way:</p>
<pre><code class="lang-md">---
layout: blog
title: "How to make sandwiches like a pro"
date: 2020-09-26 11:59:59
<span class="hljs-section">thumbnail: "/images/sandwich.jpg"
---</span>

This is the post body where I write about how to make a sandwich so good that will impress Gordon Ramsay.
</code></pre>
<p>With this example above, this is how you will add <code>collections</code> settings to your Netlify CMS <code>config.yml</code> file:</p>
<pre><code class="lang-yml"><span class="hljs-attr">collections:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"blog"</span>
    <span class="hljs-attr">label:</span> <span class="hljs-string">"Blog"</span>
    <span class="hljs-attr">folder:</span> <span class="hljs-string">"content/blog"</span>
    <span class="hljs-attr">create:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">slug:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{year}}</span>-<span class="hljs-template-variable">{{month}}</span>-<span class="hljs-template-variable">{{day}}</span>-<span class="hljs-template-variable">{{slug}}</span>"</span>
    <span class="hljs-attr">fields:</span>
      <span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Layout"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"layout"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"hidden"</span>, <span class="hljs-attr">default:</span> <span class="hljs-string">"blog"</span>}
      <span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Title"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"title"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"string"</span>}
      <span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Publish Date"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"date"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"datetime"</span>}
      <span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Body"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"body"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"markdown"</span>}
</code></pre>
<p>Let's examine what each of these fields does:</p>
<ul>
<li><code>name</code>:  This one is used in routes like /admin/collections/blog</li>
<li><code>label</code>: This one is used in the UI (User Interface). When you are in the admin page, you will see a big word "Blog" on the top of the screen. That big word "Blog" is the label.</li>
<li><code>folder</code>: This one points to the file path where your blog posts are stored.</li>
<li><code>create</code>: This one lets the user (you or whoever has admin access) create new documents (blog posts in this case) in these collections.</li>
<li><code>slug</code>: This one is the template for filenames. <code>{{year}}</code>, <code>{{month}}</code>, and <code>{{day}}</code> which are pulled from the post's date field or save date. <code>{{slug}}</code> is a URL-safe version of the post's title. By default it is <code>{{slug}}</code>.</li>
</ul>
<p>The fields are where you can customize the content editor (the page where you write the blog post). You can add stuff like ratings (1-5), featured images, meta descriptions, and so on.</p>
<p>For instance, in this particular code, we add curly braces <code>{}</code>. Inside them we write <code>label</code> with the value "Publish Date" which will be the label in the editor UI. </p>
<p>The <code>name</code> field is the name of the field in the front matter and we name it "date" since the purpose of this field is to enter the date input.</p>
<p>And lastly, the widget determines how the UI style will look and the type of data we can enter. In this case, we wrote <code>"datetime"</code> which means we can only enter the date and time.</p>
<pre><code class="lang-yml"><span class="hljs-bullet">-</span> {<span class="hljs-attr">label:</span> <span class="hljs-string">"Publish Date"</span>, <span class="hljs-attr">name:</span> <span class="hljs-string">"date"</span>, <span class="hljs-attr">widget:</span> <span class="hljs-string">"datetime"</span>}
</code></pre>
<p>You can check the list right <a target="_blank" href="https://www.netlifycms.org/docs/widgets/">here</a> to see what exactly you can add. If you want, you can even create your own widgets, too. For the sake of brevity, we'll try to keep things simple here.</p>
<h3 id="heading-enable-authentication">Enable Authentication</h3>
<p>At this point, we are nearly done with the installation and configuration of Netlify CMS. Now it's time to connect your Gatsby site to the CMS by enabling authentication. </p>
<p>We'll add some HTML code and then activate some features from Netlify. After that, you are on the way to creating your first blog post.</p>
<p>We are going to need a way to connect a front end interface to the backend so that we can handle authentication. To do that, add this HTML script tag to two files:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://identity.netlify.com/v1/netlify-identity-widget.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>The first file to add this script tag is the <code>admin/index.html</code> file. Place it between the <code>&lt;head&gt;</code> tags. And the second file to add the tag is the <code>public/index.html</code> file. This one also goes in between the <code>&lt;head&gt;</code> tags.</p>
<p>When a user logs in with the Netlify Identity widget, an access token directs them to the site homepage. In order to complete the login and get back to the CMS, redirect the user back to the <code>/admin/</code> path.</p>
<p>To do this, add the following code before the closing <code>body</code> tag of the <code>public/index.html</code> file:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.netlifyIdentity) {
    <span class="hljs-built_in">window</span>.netlifyIdentity.on(<span class="hljs-string">"init"</span>, <span class="hljs-function"><span class="hljs-params">user</span> =&gt;</span> {
      <span class="hljs-keyword">if</span> (!user) {
        <span class="hljs-built_in">window</span>.netlifyIdentity.on(<span class="hljs-string">"login"</span>, <span class="hljs-function">() =&gt;</span> {
          <span class="hljs-built_in">document</span>.location.href = <span class="hljs-string">"/admin/"</span>;
        });
      }
    });
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>With this, we are now done writing the code and it's time to visit Netlify to activate authentication.</p>
<p>Before we move on, you should Git commit your changes and push them to the repository. Plus, you will have to deploy your site live so you can access the features in the Enable Identity and Git Gateway section.</p>
<h2 id="heading-deploy-your-site-live-with-netlify">Deploy your site live with Netlify</h2>
<p>We are going to use Netlify to deploy our Gatsby site live. The deployment process is pretty straightforward, quick, and most importantly, it comes with a free SSL (Secure Sockets Layer). This means your site is protected (you can tell by looking at the green lock on the browser search).</p>
<p>If you haven't signed up for the platform, you can do it <a target="_blank" href="https://app.netlify.com/signup?_ga=2.69477016.986166254.1601369549-1254573554.1571849986">right here</a>. When you've finished signing up, you can begin the deployment process by following these 3 steps.</p>
<ol>
<li>Click the "New site from Git" button to create a new site to be deployed. Choose the Git provider where your site is hosted. My site is hosted on GitHub so that's what I will choose.</li>
<li>Choose the repository you want to connect to Netlify. The name of my Gatsby site is "foodblog" but you have to pick your own project name.</li>
<li>The last one asks how you would like Netlify to adjust your builds and deploy your site. We are going to leave everything as it is and we will click the "Deploy site" button. This will begin deploying your site to live.</li>
</ol>
<p>Once the deployment is complete, you can visit your live site by clicking the green link that has been generated for you on the top left of the screen. Example: <code>https://random_characters.netlify.app</code>.</p>
<p>With this, the world can now view your site. You can replace the weird URL with your custom domain by reading this <a target="_blank" href="https://docs.netlify.com/domains-https/custom-domains/#definitions">documentation</a>.</p>
<h3 id="heading-how-to-enable-identity-and-git-gateway">How to enable Identity and Git Gateway</h3>
<p>Netlify's Identity and Git Gateway services help you manage CMS admin users for your site without needing them to have an account with your Git host (Like GitHub) or commit access on your repository.</p>
<p>To activate these services, head to your site dashboard on Netlify and follow these steps:</p>
<ol>
<li>Go to <strong>Settings</strong> &gt; <strong>Identity</strong>, and select <strong>Enable Identity</strong> service.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--34-.png" alt="Image" width="600" height="400" loading="lazy">
<em>In the Overview page of your site, click the "Settings" link.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--36-.png" alt="Image" width="600" height="400" loading="lazy">
<em>After clicking "Settings", scroll down the left sidebar and click the "Identity" link.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--37-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Click the "Enable Identity" button to activate the Identity feature.</em></p>
<ol start="2">
<li>Under <strong>Registration</strong> preferences, select <strong>Open</strong> or <strong>Invite only</strong>. Most of the time, you want only invited users to access your CMS. But if you are just experimenting, you can leave it open for convenience.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--38-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Under the Identity submenu, click the "Registration" link and you'll be taken to the registration preferences.</em></p>
<ol start="3">
<li>Scroll down to <strong>Services</strong> &gt; <strong>Git Gateway</strong>, and click <strong>Enable Git Gateway</strong>. This authenticates with your Git host and generates an API access token. </li>
</ol>
<p>In this case, we're leaving the <strong>Roles</strong> field blank, which means any logged-in user may access the CMS.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--40-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Under the Identity submenu, click the "Services" link.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--41-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Click the "Enable Git Gateway" button to activate the Git Gateway feature.</em></p>
<p>With this, your Gatsby site has been connected with Netlify CMS. All that is left is to access the CMS admin and write blog posts.</p>
<h2 id="heading-how-to-access-the-cms">How to access the CMS</h2>
<p>All right, you are now ready to write your first blog post!</p>
<p>There are two ways to access your CMS admin, depending on what accessing options you chose from the Identity.</p>
<p>If you selected <strong>Invite only</strong>, you can invite yourself and other users by clicking the Invite user button. Then an email message will be sent with an invitation link to login to your CMS admin. Click the confirmation link and you'll be taken to the login page.</p>
<p>Alternatively, if you selected <strong>Open</strong>, you can access your site's CMS directly at <code>yoursite.com/admin/</code>. You will be prompted to create a new account. When you submit it, a confirmation link will be sent to your email. Click the confirmation link to complete the signup process and you'll be taken to the CMS page.</p>
<p><strong>Note</strong>: If you cannot access your CMS admin after clicking the link from the email, the solution is to copy the link in the browser starting with <code>#confirmation_token=random_characters</code> and paste the link after the hashtag "#", like this: <code>https://yoursite.com/admin/#confirmation_token=random_characters</code>. This should fix the problem.</p>
<p>If everything goes well, you should see your site's admin dashboard:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/Screenshot--42-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Netlify CMS admin.</em></p>
<p>You can create your new post by clicking the "New post" button.</p>
<p>When you're ready to publish your post, you can click the "Publish Now" button to publish it immediately.</p>
<p>When you hit the publish button, the post file is automatically created. Then it will add to the changes with the commit message based on the name of the post along with the date and time of publishing. Finally, it will be pushed to the host repository, and from there your post will be seen live.</p>
<p>You can view the changes by looking at the commit message in your host repository.</p>
<p>After waiting for a few minutes, your new post should be live.</p>
<h3 id="heading-one-more-thing">One more thing</h3>
<p>The last thing to do is clean up the sample articles. To delete these posts, go to the blog files in your text editor and delete them one by one. Make sure you check your terminal when deleting them so that there will be no issues on your site.</p>
<p>Once all the sample posts are cleared out, commit these changes and push them to the repository.</p>
<p>And now, you are all done! You can now create your new posts from the comfortable CMS dashboard and share your stories to the world.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this guide you have learned how to:</p>
<ul>
<li>Create a Gatsby blog site</li>
<li>Added the Netlify CMS to your Gatsby site by creating and configuring files</li>
<li>Enable user authentication by activating Identity and Git Gateway</li>
<li>Access your site's CMS admin</li>
<li>Publish your first post powered by Gatsby and Netlify CMS</li>
</ul>
<p>By the end of this guide, you should now be able to enjoy writing blog posts with a fast website and simple content editor. And you probably don't have to touch the code unless it needs further customization.</p>
<p>There is still more to cover about Gatsby and Netlify CMS. One of the best ways to learn about them is to go through their documentation.</p>
<p>I hope you found this guide beneficial, and happy posting!</p>
<p><a target="_blank" href="https://www.mohammedasker.com/">Check out my blog</a> to learn more tips, tricks, and tutorials about web development.</p>
<p>Cover photo by <a target="_blank" href="https://unsplash.com/@neonbrand?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">NeONBRAND</a> on <a target="_blank" href="https://unsplash.com/s/photos/blogging?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a>.</p>
<h3 id="heading-resources-for-javascript-and-react">Resources for JavaScript and React</h3>
<p>Here are some resources that may help you to learn JavaScript and React:</p>
<p><strong>JavaScript</strong></p>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript">Official JavaScript Documentation</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/">freeCodeCamp</a></li>
<li><a target="_blank" href="https://www.w3schools.com/js/">W3Schools: JavaScript Tutorial</a></li>
<li><a target="_blank" href="https://javascript.info/">JavaScript.info</a></li>
</ul>
<p><strong>React</strong></p>
<ul>
<li><a target="_blank" href="https://reactjs.org/docs/getting-started.html">Official React Documentation</a></li>
<li><a target="_blank" href="https://www.udemy.com/course/complete-react-developer-zero-to-mastery/">Complete React Developer in 2020 (w/ Redux, Hooks, GraphQL)</a></li>
<li><a target="_blank" href="https://scrimba.com/learn/learnreact">Scrimba: Learn React for free</a></li>
<li><a target="_blank" href="https://flaviocopes.com/page/react-handbook/">Flavio Copes: The React Handbook</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Your Gatsby Site Can Get a Perfect Google Lighthouse Score After the Version 6 Update ]]>
                </title>
                <description>
                    <![CDATA[ By Erik Larsson Google Lighthouse is the free, go-to SEO tool for determining your website's overall health.  Enter your URL, and Google Lighthouse will score the performance metrics of your website, including page-speed, accessibility, best-practice... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/gatsby-perfect-lighthouse-score/</link>
                <guid isPermaLink="false">66d45e3db3016bf139028d29</guid>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Lighthouse ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SEO ]]>
                    </category>
                
                    <category>
                        <![CDATA[ web performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 28 Sep 2020 14:24:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/09/header-image.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Erik Larsson</p>
<p><a target="_blank" href="https://developers.google.com/web/tools/lighthouse">Google Lighthouse</a> is the free, go-to SEO tool for determining your website's overall health. </p>
<p>Enter your URL, and Google Lighthouse will score the <em>performance metrics</em> of your website, including page-speed, accessibility, best-practices and on-page SEO.</p>
<p>With the release of <strong>Lighthouse version 6</strong> earlier this year, many developers <strong>observed a drastic decrease</strong> in the performance metrics of their websites.</p>
<p>This especially came as a shock to the community of developers that use the popular React-based framework GatsbyJS, praised for its speed and performance.</p>
<p>As a GatsbyJS developer myself, I too, was puzzled. We had gotten used to seeing those sweet, green, 90+ performance ratings, without much effort on our part.</p>
<p>After the version 6 update, however, our site dropped into the orange, down to 60 points! And some us were even experiencing red, <a target="_blank" href="https://github.com/gatsbyjs/gatsby/issues/24332">sub 40 points scores</a>.</p>
<p>I want to share with you <strong>the steps I took in order to re-achieve a perfect 100 Google Lighthouse score</strong>.</p>
<h2 id="heading-step-1-the-quick-and-easy-fix-switching-to-preact">Step 1. The quick and easy fix: switching to Preact</h2>
<p>With the <a target="_blank" href="https://web.dev/lighthouse-whats-new-6.0/">release of Lighthouse version 6</a>, three new performance metrics were introduced: Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), and Total Blocking Time (TBT).</p>
<p>After scouring the <a target="_blank" href="https://github.com/gatsbyjs/gatsby">Gatsby Github repo</a>, as well as the Lighthouse docs, it seemed clear that <em>Total Blocking Time</em> (TBT) was the main culprit responsible for hampering the performance score for many sites.</p>
<p><a target="_blank" href="https://web.dev/tbt/">Total Blocking Time</a> (TBT) is defined as the <em>total amount of time between First Contentful Paint (FCP) and Time to Interactive (TTI)</em>. </p>
<p>Simply put, TBT is a measurement of how long the browser's <em>main thread</em> is blocked by long tasks, such as parsing JavaScript (JS).</p>
<p>That being said, any steps taken to reduce the amount of JS, as well as JS execution time, will positively impact site performance by reducing TBT.</p>
<p>Preact is a small (3kb), <a target="_blank" href="https://preactjs.com/">fast alternative to React</a>. And thanks to <a target="_blank" href="https://www.gatsbyjs.com/plugins/gatsby-plugin-preact/">gatsby-plugin-preact</a>, switching your Gatsby-site from running on React to Preact is incredibly easy.</p>
<p>Navigate to your project root and install the following packages using NPM:</p>
<p>    npm install --save gatsby-plugin-preact preact preact-render-to-string</p>
<p>...or Yarn:</p>
<p>    yarn add gatsby-plugin-preact preact preact-render-to-string</p>
<p>Then simply add </p>
<p>    ...
    <code>gatsby-plugin-preact</code>,
    ...</p>
<p>to your gatsby-config.js</p>
<p>Then run </p>
<p>    yarn gatsby build</p>
<p>If you use the <a target="_blank" href="https://www.gatsbyjs.com/plugins/gatsby-plugin-webpack-bundle-analyser-v2/">webpack bundle analyzer</a>, you should now be seeing a ~100kb smaller bundle size! Not bad, right.</p>
<p>Check out the difference this switch made to our bundle size in the image below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/preact-2.PNG" alt="The difference in bundle size between React and Preact" width="600" height="400" loading="lazy">
<em>An 8 percent decrease in bundle size for one line of code - not bad!</em></p>
<p>Switching to Preact should bump your performance score about <strong>5-10 points</strong>.</p>
<h2 id="heading-step-2-reconsider-the-necessity-of-the-hero-image">Step 2. Reconsider the necessity of the <em>Hero-Image</em></h2>
<p>Another metric that made our site, <a target="_blank" href="https://www.smartrate.se/">SmartRate</a>, take a beating performance-wise was the <a target="_blank" href="https://web.dev/lcp/">Largest Contentful Paint</a> (LCP).</p>
<p>LCP is a metric for measuring <em>perceived load speed</em>. And together with Total Blocking Time, LCP and TBT makes up 50% of the total Lighthouse Performance score.</p>
<p>With that in mind, it's not a big surprise that an image covering 80 percent of the fold negatively impacts the LCP-metric, even when optimized using the webp format.</p>
<p>We tweaked the hero image and achieved partial success by disabling fade-in and switching loading from the default parameter (lazy) to eager:</p>
    <img src="{heroImage}" width="600" height="400" alt="{heroImage}" loading="lazy">

<p>However, the improvements were only marginal and barely noticable in Lighthouse (about 2-4 points), so we decided to regroup and rethink.</p>
<p>What <em>was</em> the purpose of our hero-image, really?</p>
<p>The hero image is commonly used to grab the user's attention and convey a central message to strenghten your brand. </p>
<p>In our case however, that's not how we use <em>the fold</em>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/www.smartrate.se_-Desktop-monitor--1.png" alt="A screenshot of the SmartRate landing page" width="600" height="400" loading="lazy">
<em>A screenshot of the fold as it looks today, showing the hero-unit and user-input area.</em></p>
<p>As you can see from the image, the <em>fold</em> is dedicated to <em>user-input</em>. And the hero-image we were using before the current one was just a blurred photo in order to give some ambience to the fold.</p>
<p>After a bit of thinking, we realized we couldn't justify using a hero-image, given the negative impact it had on our performance metrics.</p>
<p>Instead, inspired by sites such as <a target="_blank" href="https://www.spotify.com">Spotify.com</a>,  we decided to go with an SVG background.</p>
<p><strong>This single decision reduced the size of the initial page-load by a whooping 65kb!</strong></p>
<p>From an optimized webp image of ~67kb down to a mere 2kb SVG for the same real estate.</p>
<p>Upon finding that this solution completely fixed our issues with the LCP metric, we quickly dropped the idea of using a <em>hero-image</em> from our most important subpage, <a target="_blank" href="https://www.smartrate.se/foretagslan/">företagslån</a> - business loans - as well.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/www.smartrate.se_foretagslan_-Desktop-monitor--1.png" alt="A screenshot of the SmartRate jämför företagslån subpage" width="600" height="400" loading="lazy">
<em>Current sub-page design utilizing a hero-unit (although not a hero-image)</em></p>
<p>For this, and our other subpages, we opted for a subtle three-color CSS gradient instead, to make the <em>hero-message</em> stand out.</p>
<p>This solution was perhaps not as sleek as using a custom tailored image, but it did the trick and greatly improved the LCP for our subpages as well.</p>
<p>Which brings me to...</p>
<h3 id="heading-key-considerations-if-your-site-is-using-a-hero-image">Key considerations if your site is using a hero-image</h3>
<p>Dropping the hero-image in favor of a SVG or CSS background will, in our experience, solve the issues caused by a low LCP score.</p>
<p>However, depending on the purpose of your hero-unit, this solution may not be optimal for you.</p>
<p>So, before you decide what to do, you should consider a few things:</p>
<ul>
<li>Is the hero-image <strong>custom made</strong> for your site <strong>or a stock-photo</strong>?</li>
<li>Does the hero-image <strong>add value</strong> to your brand?</li>
<li>What is the purpose of the fold on <strong>your site</strong>?</li>
</ul>
<p>If your hero-image adds great branding value for your site, perhaps the trade-off for better performance simply isn't worth it.</p>
<p>However, if you're ready to give my suggestions a try, you will be happy to hear about the following resources.</p>
<h3 id="heading-great-resources-for-svg-backgrounds">Great resources for SVG backgrounds</h3>
<p>Below I've compiled a short list of valuable resources and tools for anyone who wants to switch from using a hero-image to using SVG/CSS patterns:</p>
<ul>
<li><p><a target="_blank" href="https://www.heropatterns.com/">Hero Patterns</a> by Steve Schoger
 A great tool providing multiple customizable SVG patterns.</p>
</li>
<li><p><a target="_blank" href="https://philiprogers.com/svgpatterns/">SVG Patterns</a> by Philip Rogers
 Another free SVG pattern gallery.</p>
</li>
<li><p><a target="_blank" href="https://jakearchibald.github.io/svgomg/">SVGOMG</a> by Jake Archibald
 A great, free resource for minifying SVG-files. It's all about reducing
 those kb's, right?</p>
</li>
</ul>
<p>The next step is a bit more situational, but will, for those of you who are using a UI library, still be <em>very relevant</em>. For us, this step was just as important as the first two steps in improving our metrics.</p>
<h2 id="heading-step-3-dropping-material-ui-for-tailwindcss">Step 3. Dropping Material UI for TailwindCSS</h2>
<p>Let me state, right off the bat, that I'm a big, big fan of <a target="_blank" href="https://material-ui.com/">Material UI</a>. And I'm not alone in thinking so. Until recently, MUI has been the most popular React UI library on Github (currently at second position).</p>
<p>When we started developing our site, the design was entirely based on MUI components.</p>
<p>The only problem was, <em>it was slowing down the performance of the site</em>.</p>
<p>A lot.</p>
<p>Especially for mobile users.</p>
<p>After the release of Lighthouse version 6, we simply couldn't get the mobile performance ratings above 70 points, due to a very high <em>Total Blocking Time</em> (TBT). </p>
<p>Nothing we did seemed to matter at first. We even tried code-splitting using <a target="_blank" href="https://loadable-components.com/docs/code-splitting/">Loadable Components</a>, and lazy-loading non-essential payloads.</p>
<p>After some digging though, <strong>we identified Material UI as the source for the decrease in performance</strong>.</p>
<p>During page render, layout calculations (and re-calculations) seemed to occur all over the place, which contributed to an increased TBT.</p>
<p>We started removing MUI components, <em>one by one</em>, but this did little to improve the performance.</p>
<p>Finally, we were down to a single MUI component, and a near clean boilerplate website.</p>
<p>And we were still experiencing low performance ratings.</p>
<p><strong>How could this be?</strong></p>
<p>Well, as it appeared, importing a single MUI component would bring in the entire Material UI library into the bundle. And loading the landing-page would require the user to download the entire CSS and JS of Material UI.</p>
<p><strong>But what about <a target="_blank" href="https://webpack.js.org/guides/tree-shaking/">tree-shaking</a> I can hear you say?</strong></p>
<p>Well, to that I can only respond that we followed <a target="_blank" href="https://material-ui.com/guides/minimizing-bundle-size/">the MUI recommendations</a> for minimizing the bundle size. Our efforts, however, did not pay off.</p>
<p>By removing the last MUI import, we noticed <strong>a staggering ~170kb drop in bundle size!</strong></p>
<p>Finally, the performance of our site soared into the green, 90+ points, <em>even on mobile!</em></p>
<p>The TBT was now non-existent, but so was the layout of our website.</p>
<p>We therefore started looking at alternatives, and I remembered <a target="_blank" href="https://www.gatsbyjs.com/docs/tailwind-css/">reading about TailwindCSS integration into Gatsby</a> some time earlier.</p>
<p>One phrase that caught my attention was <em>"Purging your CSS"</em>.</p>
<p><a target="_blank" href="https://purgecss.com/">PurgeCSS</a>, which is now built-into TailwindCSS, does exactly what you think it does - removes unused CSS!</p>
<p>Perfect.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/muitotailwindpostcss-1.png" alt="Image illustrating the switch from Material UI to TailwindCSS" width="600" height="400" loading="lazy">
<em>By transitioning from Material UI to TailwindCSS, we could obtain a material-ish design with a great performance score.</em></p>
<p>Simply following the <a target="_blank" href="https://www.gatsbyjs.com/docs/tailwind-css/">Tailwind installation guide in the Gatsby docs</a> was enough for us to get going. We slowly started designing material-ish looking components using Tailwind through PostCSS.</p>
<p>Not quite as nice looking as MUI components, but not far off. Given the huge performance boost though, <em>it was totally worth it</em>.</p>
<p>For being complete beginners, I must say designing components using <strong>Tailwind is surprisingly intuitive</strong>. You quickly get the hang of it.</p>
<h3 id="heading-reconnecting-to-the-first-step">Reconnecting to the first step</h3>
<p>Another small perk of using Preact over React is the possibility to use the class parameter over the className parameter (which still works). This makes designing components a bit quicker - especially when copying markdown from their <a target="_blank" href="https://tailwindcss.com/docs/installation">official website</a>.</p>
<p>Should you decide to abandon Material UI, Bootstrap, or whatever other React-based UI library you're using in favor of Tailwind, you'll be happy to know about the following resources:</p>
<ul>
<li><a target="_blank" href="https://tailwindui.com/preview">Tailwind UI</a> Made by the creators of TailwindCSS, Tailwind UI is a repo where you can find beautiful, pre-designed components. A few of them can be used for free.</li>
<li><a target="_blank" href="https://tailwindcomponents.com/">Tailwind Components</a> is a repo of free-to-use community-made Tailwind components.</li>
</ul>
<h2 id="heading-bonus-tip-manage-your-bundle-size-going-forward">Bonus tip: Manage your bundle size going forward</h2>
<p>As you probably can imagine, optimizing the bundle size and rebuilding the entire UI of our site was quite taxing. If I learned one important lesson during this process it was this:</p>
<p><strong>Mind the bundle size!</strong></p>
<p>As we grew more and more aware of how bundle size was impacting the performance of our site, we stumbled upon a tool called <strong><a target="_blank" href="https://bundlephobia.com/">bundlephobia</a></strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/bundlephobia1-1.PNG" alt="A screenshot of the Bundlephobia landing page" width="600" height="400" loading="lazy">
<em>Bundlephobia landing page.</em></p>
<p>This great tool will <em>"find the cost of adding a npm package to your bundle"</em>. Not only that, but it will show you similar packages, and how they relate in size to the one you're currently viewing.</p>
<p>This was truly useful for us when we developed the <a target="_blank" href="https://www.smartrate.se/bolan/">bolån</a> (mortgages rates) sub-page. We required a charting library that would let us put together multiple line charts, showing the average mortgage rates for Sweden's largest banks over the last twelve months.</p>
<p>We couldn't find any other website doing this, so we thought it would be a great, free service to offer our visitors.</p>
<p>However, wiser from earlier experiences, we were not keen on simply using the first charting library that crossed our path.</p>
<p>Using bundlephobia, we compared the bundle size of different charting libraries and found that, based on our needs, <a target="_blank" href="https://gionkunz.github.io/chartist-js/">chartist.js</a> would be sufficient or us.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/bundlephobia2.PNG" alt="A screenshot from Bundlephobia showing similar libraries to Chartist.js" width="600" height="400" loading="lazy">
<em>Bundlephobia showing similar libraries (and their respective package-size) to chartist.js</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/bolaneranta-genomsnitt.png" alt="A line chart" width="600" height="400" loading="lazy">
<em>And an image of the resulting line charts showing historical mortgage rates.</em></p>
<p>If our need was simply to display interactive line-charts, why pay more than we need?</p>
<p>In other words, if we have the option to get the desired function with minimal impact to performance, that option should be our default choice.</p>
<p>However, it's also important to recognize that decisions between design and performance almost always implicates a trade-off. <em>And that trade-off should be considered wisely</em>.</p>
<p><strong>Our priority was performance, as shown by the results below:</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/100perfectscore-1.PNG" alt="Image showing near-perfect Lighthouse score" width="600" height="400" loading="lazy">
<em>A near perfect Google Lighthouse score! Only two points off on Accessibility for having too low of a contrast ratio on some buttons. But hey, design has to prevail somewhere, right?</em></p>
<p>In this post we covered <strong>the steps we took in order to achieve a near-perfect Google Lighthouse score</strong>, by:</p>
<ul>
<li>Improving the metric <em>Total Blocking Time</em> by switching from React to Preact </li>
<li>Improving the metric <em>Largest Contentful Paint</em> by optimizing the parameters of the hero-image, or switching the hero-image for a SVG-pattern</li>
<li>Improving the metric <em>Total Blocking Time</em> by switching from Material UI to TailwindCSS, and purging unused CSS using PurgeCSS</li>
<li>Reducing overall bundle size</li>
</ul>
<p>I truly hope the lessons we learned will inspire and benefit you, too!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Travel Bucket List Map with Gatsby, React Leaflet, & Hygraph ]]>
                </title>
                <description>
                    <![CDATA[ Traveling is fun and we all have a lot of places we want to visit, but rarely do we have time to do it all at once. That’s what bucket lists are for! How can we create a custom mapping app that we can show all of our the destinations ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-travel-bucket-list-map-with-gatsby-react-leaflet-graphcms/</link>
                <guid isPermaLink="false">66b8e3580cedc1f2a4f7069b</guid>
                
                    <category>
                        <![CDATA[ beginners guide ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ graphcms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ headless cms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mapping ]]>
                    </category>
                
                    <category>
                        <![CDATA[ maps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react-leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Travel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Tue, 23 Jun 2020 14:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/travel-bucket-list.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Traveling is fun and we all have a lot of places we want to visit, but rarely do we have time to do it all at once. That’s what bucket lists are for! How can we create a custom mapping app that we can show all of our the destinations on our bucket list?</p>
<p>Note: As of July 2022, GraphCMS is now <a target="_blank" href="https://hygraph.com/">Hygraph</a>.</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What are we going to build?</a></li>
<li><a class="post-section-overview" href="#heading-step-1-creating-a-new-app-with-gatsby-starter-leaflet">Step 1: Creating a new app with Gatsby Starter Leaflet</a></li>
<li><a class="post-section-overview" href="#heading-step-2-creating-and-managing-a-list-of-travel-locations-with-graphcms">Step 2: Creating and managing a list of travel locations with GraphCMS</a></li>
<li><a class="post-section-overview" href="#heading-step-3-querying-our-graphcms-location-data-with-gatsby-and-graphql">Step 3: Querying our GraphCMS location data with Gatsby and GraphQL</a></li>
<li><a class="post-section-overview" href="#heading-step-4-creating-a-bucket-list-of-destinations-and-adding-them-to-the-map">Step 4: Creating a bucket list of destinations and adding them to the map</a></li>
<li><a class="post-section-overview" href="#heading-what-else-other-features-can-we-add-to-our-app">What else other features can we add to our app?</a></li>
<li><a class="post-section-overview" href="#heading-want-to-learn-more-about-maps">Want to learn more about maps?</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/isbr52VKjb0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We’re going to build a mapping app with <a target="_blank" href="https://www.gatsbyjs.org/">Gatsby</a> managed by a CMS that will both display markers on a map and show our locations in a simple text-based list for our bucket list locations.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/travel-bucket-list-demo.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Demo of a Travel Bucket List mapping app</em></p>
<p>We’ll spin up the app with a <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">Gatsby Starter for Leaflet</a> and then we’ll use <a target="_blank" href="https://graphcms.com/">GraphCMS</a> to create and manage the list of locations for our map!</p>
<h2 id="heading-woah-a-mapping-app">Woah, a mapping app?</h2>
<p>Yup. If you haven't played with maps before, don't be discouraged! It's not as bad as you probably think. If you'd rather start with mapping basics, you can  <a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">read more about how mapping works</a>  first.</p>
<h2 id="heading-step-1-creating-a-new-app-with-gatsby-starter-leaflet">Step 1: Creating a new app with Gatsby Starter Leaflet</h2>
<p>We’ll start off with Gatsby Starter Leaflet. This is going to give us a basic React application with our mapping tools already built in.</p>
<h3 id="heading-creating-a-new-gatsby-app-with-gatsby-starter-leaflet">Creating a new Gatsby app with Gatsby Starter Leaflet</h3>
<p>To get started, navigate to where you want to create your new app and run:</p>
<pre><code class="lang-shell">gatsby new my-travel-bucket-list https://github.com/colbyfayock/gatsby-starter-leaflet
</code></pre>
<p><em>Note: you can replace <code>my-travel-bucket-list</code> with whatever you want. This will be used to create the new folder for the app.</em></p>
<p>Once you run that, Gatsby will pull down the Starter and install the dependencies. After it’s complete, navigate to that directory and run the development command:</p>
<pre><code class="lang-shell">cd my-travel-bucket-list
yarn develop
# or
npm run develop
</code></pre>
<p>Once it’s finished location, your app should be ready to go!</p>
<h3 id="heading-cleaning-our-some-demo-code">Cleaning our some demo code</h3>
<p>Because we’re using a Starter, it has a little bit of demo code. Let’s clean that out to avoid any confusion.</p>
<p>Open up the <code>src/pages/index.js</code> file.</p>
<p>First, remove everything inside of <code>mapEffect</code> except the first line and set up an alias for <code>leafletElement</code> to <code>map</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mapEffect</span>(<span class="hljs-params">{ leafletElement: map } = {}</span>) </span>{
  <span class="hljs-keyword">if</span> ( !map ) <span class="hljs-keyword">return</span>;
}
</code></pre>
<p>With that gone, we can remove the <code>markerRef</code> definition at the top of the <code>IndexPage</code> component, remove the <code>ref={markerRef}</code> prop from our <code>&lt;Marker&gt;</code> component, and the <code>useRef</code> import next to React.</p>
<p>Now, we can remove all of the variables that start with <code>popup</code> and <code>time</code>, including:</p>
<ul>
<li>timeToZoom</li>
<li>timeToOpenPopupAfterZoom</li>
<li>timeToUpdatePopupAfterZoom</li>
<li>popupContentHello</li>
<li>popupContentGatsby</li>
</ul>
<p>Lastly, you can remove all of the following lines:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> L <span class="hljs-keyword">from</span> <span class="hljs-string">'leaflet'</span>;
...
import { promiseToFlyTo, getCurrentLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">'lib/map'</span>;
...
import gatsby_astronaut <span class="hljs-keyword">from</span> <span class="hljs-string">'assets/images/gatsby-astronaut.jpg'</span>;
...
const ZOOM = <span class="hljs-number">10</span>;
</code></pre>
<p>Once done, we should be ready to go with a basic app with a map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/new-app-gatsby-starter-leaflet.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New app with Gatsby Starter Leaflet</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-travel-bucket-list/commit/63eed5a7a208ede6f8eeec44e0c08b594b407360">Follow along with the commit!</a></p>
<h2 id="heading-step-2-creating-and-managing-a-list-of-travel-locations-with-graphcms">Step 2: Creating and managing a list of travel locations with GraphCMS</h2>
<h3 id="heading-creating-a-graphcms-account">Creating a GraphCMS account</h3>
<p>To get started with GraphCMS, you’ll need an account. I’m not going to walk you through this part, but the good news is they have a generous free tier that makes it easy to sign up for us to use for our demo!</p>
<p><a target="_blank" href="https://app.graphcms.com/signup">Sign up for GraphCMS</a></p>
<p>Alternatively, if you already have an account, you can make sure you’re logged in.</p>
<h3 id="heading-creating-a-new-graphcms-project">Creating a new GraphCMS project</h3>
<p>Once logged in, we’ll want to create a new project. We’re going to create one manually, so once at the <a target="_blank" href="https://app.graphcms.com/">GraphCMS Dashboard</a>, select <strong>Create new project</strong>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-create-new-project.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new project in GraphCMS</em></p>
<p>Here, you can enter whatever you’d like for the <strong>Name</strong> and <strong>Description</strong> such as:</p>
<ul>
<li>Name: My Travel Bucket List</li>
<li>Description: The locations that I want to travel to some day!</li>
</ul>
<p>Below that you’ll see a map where you’ll select a <strong>Region</strong>. This is where your database data will live, so while it probably doesn’t matter too much for our purposes, you can choose the one that’s closest to you.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-new-project.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring a new project in GraphCMS</em></p>
<p>After you select your options, go ahead and click <strong>Create Project</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-select-plan.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Selecting the Personal plan in GraphCMS</em></p>
<p>Next, you’ll be presented with billing options. Since we’re just creating a demo, under <strong>Personal</strong> select <strong>Continue</strong> at which point we’ll be dropped into our new GraphCMS project dashboard.</p>
<h3 id="heading-creating-a-new-content-model-schema-with-graphcms">Creating a new Content Model Schema with GraphCMS</h3>
<p>In GraphCMS, a Content Model refers to a specific type of data that has specific properties associated with it. In our case, our Model will be a Destination, which will be defined by a Name and a Location.</p>
<p>First, navigate to the <strong>Schema</strong> section of GraphCMS in the left sidebar and select <strong>Create Model</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-create-new-schema-model.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new Schema Model in GraphCMS</em></p>
<p>Once selected, you’ll see a popup that asks for a bit more information. Here, you can type in “Destination” as the <strong>Display Name</strong>, which will also fill in most of the other fields. We’ll leave those as is.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-new-content-model.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring a new Model in GraphCMS</em></p>
<p>Feel free to add a description if you’d like, but it’s not required. Then select <strong>Create model</strong>.</p>
<p>Now that we have our Model, we need our properties.</p>
<p>First, select <strong>Single line text</strong> in the right list of fields and add a <strong>Display Name</strong> of “Name”. This will also fill out <strong>App Id</strong> which you can leave as is. Then click <strong>Create</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-text-field.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding and configuring a new text field in GraphCMS</em></p>
<p>Next, scroll down in the field options on the right and under <strong>Location</strong> select <strong>Map</strong>. Add “Location” as the <strong>Display Name</strong>, which will set the <strong>App Id</strong> as “location” which you can leave as is. Then same as before, click <strong>Create</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-new-map-field.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding and configuring a new map field in GraphCMS</em></p>
<p>Now we have a Content Model which we’ll use to create our locations!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-destination-content-model.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Destination content Model in GraphCMS</em></p>
<h3 id="heading-creating-our-locations">Creating our locations</h3>
<p>Finally, let’s create our locations. Navigate over to <strong>Content</strong> in the GraphCMS dashboard, make sure you’ve selected <strong>Destination</strong> under <strong>System</strong> (should be the only one), and select <strong>Create New</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-add-new-content.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Create new Destination Content in GraphCMS</em></p>
<p>Now we can start adding all of our locations! First, add the name of your location in the <strong>Name</strong> field, then you can use the <strong>Search</strong> box under <strong>Location</strong> to find that location on the map.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-create-new-destination-content-item.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding a new Destination Content item in GraphCMS</em></p>
<p>Once you’re good, hit <strong>Save and publish</strong>. This will create your first location!</p>
<p>Follow those same steps and create as many locations as you want.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-destination-content-items.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>List of Destination Content items in GraphCMS</em></p>
<p>We’ll use these for our map and bucket list.</p>
<h2 id="heading-step-3-querying-our-graphcms-location-data-with-gatsby-and-graphql">Step 3: Querying our GraphCMS location data with Gatsby and GraphQL</h2>
<p>Now that we have our locations, let’s use them!</p>
<h3 id="heading-adding-a-plugin-to-gatsby-to-query-our-graphql-data">Adding a plugin to Gatsby to query our GraphQL data</h3>
<p>First, we need to <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-source-graphql/">add a new plugin</a> to our Gatsby project to query our GraphQL data. In your terminal make sure your development server isn’t running and run:</p>
<pre><code class="lang-shell">yarn add gatsby-source-graphql
# or
npm install gatsby-source-graphql
</code></pre>
<p>Next, open up your <code>gatsby-config.js</code> file in the root of your project and add the following to your plugins:</p>
<pre><code class="lang-json">{
  resolve: 'gatsby-source-graphql',
  options: {
    typeName: 'GCMS',
    fieldName: 'gcms',
    url: '[API ENDPOINT]',
  }
}
</code></pre>
<p>This will be what sources our data from GraphCMS, but we need an endpoint.</p>
<h3 id="heading-finding-our-api-endpoint-for-graphcms">Finding our API endpoint for GraphCMS</h3>
<p>Open back up your browser and head over to your GraphCMS project. After selecting <strong>Settings</strong> in the left navigation, select <strong>API Access</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-api-access.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>API Access in GraphCMS</em></p>
<p>Before we copy our API Endpoint, first we need to update our permissions so we can query our API. Under <strong>Public API Permissions</strong>, check the box next to <strong>Content from stage Published</strong> and click <strong>Save</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-api-access.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring API permissions in GraphCMS</em></p>
<p>Next, copy the URL under <strong>Endpoints</strong>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-copy-api-access-endpoint.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Copying API Endpoint in GraphCMS</em></p>
<p>And paste that in to your <code>gatsby-config.js</code> file that we modified above:</p>
<pre><code class="lang-json">{
  resolve: 'gatsby-source-graphql',
  options: {
    typeName: 'GCMS',
    fieldName: 'gcms',
    url: 'https:<span class="hljs-comment">//[region-id].graphcms.com/v2/[project-id]/master',</span>
  },
},
</code></pre>
<p><em>Note: your URL will have actual values inside of <code>[region-id]</code> and <code>[project-id]</code>.</em></p>
<p>Save your <code>gatsby-config.js</code> file and start your development server backup (<code>yarn develop</code>) and we’re ready to go!</p>
<h3 id="heading-querying-our-locations-via-graphql">Querying our locations via GraphQL</h3>
<p>Finally, let’s actually query our data so that we’ll be able to use it in our app.</p>
<p>We’re going to create a new <a target="_blank" href="https://reactjs.org/docs/hooks-reference.html">React Hook</a> that we’ll be able to use to grab our locations anywhere within our app.</p>
<p>Under <code>src/hooks/index.js</code>, add the following line to the existing list:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> { <span class="hljs-keyword">default</span> <span class="hljs-keyword">as</span> useDestinations } <span class="hljs-keyword">from</span> <span class="hljs-string">'./useDestinations'</span>;
</code></pre>
<p>This will allow us to more conveniently import our hook which we’ll create next.</p>
<p>Under <code>src/hooks</code>, create a new file <code>useDestinations.js</code> and paste in this code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { graphql, useStaticQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useDestinations</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { gcms = {} } = useStaticQuery( graphql<span class="hljs-string">`
    query {
      gcms {
        destinations {
          id
          name
          location {
            latitude
            longitude
          }
        }
      }
    }
  `</span> );

  <span class="hljs-keyword">let</span> { destinations } = gcms;

  <span class="hljs-keyword">return</span> {
    destinations,
  };
}
</code></pre>
<p>Here, we’re:</p>
<ul>
<li>Importing the <code>graphql</code> and <code>useStaticQuery</code> utilities from Gatsby</li>
<li>We’re creating a new function (or hook) that is exported by default</li>
<li>In that function, we’re using <code>useStaticQuery</code> to create a new GraphQL query which asks GraphCMS to return the data structure we defined.</li>
<li>That query returns a value which we destructure immediately to grab the <code>gmcs</code> object</li>
<li>We destructure <code>destinations</code> from <code>gmcs</code> and return it as part of a new object from our hook</li>
</ul>
<p>With this, we can now use our hook anywhere in our app!</p>
<p>Head over to your <code>src/pages/index.js</code> file, first import our new hook:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useDestinations } <span class="hljs-keyword">from</span> <span class="hljs-string">'hooks'</span>;
</code></pre>
<p>And at the top of the <code>IndexPage</code> component, query our data:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { destinations } = useDestinations();
</code></pre>
<p>This puts all of our locations into the <code>destinations</code> variable. We can test that this works by console logging it out:</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'destinations'</span>, destinations);
</code></pre>
<p>And once we open up our browser and look in our web developer tools console, we can see our location data!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/gatsby-starter-leaflet-logging-graphcms-destinations.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Logging destinations data to the web console</em></p>
<h2 id="heading-step-4-creating-a-bucket-list-of-destinations-and-adding-them-to-the-map">Step 4: Creating a bucket list of destinations and adding them to the map</h2>
<p>We’re going to start with creating a simple text list of our destinations. This will let us see all of our destinations in an easy to read format.</p>
<h3 id="heading-creating-a-text-list-of-our-destinations">Creating a text list of our destinations</h3>
<p>Inside of our <code>IndexPage</code> and above “Still Getting Started?”, let’s add the following code:</p>
<pre><code class="lang-jsx">&lt;h2&gt;My Destinations&lt;/h2&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
  { destinations.map(destination =&gt; {
    const { id, name } = destination;
    return <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>&gt;</span>{ name }<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  })}
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span>
</code></pre>
<p>This code:</p>
<ul>
<li>Adds a new header for our list</li>
<li>Creates a new unordered list</li>
<li>Loops through our <code>destinations</code> and creates a new list item for each destination that include’s the location’s name</li>
</ul>
<p>Once we hit save and reload, we should see our list under our map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/app-adding-list-of-destinations.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New basic list of destinations in the app</em></p>
<p>The list looks a little odd though right? We probably want to format it a little better to fit into the page.</p>
<p>Open up <code>src/assets/stylesheets/pages/_home.scss</code> and inside of the <code>.home-start</code> class, add:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.home-start</span> {

  ...

  <span class="hljs-selector-tag">ul</span> {
    <span class="hljs-attribute">list-style</span>: none;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">1.2em</span> <span class="hljs-number">0</span>;
  }
</code></pre>
<p>Let’s also modify the <code>h2</code> to space things out a little better:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.home-start</span> {

  ...

  <span class="hljs-selector-tag">h2</span> {

    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2em</span>;

    &amp;<span class="hljs-selector-pseudo">:first-child</span> {
      <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0</span>;
    }

  }
</code></pre>
<p>Once you hit save and reload, it should look a little better.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/app-fixing-styles-list-of-destinations.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Destinations in the app with cleaned up styles</em></p>
<p>Feel free to make additional changes, but we’ll leave it there for now.</p>
<h3 id="heading-adding-our-destinations-to-the-map">Adding our destinations to the map</h3>
<p>Now we can finally add our destinations to the map!</p>
<p>Inside of our <code>&lt;Map&gt;</code> component, we already have a <code>&lt;Marker&gt;</code>. This allows us to easily add a marker to the map given a position. We’ll take this concept and combine it with our text list to add our locations to the map.</p>
<p>Let’s update our <code>&lt;Map&gt;</code> code to match the following:</p>
<pre><code class="lang-jsx">&lt;<span class="hljs-built_in">Map</span> {...mapSettings}&gt;
  { destinations.map(<span class="hljs-function"><span class="hljs-params">destination</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> { id, name, location } = destination;
    <span class="hljs-keyword">const</span> position = [location.latitude, location.longitude];
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Marker</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{position}</span> /&gt;</span></span>
  })}
&lt;/<span class="hljs-built_in">Map</span>&gt;
</code></pre>
<p>Here we:</p>
<ul>
<li>Loop through our <code>destinations</code> to dynamically create a new list of components inside our <code>&lt;Map&gt;</code></li>
<li>Inside each loop instance, we destructure our date from <code>destination</code></li>
<li>We create a new <code>position</code> array with the latitude and longitude</li>
<li>Create a new <code>Marker</code> where we use our position to add it to the map</li>
</ul>
<p>This gives us our markers on the map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/mapping-app-with-destination-markers.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Markers for each destination in the mapping app</em></p>
<p>But we want to know what each of those locations are, so let’s also add a popup to each marker that will show the name.</p>
<p>First, we need to import <code>Popup</code> from <code>react-leaflet</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { Marker, Popup } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-leaflet'</span>;
</code></pre>
<p>Then, let’s update our <code>&lt;Marker&gt;</code> component to return:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">return</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Marker</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{position}</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Popup</span>&gt;</span>{ name }<span class="hljs-tag">&lt;/<span class="hljs-name">Popup</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">Marker</span>&gt;</span></span>
);
</code></pre>
<p>And once we save and open back up our map, you can now click on each marker and see our destinations name!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/mapping-app-with-destination-marker-popup.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Popup for each destination marker in the mapping app</em></p>
<h3 id="heading-before-were-done-center-the-map">Before we’re done, center the map</h3>
<p>Previously, our demo map centered on Washington, DC. Let’s update that to the center of the world since our map doesn’t focus on the United States.</p>
<p>Update the <code>LOCATION</code> variable to:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> LOCATION = {
  <span class="hljs-attr">lat</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">lng</span>: <span class="hljs-number">0</span>,
};
</code></pre>
<p>And with that, we have our map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/mapping-app-with-travel-bucket-list-markers.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Final mapping app with markers and popups for each destination</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-travel-bucket-list/commit/56dbadb74cea2770174eb8ea7c039be27ca18971">Follow along with the commit!</a></p>
<h2 id="heading-what-else-other-features-can-we-add-to-our-app">What else other features can we add to our app?</h2>
<h3 id="heading-add-a-way-to-check-off-each-location">Add a way to check off each location</h3>
<p>Inside GraphCMS, you can add a new field to your Destination content model that allows you to select whether you visited each location or not.</p>
<p>With this value, we can add it to our query and update our map with some kind of indicator like a checkmark to show that we’ve checked it off our bucket list!</p>
<h3 id="heading-customize-your-map-background-styles">Customize your map background styles</h3>
<p>We’re using a public version of <a target="_blank" href="https://www.openstreetmap.org/#map=5/38.007/-95.844">OpenStreetMap</a> which is open source, but <a target="_blank" href="https://www.mapbox.com/">Mapbox</a> offers some cool maps we can use to make it look a little more impressive.</p>
<p>If you want to get started changing your map styles, you can <a target="_blank" href="https://www.freecodecamp.org/news/how-to-set-up-a-custom-mapbox-basemap-with-gatsby-and-react-leaflet/">check out this other walkthrough</a> of mine to learn how to use Mapbox.</p>
<p><a target="_blank" href="https://www.colbyfayock.com/2020/04/how-to-set-up-a-custom-mapbox-basemap-style-with-react-leaflet-and-leaflet-gatsby-starter">Check out the blog post</a> or <a target="_blank" href="https://www.youtube.com/watch?v=KcPJr1b_rv0">watch the video</a>!</p>
<h3 id="heading-style-the-map-markers-with-a-custom-image">Style the map markers with a custom image</h3>
<p>You can check out my video walk through on how to change the markers to a custom image.</p>
<p>Take that a step further and use the feature above to dynamically show a different marker image when you’ve checked off a location.</p>
<p><a target="_blank" href="https://egghead.io/lessons/react-customize-geojson-data-markers-with-a-react-leaflet-icon-image?pl=mapping-with-react-leaflet-e0e0&amp;af=atzgap">Check out the video on Egghead.io!</a></p>
<h2 id="heading-want-to-learn-more-about-maps">Want to learn more about maps?</h2>
<p>Check out some of my other tutorials and videos:</p>
<ul>
<li><a target="_blank" href="https://egghead.io/playlists/mapping-with-react-leaflet-e0e0?af=atzgap">Mapping with React Leaflet</a> (<a target="_blank" href="https://egghead.io/?af=atzgap">egghead.io</a>)</li>
<li><a target="_blank" href="https://www.youtube.com/playlist?list=PLFsfg2xP7cbJTnTFH3OGXEAt9O1mpoqpR">Mapping Apps with React, Gatsby, &amp; Leaflet</a> (<a target="_blank" href="https://www.youtube.com/channel/UC7Wpv0Aft4NPNhHWW_JC4GQ">youtube.com</a>)</li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet">How to create a Coronavirus (COVID-19) Dashboard &amp; Map App with Gatsby and Leaflet</a> (colbyfayock.com)</li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-summer-road-trip-mapping-app-with-gatsby-and-leaflet">How to Create a Summer Road Trip Mapping App with Gatsby and Leaflet</a> (colbyfayock.com)</li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">How to build a mapping app in React the easy way with Leaflet</a> (colbyfayock.com)</li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping">Anyone Can Map! Inspiration and an introduction to the world of mapping</a> (colbyfayock.com)</li>
</ul>
<h2 id="heading-whats-on-your-travel-bucket-list">What’s on your travel bucket list?</h2>
<p><a target="_blank" href="https://twitter.com/colbyfayock">Let me know on Twitter!</a></p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/colbyfayock/status/1275441134144110595"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="600" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Enable Offline Mode for Your Gatsby Site ]]>
                </title>
                <description>
                    <![CDATA[ By Ondrej Polesny One of the reasons we create JAMstack sites is because of their great performance. Serving static files is easy and quick. But what if we upgrade the visitor's experience and make the site available offline? Looking at both recent r... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-enable-offline-mode-for-gatsby-site/</link>
                <guid isPermaLink="false">66d4608f9f2bec37e2da0662</guid>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JAMstack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 09 Jun 2020 15:15:41 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/gatsby-offline-article.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Ondrej Polesny</p>
<p>One of the reasons we create JAMstack sites is because of their great performance. Serving static files is easy and quick. But what if we upgrade the visitor's experience and make the site available offline?</p>
<p>Looking at both recent reports on the State of the JAMstack in 2020 (you can check out <a target="_blank" href="https://tracker.kontent.ai/942894/the-state-of-jamstack-report-2020">Kontent's report</a> and <a target="_blank" href="https://www.netlify.com/blog/2020/05/27/state-of-the-jamstack-survey-2020-first-results/">Netlify's report</a>) it's clear that performance is the main reason we are building static sites. </p>
<p>I call this fact a bit of a cheat. Performance benefit comes with the JAMstack by design, so it's like calling puppies cute. They're always cute because they're puppies.</p>
<p>If we're really serious about performance, though, even JAMstack sites can do better. But before we can dive into offline mode for Gatsby, we must understand how Gatsby serves pages:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/gatsby-site.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The visitor does not request every single HTML file from the server. Rather, Gatsby's JS client asks the server for the <em>page-data.json</em> of the respective page. That's how the visitor transitions into the requested page without that classic page reload. But even for that we need the network.</p>
<h2 id="heading-why-do-websites-even-need-an-offline-mode">Why do websites even need an offline mode?</h2>
<p>These days, everyone is online, right? An internet connection in mobile phones no longer seems like an option, but a requirement. We use apps like Whatsapp, Messenger, and others all the time. </p>
<p>But what if we step inside an elevator? What if we're walking to a car parked in an underground garage or driving through a tunnel? What if we're inside a plane that's about to take off?</p>
<p>No reception. That's what all these places share. And the only thing people can do with their phones without reception is to watch downloaded Netflix movies. Until you enable offline mode for your website.</p>
<h2 id="heading-how-does-it-work">How does it work?</h2>
<p>In a nutshell, we save the visitor the round-trip to the server and download all the necessary data in advance. And we install a ServiceWorker which acts as a server instead of a real remote server. </p>
<p>A ServiceWorker is a script that the visitor's browser runs in the background and enables features like push notifications and others. See <a target="_blank" href="https://developers.google.com/web/fundamentals/primers/service-workers">Google docs</a> for more information.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/gatsby-service-worker.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>With Gatsby, just like we're all used to, it's as simple as installing a plugin:</p>
<pre><code>npm i gatsby-offline-plugin --save
</code></pre><p>And adding it to the <code>gatsby-config.js</code>:</p>
<pre><code class="lang-javascript">plugins: [
    ...
    <span class="hljs-string">`gatsby-plugin-offline`</span>,
    ...
]
</code></pre>
<p>But every website uses many different types of assets, so typically, we need to take one additional step and configure the service worker.</p>
<h2 id="heading-assets-serving-strategies">Assets serving strategies</h2>
<p>Every website contains many assets ranging from CSS files through images and icons to web fonts and actual page data. </p>
<p>The service worker cannot really download all these assets during the first page load as that would go directly against the performance benefit. Visitors would also not be happy if their browsers started downloading 100MB of images the moment they decided to visit your photo gallery.</p>
<p>We can use regular expressions to target specific files and configure the service worker to treat them appropriately. Let's take a look at the available strategies:</p>
<h3 id="heading-cachefirst">CacheFirst</h3>
<p>Typical use: web fonts, stylesheets</p>
<p>The service worker checks the cache for the requested file. If the file's missing, it goes online to fetch it while storing it in the cache for future use.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/cache-first.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image credit: <a target="_blank" href="https://developers.google.com/web/tools/workbox/modules/workbox-strategies">https://developers.google.com/web/tools/workbox/modules/workbox-strategies</a></em></p>
<h3 id="heading-cacheonly">CacheOnly</h3>
<p>Possible use: your own pre-caching logic</p>
<p>The service worker checks the cache for the requested file. If the file's missing, it returns an error.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/cache-only.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image credit: <a target="_blank" href="https://developers.google.com/web/tools/workbox/modules/workbox-strategies">https://developers.google.com/web/tools/workbox/modules/workbox-strategies</a></em></p>
<h3 id="heading-networkfirst">NetworkFirst</h3>
<p>Typical use: non-critical API requests</p>
<p>The service worker goes online to fetch the requested file. If the network is down or server unresponsive, it falls back to the cache.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/network-first.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image credit: <a target="_blank" href="https://developers.google.com/web/tools/workbox/modules/workbox-strategies">https://developers.google.com/web/tools/workbox/modules/workbox-strategies</a></em></p>
<h3 id="heading-networkonly">NetworkOnly</h3>
<p>Typical use: critical API requests</p>
<p>The service worker goes online to fetch the requested file. If the network is down or server unresponsive, it returns an error.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/network-only.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image credit: <a target="_blank" href="https://developers.google.com/web/tools/workbox/modules/workbox-strategies">https://developers.google.com/web/tools/workbox/modules/workbox-strategies</a></em></p>
<h3 id="heading-stalewhilerevalidate">StaleWhileRevalidate</h3>
<p>Typical use: front-end assets, images</p>
<p>The service worker checks the cache for the requested file and provides it. Subsequently, it makes a network request to silently update the cache.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/stale-while-revalidate.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image credit: <a target="_blank" href="https://developers.google.com/web/tools/workbox/modules/workbox-strategies">https://developers.google.com/web/tools/workbox/modules/workbox-strategies</a></em></p>
<h2 id="heading-configuring-the-gatsby-site">Configuring the Gatsby site</h2>
<p>A simple configuration of a Gatsby site that should work offline looks like this:</p>
<pre><code class="lang-javascript">{
    <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-plugin-offline`</span>,
    <span class="hljs-attr">options</span>: {
        <span class="hljs-attr">precachePages</span>: [<span class="hljs-string">`/blog/*`</span>],
    },
}
</code></pre>
<p>This way I'm configuring the service worker to pre-cache all blog posts, which are all pages whose URL starts with <code>/blog/</code>. </p>
<p>Once the visitor accesses the index page with links to blog posts, they will be able to click-through to any of them without an active internet connection. That is, if you use the <code>Link</code> element in the implementation. Standard anchor tags make a browser go around the service worker and fetch data from remote.</p>
<p>The service worker will treat all assets according to its default configuration:</p>
<ul>
<li><strong>CacheFirst</strong><br>JS files, CSS files, everything inside folder "static/"</li>
<li><strong>NetworkFirst</strong><br><em>/page-data/</em>/page-data.json files</li>
<li><strong>StaleWhileRevalidate</strong><br>images, web font files, etc.</li>
</ul>
<p>So if you're worried that the service worker will fetch all assets of all blog posts, it will do so only after the visitor actually opens the blog post page. </p>
<p>The reason is, cache space and the visitor's internet connection bandwidth are limited. On the first page load, the visitor downloads all the site-wide assets like stylesheets, web fonts, icons, and others, so these assets will be available in the cache on subsequent loads. Images and other resources of pre-cached pages will be resolved once the page is requested and this can be changed only via custom logic.</p>
<p>So in which cases would you want to change the config? There were actually not many cases I could come up with, but I did stumble upon a few:</p>
<ul>
<li><strong>Assets provided from URLs without matching filename suffix</strong><br>Google serves CSS definitions of web fonts without the .css suffix and that is already covered by the default config. However, you may be serving images or other assets from URLs that don't have the appropriate suffix.</li>
<li><strong>Gain more control over the cache</strong><br>With some assets you want to be in charge of how long a certain item can reside in cache until it becomes expired.</li>
<li><strong>Exotic assets</strong><br>Alright, exotic may be a bit strong word :-) but web fonts in EOT format, pictures in HEIC format, short videos, songs, etc.</li>
</ul>
<p>In these cases you need to adjust the default configuration and define it within the plugin options:</p>
<pre><code class="lang-javascript">{
  <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-plugin-offline`</span>,
  <span class="hljs-attr">options</span>: {
    <span class="hljs-attr">precachePages</span>: [<span class="hljs-string">`/blog/*`</span>],
    <span class="hljs-attr">runtimeCaching</span>: [
      <span class="hljs-comment">// previous definitions from the default config</span>
      (...),
      {
        <span class="hljs-attr">urlPattern</span>: <span class="hljs-regexp">/^https:\/\/fonts\.gstatic\.com/</span>,
        handler: <span class="hljs-string">'cacheFirst'</span>,
        <span class="hljs-attr">options</span>: {
          <span class="hljs-attr">cacheableResponse</span>: {
            <span class="hljs-attr">statuses</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">200</span>]
          },
          <span class="hljs-attr">cacheName</span>: <span class="hljs-string">'google-fonts-webfonts'</span>,
          <span class="hljs-attr">expiration</span>: {
            <span class="hljs-attr">maxAgeSeconds</span>: <span class="hljs-number">60</span> * <span class="hljs-number">60</span>,
            <span class="hljs-attr">maxEntries</span>: <span class="hljs-number">30</span>
          }
        }
      },
    ]
  },
},
</code></pre>
<p>This additional config item will ensure that at most 30 Google fonts served from gstatic.com will be cached for a maximum duration of one hour and will be handled using the CacheFirst strategy.</p>
<h3 id="heading-build-the-site-before-testing">Build the Site Before Testing</h3>
<p>Once you are finished with the configuration, make sure to build and serve the site before testing the offline capabilities. They don't work in development mode.</p>
<pre><code>gatsby build &amp;&amp; gatsby serve
</code></pre><h2 id="heading-jamstack-performance-but">JAMstack = performance, but...</h2>
<p>In this article I showed you how to install and configure the offline plugin for Gatsby.</p>
<p> All JAMstack sites come with the great benefit of amazing performance by design. By adding offline capabilities, you are taking the browsing experience of your visitors to the next level compared to other JAMstack sites. The service worker configuration allows you to further fine-tune how each asset will be handled and cached.</p>
<p>If you're interested, you can find more information about the plugin in the <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-plugin-offline/">Gatsby docs</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What Are Environment Variables and How Can I Use Them with Gatsby and Netlify? ]]>
                </title>
                <description>
                    <![CDATA[ When starting to integrate 3rd party services into your application or website, you'll start to find it useful to have different environments, such as a development and production environment.  How can we configure this so we don't have to directly e... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-are-environment-variables-and-how-can-i-use-them-with-gatsby-and-netlify/</link>
                <guid isPermaLink="false">66b8e38ec9bc6d235bb126b2</guid>
                
                    <category>
                        <![CDATA[ continuous deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Tue, 28 Apr 2020 14:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/04/environment-variables.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When starting to integrate 3rd party services into your application or website, you'll start to find it useful to have different environments, such as a development and production environment. </p>
<p>How can we configure this so we don't have to directly edit our code to change our environment?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-are-environment-variables">What are environment variables?</a></li>
<li><a class="post-section-overview" href="#heading-how-can-environment-variables-be-useful">How can environment variables be useful?</a></li>
<li><a class="post-section-overview" href="#heading-how-can-i-keep-these-files-secure">How can I keep these files secure?</a></li>
<li><a class="post-section-overview" href="#heading-gatsby-and-environment-variables">Gatsby and environment variables</a></li>
<li><a class="post-section-overview" href="#heading-netlify-and-environment-variables">Netlify and environment variables</a></li>
<li><a class="post-section-overview" href="#heading-step-1-creating-a-hello-world-website">Step 1: Creating a "Hello, world" website</a></li>
<li><a class="post-section-overview" href="#heading-step-2-creating-a-local-environment-variable-with-gatsby">Step 2: Creating a local environment variable with Gatsby</a></li>
<li><a class="post-section-overview" href="#heading-step-3-deploying-the-website-to-netlify">Step 3: Deploying the website to Netlify</a></li>
<li><a class="post-section-overview" href="#heading-where-can-you-add-or-update-more-variables-in-netlify">Where can you add or update more variables in Netlify?</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/oq_RPOI0xsU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-are-environment-variables">What are environment variables?</h2>
<p>Environment variables are predetermined values that are typically used to provide the ability to configure a value in your code from outside of your application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/environment-variable-secret.jpg" alt="Image" width="600" height="400" loading="lazy">
_MY_SECRET<em>KEY environment variable used for authorization</em></p>
<p>When developing locally, or sometimes even in a deployment pipeline, you'll oftentimes find these variables stored in a file named with some kind of variation of  <code>.env</code>.</p>
<h2 id="heading-how-can-environment-variables-be-useful">How can environment variables be useful?</h2>
<p>Probably the most common use case for environment variables is being able to set up different configuration options for different environments. Often when developing against third party services, you want to have a development version or sandbox available to make test requests against, that way it doesn't impact real production data.</p>
<p>Environment variables are helpful because they allow you to change which of your environments use which third party service environment by changing an API key, endpoint, or whatever the service uses to distinguish between environments.</p>
<p>The code you deploy should be predictable, so by not having to change any code, just the configuration outside of the code, you can maintain that predictability.</p>
<h2 id="heading-how-can-i-keep-these-files-secure">How can I keep these files secure?</h2>
<p>This is probably one of the more important points here – you need to ensure you're handling these files with care and not checking them into a git repository. By exposing these keys by inadvertently uploading them to a public location, the internet could easily find these keys and abuse them for their own gains.</p>
<p>For instance, <a target="_blank" href="https://aws.amazon.com/">AWS</a> keys are a valuable source. People run bots with the sole purpose of trying to scan Github for keys. If someone finds an AWS key, they could use this key to access resources such as running a bitcoin operation at your expense. This isn't to scare you, its to make you aware so you avoid your keys getting compromised.</p>
<p>So how can we keep these secure? The easiest way is to add the environment file where you keep these keys to your <code>.gitignore</code> file.</p>
<p>To do this, simply open your existing <code>.gitignore</code> file or create a new one at the root of your repository and add the filename as a new line:</p>
<pre><code># Inside .gitignore
.env
</code></pre><p>If you want to get more advanced and make sure this never happens to a repository, you can check out some tools like <a target="_blank" href="https://github.com/awslabs/git-secrets">git-secrets</a> from AWS Labs or <a target="_blank" href="https://github.com/zricethezav/gitleaks">GitLeaks</a> that even has a <a target="_blank" href="https://github.com/marketplace/actions/gitleaks">Github Action</a> to make it easy to integrate with Github.</p>
<h2 id="heading-gatsby-and-environment-variables">Gatsby and environment variables</h2>
<p><a target="_blank" href="https://www.gatsbyjs.org/">Gatsby</a> by default makes two files available as part of its <a target="_blank" href="https://www.gatsbyjs.org/docs/environment-variables/">environment variable workflow</a> that makes these values available in the client: <code>.env.development</code> and <code>.env.production</code>. These correlate to the <code>gatsby develop</code> and <code>gatsby build</code> scripts to either develop or build your site.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/environment-variable-secret-gatsby.jpg" alt="Image" width="600" height="400" loading="lazy">
_MY_SECRET<em>KEY environment variable for development and production</em></p>
<p>To make use of these files within the Gatsby development and build process, Gatsby requires you to prefix these variables with <code>GATSBY_</code>. This also works if you'd like to have them available from an OS process level.</p>
<p>Though you could integrate <a target="_blank" href="https://github.com/motdotla/dotenv">dotenv</a> if you have more advanced needs or don't want to use the <code>GATSBY_</code> prefix, your path of least resistance is probably to just follow the Gatsby way when working in Gatsby.</p>
<h2 id="heading-netlify-and-environment-variables">Netlify and environment variables</h2>
<p><a target="_blank" href="https://www.netlify.com/">Netlify</a> provides the ability to add environment variables as part of its <strong>Build &amp; deploy</strong> settings which gets picked up as part of the build processes.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/netlify-environment-variable.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding an environment variable in Netlify</em></p>
<p>Luckily, Netlify makes it easy to add whatever environment variable you'd like to the build process! To add one, you can simply navigate to the <strong>Environment</strong> section of your project's <strong>Build &amp; deploy</strong> settings page and add a variable under <strong>Environment variables.</strong></p>
<p>We'll walk you through this process a little later.</p>
<h2 id="heading-step-1-creating-a-hello-world-website">Step 1: Creating a "Hello, world" website</h2>
<p>For our walkthrough, we're going to set up a really basic example of a Gatsby website just for the purposes of testing this out.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/new-website-gatsby-starter-leaflet.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New website with Gatsby Sass Starter</em></p>
<p>Though this isn't really a common use case of environment variables, where normally you would use them for things like API keys and service configurations, this will give you a great idea of how it fundamentally works.</p>
<p>We're going to use this <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-sass">Gatsby Sass Starter</a> I created which will give us a starting point and add "Hello, [Environment]" depending on where it's running.</p>
<p>To get started, let's create our local project by using the <a target="_blank" href="https://www.gatsbyjs.org/docs/gatsby-cli/">Gatsby CLI</a>. Navigate to where you'd like to store this project and run:</p>
<pre><code class="lang-shell">gatsby new my-env-project https://github.com/colbyfayock/gatsby-starter-sass
</code></pre>
<p>You can change <code>my-env-project</code> to whatever directory you'd like this project created in, but once you run this command, you'll now have a project in that new directory.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/new-gatsby-project-command-line.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New Gatsby project in the terminal</em></p>
<p>To get started, once inside that directory, run <code>yarn develop</code> to make changes locally or <code>yarn build</code> to compile your new site.</p>
<p>Once you're ready to go, you'll want to add this project to Github. If you're not familiar with how to do this, you can l<a target="_blank" href="https://help.github.com/en/github/importing-your-projects-to-github/adding-an-existing-project-to-github-using-the-command-line">earn how to add an existing project to Github</a> here.</p>
<h2 id="heading-step-2-creating-a-local-environment-variable-with-gatsby">Step 2: Creating a local environment variable with Gatsby</h2>
<p>Our next step is to create a local environment and add a change that will let us see that it works.</p>
<p>To get started, let's first create a new file at the root of our project called <code>.env.development</code>. It might ask you if you really want to use the <code>.</code> prefix, make sure you say yes!</p>
<p>Inside that file, let's add:</p>
<pre><code># Inside .env.development
GATSBY_MY_ENVIRONMENT=<span class="hljs-string">"Development"</span>
</code></pre><p><img src="https://www.freecodecamp.org/news/content/images/2020/04/gatsby-development-environment-file.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating an .env.development file</em></p>
<p>Next, to make sure we don't forget to do this, let's also add this <code>.env.development</code> file to our <code>.gitignore</code> so we don't accidentally commit this to our git history. If you don't already have a <code>.gitignore</code> file, make sure you create it at the root of your project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/adding-development-environment-file-gitignore.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding .env.development to your .gitignore</em></p>
<p>Finally, to check that this works, let's open <code>pages/index.js</code> and let's replace our <code>&lt;h1&gt;</code> tag's content with a "Hello, world!" variation:</p>
<pre><code class="lang-jsx">&lt;h1&gt;Hello, {process.env.GATSBY_MY_ENVIRONMENT}&lt;/h1&gt;
</code></pre>
<p>And if we save that change and open it in our browser, we should see "Hello, Development"!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/using-environment-variable-gatsby.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Using an environment variable for your Gatsby site</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-env-project/commit/e3e7000fbfab4cecac7739458034e70958e52211">Follow along with the commit!</a></p>
<h2 id="heading-step-3-deploying-the-website-to-netlify">Step 3: Deploying the website to Netlify</h2>
<p>So we have our website created using a simple environment variable. Next we'll want to actually deploy that site to Netlify. If you haven't already, we'll need to <a target="_blank" href="https://help.github.com/en/github/importing-your-projects-to-github/adding-an-existing-project-to-github-using-the-command-line">add our website to Github</a> or another Git provider. Make sure to have that set up before continuing on.</p>
<p>After creating an account and logging in to Netlify, let's click the <strong>New site from Git</strong> button the main dashboard, follow the instructions for connecting your Github or other Git provider to Netlify, and then find your new repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/adding-new-github-repository-netlify.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding a new Github repository to Netlify</em></p>
<p>Once you select your repository, you'll be asked to configure your build process. Luckily, Netlify can detect that we're using a Gatsby site and has it pre-filled for us. Unless you've added something special, keep the basic configuration to use <code>gatsby build</code> to build your project and <code>public/</code> for the output.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/configuring-netlify-build.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring Netlify build settings</em></p>
<p>Now before we hit <strong>Deploy</strong>, there's one thing we want to add, and that's our environment variable!</p>
<p>Right above the <strong>Deploy site</strong> button there's an <strong>Advanced</strong> button. Click that and you'll see a new dropdown with an additional <strong>New variable</strong> button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/configuring-environment-variable-netlify.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring an environment variable in the Netlify setup</em></p>
<p>Click that <strong>New variable</strong> button, add our <code>GATSBY_MY_ENVIRONMENT</code> as a new variable and add <code>Production</code> as the value. And finally, hit <strong>Deploy site</strong>!</p>
<p>From here, you should be able to watch your website deploy and once finished, you'll see your new site with "Hello, Production"!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/deployed-gatsby-site-with-environment-variable.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Deployed Gatsby site using Netlify environment variable</em></p>
<h2 id="heading-where-can-you-add-or-update-more-variables-in-netlify">Where can you add or update more variables in Netlify?</h2>
<p>With our example, we only added one variable during the setup. But Netlify lets you add or update any other variables you'd like.</p>
<p>If you'd ever like to change that variable or add more, you can navigate to the <strong>Environment</strong> section of the <strong>Build &amp; deploy</strong> settings, where you can edit and add any other variables in the <strong>Environment variables</strong> section.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/environment-variable-settings-netlify.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Environment variables settings in Netlify</em></p>
<h2 id="heading-looking-to-learn-more">Looking to learn more?</h2>
<p>Here are a few other things to help you get started with development fundamentals!</p>
<ul>
<li><a target="_blank" href="https://www.colbyfayock.com/2019/09/what-is-gatsby-and-why-its-time-to-get-on-the-hype-train">What is Gatsby and why it's time to get on the hype train?</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/02/what-is-the-jamstack-and-how-do-i-get-started">What is the JAMstack and how do I get started?</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/02/how-to-become-a-full-stack-web-developer-in-2020">How to Become a Full Stack Web Developer in 2020</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2019/08/put-down-the-javascript-learn-html-css">Put Down the Javascript - Learn HTML &amp; CSS</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/04/set-future-you-up-for-success-with-good-coding-habits">Set Future You Up for Success with Good Coding Habits</a></li>
</ul>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="600" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to add Coronavirus (COVID-19) case statistics to your React map dashboard with Gatsby ]]>
                </title>
                <description>
                    <![CDATA[ Previously, we walked through creating a map that shows an interactive look at Coronavirus (COVID-19) cases per country. How can we extend this with some case statistics to show recent data about the impacts on our world? Author's note: Similar to be... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-add-coronavirus-covid-19-case-statistics-to-your-map-dashboard-in-gatsby-and-react-leaflet/</link>
                <guid isPermaLink="false">66bee8e6bc07dbcebef938dc</guid>
                
                    <category>
                        <![CDATA[ coronavirus ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Covid-19 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ data analytics ]]>
                    </category>
                
                    <category>
                        <![CDATA[ front end ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mapping ]]>
                    </category>
                
                    <category>
                        <![CDATA[ maps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react-leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Wed, 22 Apr 2020 14:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/04/coronavirus-mapping-app-2600x1000.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Previously, we walked through creating a map that shows an interactive look at Coronavirus (COVID-19) cases per country. How can we extend this with some case statistics to show recent data about the impacts on our world?</p>
<p><em>Author's note: Similar to before, this dashboard is meant to be a demo and proof of concept for using real world data to build a dashboard. While this data should be accurate per the NovelCOVID API, I would recommend using tools like the <a target="_blank" href="https://www.arcgis.com/apps/opsdashboard/index.html#/bda7594740fd40299423467b48e9ecf6">Johns Hopkins University dashboard</a> for complete and accurate analysis. Stay home and be safe! ❤️</em></p>
<ul>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What are we going to build?</a></li>
<li><a class="post-section-overview" href="#heading-what-do-we-need-before-we-get-started">What do we need before we get started?</a></li>
<li><a class="post-section-overview" href="#heading-step-1-update-how-we-fetch-our-data-and-fetch-the-statistics">Step 1: Update how we fetch our data and fetch the statistics</a></li>
<li><a class="post-section-overview" href="#heading-step-2-adding-statistics-to-our-dashboard">Step 2: Adding statistics to our dashboard</a></li>
<li><a class="post-section-overview" href="#heading-step-3-make-the-data-human-friendly">Step 3: Make the data human friendly</a></li>
<li><a class="post-section-overview" href="#heading-step-4-add-the-last-updated-date">Step 4: Add the Last Updated date</a></li>
<li><a class="post-section-overview" href="#heading-what-can-i-do-next">What can I do next?</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/9bfxeod27fU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We're going to be extending our <a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet">original map demo</a> with some basic statistics that we can retrieve from the <a target="_blank" href="https://github.com/NovelCOVID/API">NovelCOVID API</a>. To get an idea, here's <a target="_blank" href="https://coronavirus-map-dashboard.netlify.app/">my demo</a> I'm basing this off of.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/coronavirus-covid-19-dashboard-map-stats.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Coronavirus (COVID-19) map demo with dashboard statistics</em></p>
<p>While you're not required to have completed <a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet/">Part 1</a> to apply these concepts, it definitely helps, and it lets you set up a map for your dashboard. If you'd like to start there, which I recommend, check out <a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet/">How to create a Coronavirus (COVID-19) Dashboard &amp; Map App with Gatsby and Leaflet</a> first.</p>
<h2 id="heading-woah-a-mapping-app">Woah, a mapping app?</h2>
<p>Yup. If you haven't played with maps before, don't be discouraged! It's not as bad as you probably think. If you'd rather start with mapping basics, you can  <a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">read more about how mapping works</a>  first.</p>
<h2 id="heading-what-do-we-need-before-we-get-started">What do we need before we get started?</h2>
<p>For this walkthrough, you pretty much need a React app in some form. I'll be working with the dashboard we previously built in my last walkthrough that includes a <a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet/">map of the cases of the Coronavirus (COVID-19) per country</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/coronavirus-map-tutorial-country-markers.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Coronavirus (COVID-19) map dashboard</em></p>
<p>I recommend starting with the previous tutorial, but if you want to skip the map and start fresh, the easiest way would probably be to use <a target="_blank" href="https://github.com/facebook/create-react-app">Create React App</a>, <a target="_blank" href="https://www.gatsbyjs.org/">Gatsby</a>, or <a target="_blank" href="https://nextjs.org/">Next.js</a>.</p>
<h2 id="heading-step-1-update-how-we-fetch-our-data-and-fetch-the-statistics">Step 1: Update how we fetch our data and fetch the statistics</h2>
<p>To get started with our statistics dashboard, we're going to do a little prep work by changing how we're fetching the data. The goal here, is we're going to wrap our request logic in a reusable way so that we can use it for both our countries data and our new statistics data.</p>
<h3 id="heading-creating-a-new-react-hook-to-fetch-data">Creating a new React hook to fetch data</h3>
<p>Diving in, the first we'll do is create a new <a target="_blank" href="https://reactjs.org/docs/hooks-reference.html">React hook</a> that will serve as how we fetch the data. To get started, create a new file in your hooks directory called <code>useTracker.js</code>  and add a line inside of <code>hooks/index.js</code> to export it:</p>
<pre><code class="lang-js"><span class="hljs-comment">// New file src/hooks/useTracker.js</span>
<span class="hljs-comment">// This will be empty for now</span>
</code></pre>
<pre><code class="lang-js"><span class="hljs-comment">// Inside hooks/index.js</span>
<span class="hljs-keyword">export</span> { <span class="hljs-keyword">default</span> <span class="hljs-keyword">as</span> useTracker } <span class="hljs-keyword">from</span> <span class="hljs-string">'./useTracker'</span>;
</code></pre>
<p>Inside of our <code>useTracker.js</code> file, we're going to set up our request logic. This is a long file, so make sure you copy and paste the entire thing before we walk through what it does:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;

<span class="hljs-keyword">const</span> API_HOST = <span class="hljs-string">'https://corona.lmao.ninja/v2'</span>;

<span class="hljs-keyword">const</span> ENDPOINTS = [
  {
    <span class="hljs-attr">id</span>: <span class="hljs-string">'all'</span>,
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/all'</span>,
    <span class="hljs-attr">isDefault</span>: <span class="hljs-literal">true</span>
  },
  {
    <span class="hljs-attr">id</span>: <span class="hljs-string">'countries'</span>,
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/countries'</span>
  }
]

<span class="hljs-keyword">const</span> defaultState = {
  <span class="hljs-attr">data</span>: <span class="hljs-literal">null</span>,
  <span class="hljs-attr">state</span>: <span class="hljs-string">'ready'</span>
}

<span class="hljs-keyword">const</span> useTracker = <span class="hljs-function">(<span class="hljs-params">{ api = <span class="hljs-string">'all'</span> }</span>) =&gt;</span> {

  <span class="hljs-keyword">const</span> [tracker = {}, updateTracker] = useState(defaultState)

  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchTracker</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> route = ENDPOINTS.find(<span class="hljs-function">(<span class="hljs-params">{ id } = {}</span>) =&gt;</span> id === api);

    <span class="hljs-keyword">if</span> ( !route ) {
      route = ENDPOINTS.find(<span class="hljs-function">(<span class="hljs-params">{ isDefault } = {}</span>) =&gt;</span> !!isDefault);
    }

    <span class="hljs-keyword">let</span> response;

    <span class="hljs-keyword">try</span> {
      updateTracker(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> {
          ...prev,
          <span class="hljs-attr">state</span>: <span class="hljs-string">'loading'</span>
        }
      });
      response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">`<span class="hljs-subst">${API_HOST}</span><span class="hljs-subst">${route.path}</span>`</span>);
    } <span class="hljs-keyword">catch</span>(e) {
      updateTracker(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> {
          ...prev,
          <span class="hljs-attr">state</span>: <span class="hljs-string">'error'</span>,
          <span class="hljs-attr">error</span>: e
        }
      });
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> { data } = response;

    updateTracker(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> {
      <span class="hljs-keyword">return</span> {
        ...prev,
        <span class="hljs-attr">state</span>: <span class="hljs-string">'ready'</span>,
        data
      }
    });

  }

  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchTracker()
  }, [api])

  <span class="hljs-keyword">return</span> {
    fetchTracker,
    ...tracker
  }
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> useTracker;
</code></pre>
<p>Starting from the top:</p>
<ul>
<li>We import our dependencies: we're going to use Reacts <code>useEffect</code>  and <code>useState</code> hooks to manage our requests</li>
<li>We define default constants: we have a base API endpoint for our data, a list of the available endpoints we'll use, and a state object that will store our data</li>
<li>We define our <code>useTracker</code> hook:  our hook includes one argument <code>api</code>  that will allow us to specify which endpoint we'll use to make our request</li>
<li>We set up a state instance: we'll want to keep track of our fetched data, so we create a <code>tracker</code> state instance that we'll be able to update</li>
<li>We created an asynchronous <code>fetchTracker</code> function: we'll use this to make our actual request</li>
<li>Inside our function: we first find the API route and create our URL, update our state instance to a "loading" state, try to make our request, catch any errors if there are any, and finally if the request is successful, we update our state with that data</li>
<li>We trigger our function: using a <code>useEffect</code> hook, we trigger our <code>fetchTracker</code> function to make the request. We only have one dependency of <code>api</code>. This means the function will only fire the first time and any time the <code>api</code> value we pass in changes. We won't be changing that value, but it may be helpful in other instances if you're dynamically changing the API used</li>
<li>We return our tracker: the returned object includes both our <code>tracker</code> data as well as our <code>fetchTracker</code> function that we could use to refetch the data if we'd like</li>
</ul>
<p>And with all of that, we have a brand new hook that will fetch data from the NovelCOVID API.</p>
<h3 id="heading-using-our-new-tracker-hook">Using our new tracker hook</h3>
<p>To make use of this hook, let's jump over to <code>src/pages/index.js</code>, remove our <code>axios</code> import if it's there, and instead import our hook:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useTracker } <span class="hljs-keyword">from</span> <span class="hljs-string">'hooks'</span>;
</code></pre>
<p>With our hook, let's replace our original country data request.  First, add the following to the top of the <code>IndexPage</code> component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { <span class="hljs-attr">data</span>: countries = [] } = useTracker({
  <span class="hljs-attr">api</span>: <span class="hljs-string">'countries'</span>
});

<span class="hljs-keyword">const</span> hasCountries = <span class="hljs-built_in">Array</span>.isArray(countries) &amp;&amp; countries.length &gt; <span class="hljs-number">0</span>;
</code></pre>
<p>This will let us fetch our country data and let us know if we have any results. Next, let's replace our original request.</p>
<p>Inside of our <code>mapEffect</code> function, let's remove the <code>axios</code> request in addition to the response, the destructured data object, and the <code>hasData</code> constant.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/code-diff-map-effect-countries-data.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Code diff showing update to map effect</em></p>
<p>Then, replace <code>hasData</code> with <code>hasCountries</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span> ( !hasCountries ) <span class="hljs-keyword">return</span>;
</code></pre>
<p>And replace <code>data</code> with <code>countries</code> in the <code>geoJson</code> object where we map our features:</p>
<pre><code class="lang-js">features: countries.map(<span class="hljs-function">(<span class="hljs-params">country = {}</span>) =&gt;</span> {
</code></pre>
<p>At this point, if you hit save and refresh, you shouldn't notice any difference to what you previously had.</p>
<h3 id="heading-add-a-request-for-our-stats">Add a request for our stats</h3>
<p>Now that we are using our <code>useTracker</code> hook to fetch our country data, let's also use that to fetch our stats.</p>
<p>Right next to where we set up our <code>useTracker</code> hook before, let's add another request:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { <span class="hljs-attr">data</span>: stats = {} } = useTracker({
  <span class="hljs-attr">api</span>: <span class="hljs-string">'all'</span>
});
</code></pre>
<p>And if we add a <code>console.log</code> statement under to see what's inside <code>stats</code>:</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'stats'</span>, stats);
</code></pre>
<p>We should see our <code>stats</code> data object logged out!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/console-log-coronavirus-stats-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Using console.log to show Coronavirus (COVID-19) statistics</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/fe9d85e57f7474a86d38213676bf62df4b6168a4">Follow along with the commit!</a></p>
<h2 id="heading-step-2-adding-statistics-to-our-dashboard">Step 2: Adding statistics to our dashboard</h2>
<p>Now that we have our data available to use, let's use it!</p>
<p>To get started adding our statistics to the dashboard, let's create a data structure that will allow us to easily configure the data we want to use.</p>
<p>To do this, let's first create a new array called <code>dashboardStats</code> below <code>hasCountries</code> at the top of the page component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> dashboardStats = [];
</code></pre>
<p>Inside this array, let's add some new objects that specify our data that we're pulling from the <code>stats</code> object we requested. To start, let's try to add:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> dashboardStats = [
  {
    <span class="hljs-attr">primary</span>: {
      <span class="hljs-attr">label</span>: <span class="hljs-string">'Total Cases'</span>,
      <span class="hljs-attr">value</span>: stats?.cases
    },
    <span class="hljs-attr">secondary</span>: {
      <span class="hljs-attr">label</span>: <span class="hljs-string">'Per 1 Million'</span>,
      <span class="hljs-attr">value</span>: stats?.casesPerOneMillion
    }
  },
  {
    <span class="hljs-attr">primary</span>: {
      <span class="hljs-attr">label</span>: <span class="hljs-string">'Total Deaths'</span>,
      <span class="hljs-attr">value</span>: stats?.deaths
    },
    <span class="hljs-attr">secondary</span>: {
      <span class="hljs-attr">label</span>: <span class="hljs-string">'Per 1 Million'</span>,
      <span class="hljs-attr">value</span>: stats?.deathsPerOneMillion
    }
  },
  {
    <span class="hljs-attr">primary</span>: {
      <span class="hljs-attr">label</span>: <span class="hljs-string">'Total Tests'</span>,
      <span class="hljs-attr">value</span>: stats?.tests
    },
    <span class="hljs-attr">secondary</span>: {
      <span class="hljs-attr">label</span>: <span class="hljs-string">'Per 1 Million'</span>,
      <span class="hljs-attr">value</span>: stats?.testsPerOneMillion
    }
  }
]
</code></pre>
<p>The reason we're splitting this up into <code>primary</code> and <code>secondary</code> keys, is we're going to use that to differentiate between logically similar stats that we want to style a little bit differently.</p>
<p>_Note: if you're not familiar with the <code>?.</code> syntax, it's called <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining">Optional Chaining</a>. This allows us to chain our properties without worrying about if the objects exist. If <code>stats</code> is undefined, it will simply return undefined instead of throwing an error._</p>
<p>With our stats data, let's add the tracker to our map. Let's remove our current <code>&lt;Map&gt;</code> component and include it nested inside our tracker div in the following:</p>
<pre><code class="lang-jsx">&lt;div className=<span class="hljs-string">"tracker"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Map</span> {<span class="hljs-attr">...mapSettings</span>} /&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tracker-stats"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      { dashboardStats.map(({ primary = {}, secondary = {} }, i) =&gt; {
        return (
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">Stat-</span>${<span class="hljs-attr">i</span>}`} <span class="hljs-attr">className</span>=<span class="hljs-string">"tracker-stat"</span>&gt;</span>
            { primary.value &amp;&amp; (
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tracker-stat-primary"</span>&gt;</span>
                { primary.value }
                <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>{ primary.label }<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            )}
            { secondary.value &amp;&amp; (
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tracker-stat-secondary"</span>&gt;</span>
                { secondary.value }
                <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>{ secondary.label }<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            )}
          <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        );
      })}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/div&gt;
</code></pre>
<p>This code should be immediately following the <code>&lt;Helmet&gt;</code> component if you're following along.</p>
<p>To explain what we're doing:</p>
<ul>
<li>We're creating a "tracker" div that will organize our stats</li>
<li>We move our <code>&lt;Map</code> component inside of this tracker</li>
<li>We create a separate section called "tracker-stats"</li>
<li>Inside of this, we create an unordered list (<code>ul</code>)</li>
<li>Inside of our list, we loop through all of our stats inside <code>dashboardStats</code></li>
<li>For each stat, we create a new list element (<code>li</code>) and include 2 optional paragraphs that includes our primary stat data and our secondary stat data</li>
</ul>
<p>Once we reload our page, we should now see a few stats:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/adding-coronavirus-stats-to-page.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding the first statistics to the page</em></p>
<p>Now that we have our stats on our page, let's make them look like they're in a dashboard.</p>
<p>Let's create a new file called <code>_tracker.scss</code> inside of our <code>src/assets/stylesheets/components</code> directory. Once that file is created, additionally add it to the <code>src/assets/stylesheets/components/__components.scss</code> file:</p>
<pre><code class="lang-scss"><span class="hljs-keyword">@import</span> <span class="hljs-string">"tracker"</span>;
</code></pre>
<p>With our new component style file ready to go, let's add some styles into <code>_tracker.scss</code>:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.tracker-stats</span> {

  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-variable">$blue-grey-900</span>;
  <span class="hljs-attribute">border-top</span>: solid <span class="hljs-number">1px</span> darken(<span class="hljs-variable">$blue-grey-900</span>, <span class="hljs-number">5</span>);

  <span class="hljs-selector-tag">ul</span> {
    <span class="hljs-attribute">display</span>: grid;
    grid-template-<span class="hljs-attribute">columns</span>: <span class="hljs-number">1</span>fr <span class="hljs-number">1</span>fr <span class="hljs-number">1</span>fr;
    <span class="hljs-attribute">list-style</span>: none;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  }

}

<span class="hljs-selector-class">.tracker-stat</span> {

  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2em</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">padding</span>: .<span class="hljs-number">5em</span>;
  <span class="hljs-attribute">border-right</span>: solid <span class="hljs-number">1px</span> darken(<span class="hljs-variable">$blue-grey-900</span>, <span class="hljs-number">5</span>);
  <span class="hljs-attribute">border-bottom</span>: solid <span class="hljs-number">1px</span> darken(<span class="hljs-variable">$blue-grey-900</span>, <span class="hljs-number">5</span>);

  <span class="hljs-selector-tag">strong</span> {
    <span class="hljs-attribute">font-weight</span>: normal;
    <span class="hljs-attribute">color</span>: <span class="hljs-variable">$blue-grey-300</span>;
  }

}

<span class="hljs-selector-class">.tracker-stat-primary</span> {

  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;

  <span class="hljs-selector-tag">strong</span> {
    <span class="hljs-attribute">display</span>: block;
    <span class="hljs-attribute">font-size</span>: .<span class="hljs-number">5em</span>;
  }

}

<span class="hljs-selector-class">.tracker-stat-secondary</span> {

  <span class="hljs-attribute">font-size</span>: .<span class="hljs-number">5em</span>;
  <span class="hljs-attribute">margin</span>: .<span class="hljs-number">8em</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>;

  <span class="hljs-selector-tag">strong</span> {
    <span class="hljs-attribute">font-size</span>: .<span class="hljs-number">8em</span>;
    <span class="hljs-attribute">margin-left</span>: .<span class="hljs-number">4em</span>;
  }

}
</code></pre>
<p>Above – we're adding colors and organizational effects, such as using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout">CSS Grid</a>, to allow our data to be organized in an easy to read way and to look good! We're also making use of some pre-existing colors variables that are used within the project to keep the color use consistent.</p>
<p>Once you save those styles and reload the page, it should look much better:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/adding-coronavirus-case-statistics-to-map-dashboard.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding case statistics to the dashboard</em></p>
<p>From here, feel free to add more stats or adjust them to your liking. In the demo I created, I added the stats for active cases, critical cases, and recovered cases. If you'd like to do the same, you can <a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/eb8a28c9e46dc2327ada0df21b250422e55d304c">check out the commit</a>.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/eb8a28c9e46dc2327ada0df21b250422e55d304c">Follow along with the commit!</a></p>
<h2 id="heading-step-3-make-the-data-human-friendly">Step 3: Make the data human friendly</h2>
<p>Now the rest of this walkthrough could be considered optional, but ultimately we want people to be able to read these statistics, right? So let's make the numbers a little more easy to read.</p>
<p>First, let's open our <code>src/lib/util.js</code> file and add this function:</p>
<pre><code class="lang-js"><span class="hljs-comment">/**
 * commafy
 * <span class="hljs-doctag">@description </span>Applies appropriate commas to large numbers
 */</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">commafy</span>(<span class="hljs-params">value</span>) </span>{
  <span class="hljs-keyword">let</span> numberString = <span class="hljs-string">`<span class="hljs-subst">${value}</span>`</span>;

  numberString = numberString.split(<span class="hljs-string">''</span>);

  numberString.reverse();

  numberString = numberString.reduce(<span class="hljs-function">(<span class="hljs-params">prev, current, index</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> shouldComma = (index + <span class="hljs-number">1</span>) % <span class="hljs-number">3</span> === <span class="hljs-number">0</span> &amp;&amp; index + <span class="hljs-number">1</span> &lt; numberString.length;
    <span class="hljs-keyword">let</span> updatedValue = <span class="hljs-string">`<span class="hljs-subst">${prev}</span><span class="hljs-subst">${current}</span>`</span>;
    <span class="hljs-keyword">if</span> ( shouldComma ) {
      updatedValue = <span class="hljs-string">`<span class="hljs-subst">${updatedValue}</span>,`</span>;
    }
    <span class="hljs-keyword">return</span> updatedValue;
  }, <span class="hljs-string">''</span>);

  numberString = numberString.split(<span class="hljs-string">''</span>);
  numberString.reverse()
  numberString = numberString.join(<span class="hljs-string">''</span>);

  <span class="hljs-keyword">return</span> numberString;
}
</code></pre>
<p>This function will take a number and turn it into a string with commas. To walk through what it does:</p>
<ul>
<li>Takes in a value as an argument. For our use, this value will most likely be a number.</li>
<li>It converts the value into a string. We'll use this to work with adding commas to our number.</li>
<li>We split that string into an array and reverse it. We want to reverse it because it makes it easier to add our commas depending on the index.</li>
<li>We use the javascript <code>reduce</code> function to recreate our number-string. After every 3 numbers, we want to add a comma.</li>
<li>Once we have our new value with the commas, we want to re-reverse it. So we split it again, reverse the array of characters, and re-join it, which is what we return</li>
</ul>
<p>And now that we have our <code>commafy</code> function, let's use it. Back inside <code>src/pages/index.js</code>, let's import our function at the top of the page:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { commafy } <span class="hljs-keyword">from</span> <span class="hljs-string">'lib/util'</span>;
</code></pre>
<p>Then, in our <code>dashboardStats</code> array, let's replace every number value with a ternary expression and function that will convert our number if it's available:</p>
<pre><code class="lang-js">value: stats ? commafy(stats?.cases) : <span class="hljs-string">'-'</span>
</code></pre>
<p>This line checks to see if <code>stats</code> exists. If it does, we <code>commafy</code> the <code>cases</code> value. If it doesn't exist, we return a <code>-</code> to show it's unavailable.</p>
<p>Once we repeat that process for all of our numbers, we can save, reload the page, and see our human friendly numbers!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/coronavirus-dashboard-stats-with-readable-stats.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Formatting the statistics to be human readable</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/90f266c17815239d9d3356d9b9d660915fdc26c2">Follow along with the commit!</a></p>
<h2 id="heading-step-4-add-the-last-updated-date">Step 4: Add the Last Updated date</h2>
<p>Finally, we want to make sure people are staying informed and understand the last time this data was updated. Luckily, our API provides a Last Updated date for us, so let's use it!</p>
<p>At the bottom of our "tracker" <code>div</code> under <code>tracker-stats</code>, let's add the following:</p>
<pre><code class="lang-jsx">&lt;div className=<span class="hljs-string">"tracker-last-updated"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    Last Updated: { stats?.updated }
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
&lt;/div&gt;
</code></pre>
<p>This creates a new section where we simply include the <code>updated</code> property from our stats. And if we save and reload the page, we can see the last updated date!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/coronvirus-dashboard-last-updated.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding last updated to the dashboard</em></p>
<p>But how could we even understand what that number is, unless you're the computer crawling this blog post? So let's change it to a human readable format like we did with our numbers.</p>
<p>Inside of our <code>src/lib/util.js</code> file, let's add another function:</p>
<pre><code class="lang-js"><span class="hljs-comment">/**
 * friendlyDate
 * <span class="hljs-doctag">@description </span>Takes in a date value and returns a friendly version
 */</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">friendlyDate</span>(<span class="hljs-params">value</span>) </span>{
  <span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(value);
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.DateTimeFormat(<span class="hljs-string">'en'</span>, {
    <span class="hljs-attr">year</span>: <span class="hljs-string">'numeric'</span>,
    <span class="hljs-attr">month</span>: <span class="hljs-string">'short'</span>,
    <span class="hljs-attr">day</span>: <span class="hljs-string">'2-digit'</span>,
    <span class="hljs-attr">hour</span>: <span class="hljs-string">'numeric'</span>,
    <span class="hljs-attr">minute</span>: <span class="hljs-string">'numeric'</span>
  }).format(date);
}
</code></pre>
<p>This function creates a new <code>Date</code> object, then uses the javascript <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat">International DateTimeFormat API</a> to convert it into a friendly readable format!</p>
<p>Once that's saved, let's import it next to our <code>commafy</code> function at the top of <code>src/pages/index.js</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { commafy, friendlyDate } <span class="hljs-keyword">from</span> <span class="hljs-string">'lib/util'</span>;
</code></pre>
<p>Then we can update our code similar to how we updated our numbers:</p>
<pre><code class="lang-jsx">Last Updated: { stats ? friendlyDate(stats?.updated) : <span class="hljs-string">'-'</span> }
</code></pre>
<p>And if we save and reload, we see it in a human readable way!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/coronvirus-dashboard-last-updated-formatted-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Formatting the last updated date</em></p>
<p>Finally for our "last updated" should look like it fits in with the rest of the dashboard, so let's add a few more styles. Inside of our <code>_tracker.scss</code> file we were working with earlier:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.tracker-last-updated</span> {

  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-variable">$blue-grey-900</span>;
  <span class="hljs-attribute">padding</span>: .<span class="hljs-number">8em</span> <span class="hljs-number">0</span>;

  <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-variable">$blue-grey-300</span>;
    <span class="hljs-attribute">font-size</span>: .<span class="hljs-number">8em</span>;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  }

}
</code></pre>
<p>And once we hit save and refresh the browser, we have our dashboard statistics with the last updated time! ?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/coronavirus-dashboard-formatted-styled.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Final dashboard with formatted lasted updated date</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/408286aecb32223c8782eb1539f5563135c75dfb">Follow along with the commit!</a></p>
<h2 id="heading-what-can-i-do-next">What can I do next?</h2>
<h3 id="heading-make-the-marker-tooltip-data-human-friendly">Make the marker tooltip data human friendly</h3>
<p>Now that we have our handy <code>commafy</code> and <code>friendlyDate</code> functions, we can reuse those functions to clean up the data in our country marker popups!</p>
<h3 id="heading-use-the-fetchtracker-function-to-poll-for-updates">Use the fetchTracker function to poll for updates</h3>
<p>Inside of the <code>useTracker</code> hook we created, we exported a function called <code>fetchTracker</code>. This allows us to force a request to the API to fetch new data. To make sure our map stays current even when somebody doesn't refresh the page, we can create a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">timer</a> in javascript to regularly invoke that function to update our dashboard data.</p>
<h3 id="heading-clear-the-map-layers-before-re-adding-the-new-ones">Clear the map layers before re-adding the new ones</h3>
<p>One thing we're currently not doing is cleaning up old layers before adding a new one. The way the map is set up, it just keeps layering them on top. What we can do is before we add all of our new layers, we can clear out the old ones. <a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/cad3b5a6e31a6ae090549c12e40a08fee4db4aa5">Check out this commit</a> to get started!</p>
<h2 id="heading-want-to-learn-more-about-maps">Want to learn more about maps?</h2>
<p>You can check out a few of my other resources to get started:</p>
<ul>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet">How to create a Coronavirus (COVID-19) Dashboard &amp; Map App in React with Gatsby and Leaflet</a> (Part 1 of this post)</li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/04/how-to-set-up-a-custom-mapbox-basemap-style-with-react-leaflet-and-leaflet-gatsby-starter/">How to set up a custom Mapbox basemap style with React Leaflet and Leaflet Gatsby Starter</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping">Anyone Can Map! Inspiration and an introduction to the world of mapping</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-summer-road-trip-mapping-app-with-gatsby-and-leaflet">How to Create a Summer Road Trip Mapping App with Gatsby and Leaflet</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2019/12/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/">How to Create your own Santa Tracker with Gatsby and React Leaflet</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">How to build a mapping app in React the easy way with Leaflet</a></li>
</ul>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="600" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to set up a custom Mapbox basemap style with React Leaflet and Leaflet Gatsby Starter ]]>
                </title>
                <description>
                    <![CDATA[ Building maps can be pretty powerful, but often you’re stuck with open source options for the map imagery that might not help the readability of your data. How can we leverage Mapbox’s tile APIs to add a custom basemap to our React Leaflet app? What... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-a-custom-mapbox-basemap-with-gatsby-and-react-leaflet/</link>
                <guid isPermaLink="false">66b8e36647c23b7ae1ad0bdb</guid>
                
                    <category>
                        <![CDATA[ create-react-app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ front end ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JAMstack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mapbox ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mapping ]]>
                    </category>
                
                    <category>
                        <![CDATA[ maps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react-leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Wed, 08 Apr 2020 14:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-basemap-react-leaflet-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building maps can be pretty powerful, but often you’re stuck with open source options for the map imagery that might not help the readability of your data. How can we leverage Mapbox’s tile APIs to add a custom basemap to our React Leaflet app?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What are we going to build?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-mapbox">What is Mapbox?</a></li>
<li><a class="post-section-overview" href="#heading-part-1-creating-a-custom-mapbox-style">Part 1: Creating a custom Mapbox style</a></li>
<li><a class="post-section-overview" href="#heading-part-2-adding-a-custom-tilelayer-to-react-leaflet">Part 2: Adding a custom TileLayer to React Leaflet</a></li>
<li><a class="post-section-overview" href="#heading-part-3-adding-a-custom-basemap-to-gatsby-starter-leaflet">Part 3: Adding a custom basemap to Gatsby Starter Leaflet</a></li>
<li><a class="post-section-overview" href="#heading-securing-your-mapbox-key">Securing your Mapbox key</a></li>
<li><a class="post-section-overview" href="#heading-want-to-learn-more-about-maps">Want to learn more about maps?</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/KcPJr1b_rv0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We’re going to walk through creating a new basic <a target="_blank" href="https://www.mapbox.com/mapbox-studio/">Mapbox style</a> in our <a target="_blank" href="https://www.mapbox.com/">Mapbox</a> account. Once created, we’re going to use their <a target="_blank" href="https://docs.mapbox.com/api/maps/">Map API</a> to add a custom basemap to our <a target="_blank" href="https://react-leaflet.js.org/">React Leaflet</a> app.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/gatsby-starter-leaflet-with-mapbox-tilelayer.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Gatsby Starter Leaflet with Mapbox basemap</em></p>
<p>For our map, we’re going to use this <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">Leaflet Gatsby Starter</a> I created that will allow you to easily spin up a new mapping app. Before we spin that up though, I’ll walk you through how to add it using only React Leaflet components.</p>
<h2 id="heading-a-mapping-app">A mapping app?</h2>
<p>Yup! Maps are used all around the world to study datasets for geographic locations. They're important tools for scientists and others that are trying to help the world.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/coronavirus-map-dashboard-demo.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Coronavirus (COVID-19) custom map</em></p>
<p>If you want to learn more about building a map and adding data to it, you can check out some of <a target="_blank" href="https://www.freecodecamp.org/news/author/colbyfayock/">my other articles</a> first such as creating a <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-a-coronavirus-covid-19-dashboard-map-app-in-react-with-gatsby-and-leaflet/">Coronavirus (COVID-19) map</a> or a <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-a-summer-road-trip-mapping-app-with-gatsby-and-leaflet/">Summer Road Trip map</a> as well as a little bit of inspiration about why <a target="_blank" href="https://www.freecodecamp.org/news/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping/">Anyone Can Map</a>.</p>
<h2 id="heading-what-is-mapbox">What is Mapbox?</h2>
<p>Mapbox is a mapping platform that allows its customers to create custom mapping solutions. They also leverage a variety of APIs that provide powerful capabilities for building map features.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-homepage.jpg" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://www.mapbox.com/">mapbox.com</a></em></p>
<p>For our purposes, we’re going to utilize their Map API, specifically their Static Tiles API, to serve a custom map style that we create.</p>
<h2 id="heading-part-1-creating-a-custom-mapbox-style">Part 1: Creating a custom Mapbox style</h2>
<p>To get the look and feel that we want for our map, it’s important to have a basemap that helps make our data present itself without distractions. Plus, sometimes it’s fun to have a custom map.</p>
<h3 id="heading-mapbox-account">Mapbox account</h3>
<p>The first thing we’ll need to set up our custom Mapbox style is to have an account. I'm not going to walk you through that process, but you can head over to <a target="_blank" href="https://www.mapbox.com/">Mapbox’s website</a> where you can sign up for free: <a target="_blank" href="https://www.mapbox.com/">mapbox.com</a></p>
<h3 id="heading-creating-a-new-custom-style">Creating a new custom style</h3>
<p>Creating a new style in Mapbox isn’t as hard as it sounds. While it can get really complex if you want something unique, we can copy one of Mapbox’s default styles to get started.</p>
<p>First, head over to Mapbox’s <a target="_blank" href="https://studio.mapbox.com/">Studio dashboard</a> by clicking your account link in the top right corner when logged in.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-studio.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Mapbox Studio</em></p>
<p>Once we’re on our Studio dashboard, we want to select the New Style button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-studio-new-style.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Create a new style in Mapbox Studio</em></p>
<p>After clicking the button, a modal will pop up allowing you to choose a template. You can choose whatever you want here, but I’m going to choose Monochrome with a variation of Dark. And after you’ve selected your template, click the Customize button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-studio-new-style-choose-template.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Select and customize a template for a new style in Mapbox Studio</em></p>
<p>And now we’re dropped into our customization UI.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-customize-style.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Mapbox customize style UI</em></p>
<p>From here, you can really do what you’d like. There are a ton of options to customize your map. It’s a little complex to try to dig in here, but <a target="_blank" href="https://docs.mapbox.com/studio-manual/overview/">Mapbox provides some resources</a> to try to help you get productive.</p>
<h3 id="heading-generating-a-mapbox-token">Generating a Mapbox token</h3>
<p>Once you’re happy with your new style and everything’s published, we want to generate a token that we’ll use for providing access to our Map.</p>
<p>Head on over to the Account section of the Mapbox dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-account.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new token in Mapbox</em></p>
<p>Mapbox provides you with a “default” token that you can use in your applications. You're free to use this, but I recommend creating a new token that you can provide a unique name, that way if you ever blow past the <a target="_blank" href="https://www.mapbox.com/pricing/">free tier</a> of Mapbox, you’ll be able to track your usage.</p>
<p>Additionally, it’s best to keep a separate token for each application, that way you can easily rotate an individual key, without having to update every application using it.</p>
<p>Once you click Create a token, you can set up the key how you’d like, with the scopes and permissions you choose, but for our purposes, you can leave all of the Public scopes checked for our map, which they do by default.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-create-token.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Create a new access token in Mapbox</em></p>
<h3 id="heading-configuring-our-custom-endpoint">Configuring our custom endpoint</h3>
<p>For this tutorial, we’re going to use <a target="_blank" href="https://docs.mapbox.com/api/maps/#static-tiles">Mapbox’s Static Tiles service</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-static-tiles-map-api.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Mapbox Static Tiles Maps API</em></p>
<p>Our endpoint will look like the following:</p>
<pre><code>https:<span class="hljs-comment">//api.mapbox.com/styles/v1/{username}/{style_id}/tiles/256/{z}/{x}/{y}@2x?access_token={access_token}</span>
</code></pre><p>There are a few parameters here we need to understand:</p>
<ul>
<li>username: this will be your Mapbox account’s username</li>
<li>style_id: this will be the ID of the style you created before</li>
<li>z, x, y: these are parameters that Leaflet programmatically swaps out, so we want to leave them as is</li>
<li>access_token: this is the Mapbox key you created above</li>
</ul>
<p>To find your username and style ID, we can use the Style URL for our new Mapbox style to get those values.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mapbox-studio-style-url.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Finding the Style URL in Mapbox Studio</em></p>
<p>In my example, my Style URL looks like:</p>
<pre><code>mapbox:<span class="hljs-comment">//styles/colbyfayock/ck8lryjfq0jdo1ip9ctmuhc6p</span>
</code></pre><p><code>colbyfayock</code> is my username and <code>ck8lryjfq0jdo1ip9ctmuhc6p</code> is my style ID.</p>
<p>And once I update my endpoint parameters, the final tilepoint URL will look like:</p>
<pre><code>https:<span class="hljs-comment">//api.mapbox.com/styles/v1/colbyfayock/ck8lryjfq0jdo1ip9ctmuhc6p/tiles/256/{z}/{x}/{y}@2x?access_token=MYACCESSTOKEN</span>
</code></pre><h2 id="heading-part-2-adding-a-custom-tilelayer-to-react-leaflet">Part 2: Adding a custom TileLayer to React Leaflet</h2>
<p>When building a map with React Leaflet, your main component will be a <code>&lt;Map&gt;</code> that wraps the entirety of the app. This is what sets up your <a target="_blank" href="https://leafletjs.com/reference-1.6.0.html#map-example">Map instance</a> for <a target="_blank" href="https://leafletjs.com/">Leaflet</a>.</p>
<p>For our purposes here, we’re going to use the example on the <a target="_blank" href="https://react-leaflet.js.org/">React Leaflet homepage</a> as our starting point.</p>
<h3 id="heading-react-leaflet-tilelayer-component">React Leaflet TileLayer Component</h3>
<p>Inside of your <code>&lt;Map&gt;</code> component you include a <code>&lt;TileLayer&gt;</code> component, which defines the imagery of the world that you base your map upon.</p>
<p>The example on the React Leaflet homepage uses a public version of <a target="_blank" href="https://www.openstreetmap.org/">OpenStreetMap</a> as their TileLayer, which is an open source map project created and updated by people all around the world.</p>
<pre><code class="lang-react">&lt;Map center={position} zoom={13}&gt;
  &lt;TileLayer
    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    attribution="&amp;copy; &lt;a href=&amp;quot;http://osm.org/copyright&amp;quot;&gt;OpenStreetMap&lt;/a&gt; contributors"
  /&gt;
&lt;/Map&gt;
</code></pre>
<p>This gives you a basic map, but we want to swap in Mapbox so we can set up a custom look and feel for our map.</p>
<h3 id="heading-custom-mapbox-tilelayer">Custom Mapbox TileLayer</h3>
<p>To add our custom style, we’ll want to update the <code>url</code> and <code>attribution</code> props of the <code>TileLayer</code> component.</p>
<p>For URL, it will simply be the custom style endpoint we created earlier, so in my example, it looks like:</p>
<pre><code>https:<span class="hljs-comment">//api.mapbox.com/styles/v1/colbyfayock/ck8lryjfq0jdo1ip9ctmuhc6p/tiles/256/{z}/{x}/{y}@2x?access_token=MYACCESSTOKEN</span>
</code></pre><p>For attribution, we want to credit Mapbox as the service, so we want to set our attribution as:</p>
<pre><code><span class="hljs-built_in">Map</span> data &amp;copy; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&amp;quot;https://www.openstreetmap.org/&amp;quot;</span>&gt;</span>OpenStreetMap<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span> contributors, <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&amp;quot;https://creativecommons.org/licenses/by-sa/2.0/&amp;quot;</span>&gt;</span>CC-BY-SA<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>, Imagery &amp;copy; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&amp;quot;https://www.mapbox.com/&amp;quot;</span>&gt;</span>Mapbox<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>
</code></pre><p>When plugged in to our <code>TileLayer</code>, our code should now look like this:</p>
<pre><code class="lang-react">&lt;Map center={position} zoom={13}&gt;
  &lt;TileLayer
    url="https://api.mapbox.com/styles/v1/colbyfayock/ck8lryjfq0jdo1ip9ctmuhc6p/tiles/256/{z}/{x}/{y}@2x?access_token=MYACCESSTOKEN"
    attribution="Map data &amp;copy; &lt;a href=&amp;quot;https://www.openstreetmap.org/&amp;quot;&gt;OpenStreetMap&lt;/a&gt; contributors, &lt;a href=&amp;quot;https://creativecommons.org/licenses/by-sa/2.0/&amp;quot;&gt;CC-BY-SA&lt;/a&gt;, Imagery &amp;copy; &lt;a href=&amp;quot;https://www.mapbox.com/&amp;quot;&gt;Mapbox&lt;/a&gt;"
  /&gt;
&lt;/Map&gt;
</code></pre>
<p>And once we open up our map, we should see our new basemap!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/react-leaflet-mapbox-basemap.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>React Leaflet with a Mapbox basemap</em></p>
<h3 id="heading-see-the-code">See the code!</h3>
<p>If you want to see how I did it, <a target="_blank" href="https://github.com/colbyfayock/my-mapbox-react-leaflet/commits/master">check out the diff commit by commit</a>.</p>
<p>The only caveat there is I created an <code>.env.development.local</code> file in the root of my project in which I stored a new environment variable called <code>REACT_APP_MAPBOX_KEY</code>  to store my Mapbox key.</p>
<h2 id="heading-part-3-adding-a-custom-basemap-to-gatsby-starter-leaflet">Part 3: Adding a custom basemap to Gatsby Starter Leaflet</h2>
<p>I’ve written <a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet">a few</a> <a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-summer-road-trip-mapping-app-with-gatsby-and-leaflet">other</a> <a target="_blank" href="https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping/">articles</a> on <a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">how to get started</a> with my <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">Leaflet Gatsby Starter</a>, but for this part, we’ll want to have a basic app spun up that we can use to change our <code>TileLayer</code> endpoint.</p>
<h3 id="heading-setting-up-our-react-leaflet-gatsby-app">Setting up our React Leaflet Gatsby app</h3>
<p>To get started, check out the instructions on the Starter github:</p>
<p><a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">https://github.com/colbyfayock/gatsby-starter-leaflet</a></p>
<p>Once you’re ready, you should have a basic mapping app ready to go!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/gatsby-starter-leaflet-in-browser.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New Leaflet Gatsby app in the browser</em></p>
<h3 id="heading-configuring-our-mapbox-service">Configuring our Mapbox service</h3>
<p>The first thing we’ll want to do is add Mapbox as a service in our <code>src/data/map-services.js</code> file.</p>
<p>Taking our custom endpoint URL that we created in Part 1, let’s set up a new object with a name of Mapbox, and with a url and attribution similar to what we did in Part 2.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> mapServices = [
  {
    <span class="hljs-attr">name</span>: ‘OpenStreetMap’,
    <span class="hljs-attr">attribution</span>: <span class="hljs-string">'&amp;copy; &lt;a href="http://osm.org/copyright”&gt;OpenStreetMap&lt;/a&gt; contributors’,
    url: ‘https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png’
  },
  {
    name: ‘Mapbox’,
    attribution: ‘Map data &amp;copy; &lt;a href=&amp;quot;https://www.openstreetmap.org/&amp;quot;&gt;OpenStreetMap&lt;/a&gt; contributors, &lt;a href=&amp;quot;https://creativecommons.org/licenses/by-sa/2.0/&amp;quot;&gt;CC-BY-SA&lt;/a&gt;, Imagery &amp;copy; &lt;a href=&amp;quot;https://www.mapbox.com/&amp;quot;&gt;Mapbox&lt;/a&gt;’,
    url: `https://api.mapbox.com/styles/v1/colbyfayock/ck8c2foj72lqk1jnug0g2haw0/tiles/256/{z}/{x}/{y}@2x?access_token=MY_ACCESS_TOKEN`
  }
];</span>
</code></pre>
<h3 id="heading-using-our-mapbox-map-service">Using our Mapbox map service</h3>
<p>Once you have your Mapbox service set up, all that’s left is to open up the <code>src/pages/index.js</code> file, find the <code>mapSettings</code> object definition, and update the <code>defaultBaseMap</code> property to <code>Mapbox</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> mapSettings = {
  <span class="hljs-attr">center</span>: CENTER,
  <span class="hljs-attr">defaultBaseMap</span>: ‘Mapbox’,
  <span class="hljs-attr">zoom</span>: DEFAULT_ZOOM,
  mapEffect
};
</code></pre>
<p>Save that change, refresh the map in your browser, and you should now see your custom basemap style!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/gatsby-starter-leaflet-with-mapbox-tilelayer-in-browser.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Gatsby Starter Leaflet with custom Mapbox basemap in browser</em></p>
<h3 id="heading-see-the-code-1">See the code!</h3>
<p>If you want to see how I did it, <a target="_blank" href="https://github.com/colbyfayock/my-mapbox-gatsby-starter-leaflet/commit/9baa1b7003504dec5c938328ea9b54477f65ec58">check out the diff with the commit</a>.</p>
<p>The only caveat there is I created an <code>.env.development</code> file in the root of my project in which I stored a new environment variable called <code>GATSBY_MAPBOX_KEY</code>  to store my Mapbox key.</p>
<h2 id="heading-securing-your-mapbox-key">Securing your Mapbox key</h2>
<h3 id="heading-environment-variables">Environment variables</h3>
<p>Part of most development processes that use individual keys will generally set the keys up as environment variables. Environment variables are configured settings that don’t live in the code itself.</p>
<p>This is important because it prevents your key from being checked in to your code, which is bad from a security perspective, but it also allows you to provide a different key for different environments.</p>
<p>When generating your keys, try to keep this in mind, as it can save you in the long run.</p>
<h3 id="heading-locking-down-your-mapbox-key">Locking down your Mapbox key</h3>
<p>In your settings when creating a token or when editing a token, Mapbox allows you to specify only the URLs you want your key to be accessible from.</p>
<p>Though Mapbox has a generous free tier, you probably want to keep it locked down only to the URLs that you’re using it on. You can create multiple keys, where one could be for public use on your website and one would be for your local development.</p>
<p>This is helpful for instance, where you have a key that will never be used publicly for development purposes, but then you have a key that you deploy with, which can be locked down only to that URL.</p>
<p>If someone grabs your key, they could plug it into their own website and use up all of your free tier, potentially running up your bill!</p>
<h2 id="heading-want-to-learn-more-about-maps">Want to learn more about maps?</h2>
<p>You can check out a few of my other resources to get started:</p>
<ul>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet">How to create a Coronavirus (COVID-19) Dashboard &amp; Map App in React with Gatsby and Leaflet</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping">Anyone Can Map! Inspiration and an introduction to the world of mapping</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-summer-road-trip-mapping-app-with-gatsby-and-leaflet">How to Create a Summer Road Trip Mapping App with Gatsby and Leaflet</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2019/12/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/">How to Create your own Santa Tracker with Gatsby and React Leaflet</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">How to build a mapping app in React the easy way with Leaflet</a></li>
</ul>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="600" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to create a Coronavirus (COVID-19) Dashboard & Map App in React with Gatsby and Leaflet ]]>
                </title>
                <description>
                    <![CDATA[ The Coronavirus (COVID-19) pandemic has swiftly changed how all of us interact day to day. How can we use available APIs to build a mapping app that shows the impact it has had on the world? Update: The original NovelCOVID API v1 endpoint has been de... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-coronavirus-covid-19-dashboard-map-app-in-react-with-gatsby-and-leaflet/</link>
                <guid isPermaLink="false">66bee92588139a9746c0c5cc</guid>
                
                    <category>
                        <![CDATA[ coronavirus ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Covid-19 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ data analytics ]]>
                    </category>
                
                    <category>
                        <![CDATA[ front end ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mapping ]]>
                    </category>
                
                    <category>
                        <![CDATA[ maps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react-leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Tue, 31 Mar 2020 15:16:16 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/coronavirus-mapping-app.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The Coronavirus (COVID-19) pandemic has swiftly changed how all of us interact day to day. How can we use available APIs to build a mapping app that shows the impact it has had on the world?</p>
<p><strong>Update:</strong> The original NovelCOVID API v1 endpoint has been deprecated. Please update and use the following instead: <a target="_blank" href="https://corona.lmao.ninja/v2/countries">https://corona.lmao.ninja/v2/countries</a></p>
<p><em>Author’s Note: This is meant to be a demo and proof of concept for putting together an impactful mapping application using real life data. For complete and accurate analysis, please make sure to use tools like <a target="_blank" href="https://www.arcgis.com/apps/opsdashboard/index.html#/bda7594740fd40299423467b48e9ecf6">Johns Hopkins University dashboard</a>. Stay home and be safe! ❤️</em></p>
<ul>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What are we going to build?</a></li>
<li><a class="post-section-overview" href="#heading-what-do-we-need-before-we-get-started">What do we need before we get started?</a></li>
<li><a class="post-section-overview" href="#heading-step-1-cleaning-up-some-unneeded-code">Step 1: Cleaning up some unneeded code</a></li>
<li><a class="post-section-overview" href="#heading-step-2-fetching-the-coronavirus-data">Step 2: Fetching the Coronavirus data</a></li>
<li><a class="post-section-overview" href="#heading-step-3-transform-the-coronavirus-data-into-a-geographic-data-format">Step 3: Transform the Coronavirus data into a geographic data format</a></li>
<li><a class="post-section-overview" href="#heading-step-4-adding-the-coronavirus-data-to-the-map">Step 4: Adding the Coronavirus data to the map</a></li>
<li><a class="post-section-overview" href="#heading-what-else-can-we-do">What else can we do?</a></li>
<li><a class="post-section-overview" href="#heading-be-safe-and-stay-informed">Be safe and stay informed</a></li>
<li><a class="post-section-overview" href="#heading-want-to-learn-more-about-maps">Want to learn more about maps?</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/GryBIsfBfro" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We’ll be putting together a mapping application that uses an API containing recent Coronavirus statistics and maps out the locations and impact each country is facing.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/coronavirus-map-dashboard-demo.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Coronavirus map dashboard demo</em></p>
<p>On the map, we’ll show a marker for each country with the number of confirmed cases. On top of that, we’ll include a little popup tooltip that shows more in depth information.</p>
<p>The map we’ll build will mostly look like the above, but will look a little simpler. We’ll utilize the OpenStreetMap public tileserver instead of using a custom <a target="_blank" href="https://www.mapbox.com/">Mapbox</a></p>
<p>To get started, we’re going to use this <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">Leaflet Gatsby Starter</a> I created to make the initial setup a little smoother. With our app bootstrapped, we’ll fetch our data and add markers to the map with our data.</p>
<h2 id="heading-woah-a-mapping-app">Woah, a mapping app?</h2>
<p>Yup. If you haven’t played with maps before, don’t be discouraged! It's not as bad as you probably think. If you’d rather start with mapping basics, you can <a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">read more about how mapping works</a> first.</p>
<h2 id="heading-what-do-we-need-before-we-get-started">What do we need before we get started?</h2>
<p>If you followed along with my previous tutorials for <a target="_blank" href="https://www.freecodecamp.org/news/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/">building a Santa Tracker</a> or <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-a-summer-road-trip-mapping-app-with-gatsby-and-leaflet/">creating a Summer Road Trip map</a>, you can follow the same steps to get started. If not, we’ll want to make sure we have the following set up:</p>
<ul>
<li><a target="_blank" href="https://nodejs.org/en/">node</a> or <a target="_blank" href="https://yarnpkg.com/en/">yarn</a> - I'll be using yarn, but you can substitute with npm where appropriate</li>
<li><a target="_blank" href="https://www.gatsbyjs.org/docs/gatsby-cli/">Gatsby’s CLI</a> - <code>yarn global add gatsby-cli</code></li>
</ul>
<p>If you’re not sure about one of the above, you can try checking out the beginning <a target="_blank" href="https://www.freecodecamp.org/news/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/">my previous tutorial</a>.</p>
<p>We’ll also want to set up a foundation for our map. We can do this by utilizing the Leaflet Gatsby Starter I put together that provides us a basic setup with <a target="_blank" href="https://leafletjs.com/">Leaflet</a> and <a target="_blank" href="https://react-leaflet.js.org/">React Leaflet</a>.</p>
<pre><code class="lang-shell">gatsby new my-coronavirus-map https://github.com/colbyfayock/gatsby-starter-leaflet
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/terminal-creating-new-coronavirus-map-from-gatsby-starter.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new Leaflet Gatsby app in the terminal</em></p>
<p>After that’s finished running, you can navigate to the newly created project directory and start your local development server:</p>
<pre><code class="lang-shell">cd my-coronavirus-map
yarn develop
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/terminal-starting-gatsby-development-server-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Starting your Gatsby app in the terminal</em></p>
<p>If all goes as planned, your server should start and you should now be able to see your basic mapping app in your browser!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/gatsby-starter-leaflet-in-browser-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New Leaflet Gatsby app in the browser</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commits/master">Follow along with the commit!</a></p>
<h2 id="heading-step-1-cleaning-up-some-unneeded-code">Step 1: Cleaning up some unneeded code</h2>
<p>The Gatsby Starter we're using to spin up this app comes with some demo code that we don’t need here. We’ll want to make all of the changes below in the file <code>src/pages/index.js</code>, which is the homepage of our app.</p>
<p>First, let’s remove everything from the <code>mapEffect</code> function. This function is used to run code that fires when the map renders.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// In src/pages/index.js</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mapEffect</span>(<span class="hljs-params">{ leafletElement } = {}</span>) </span>{
  <span class="hljs-comment">// Get rid of everything in here</span>
}
</code></pre>
<p>We’ll also change the variable name of our <code>leafletElement</code> simply for being able to more easily understand the code as we write it.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mapEffect</span>(<span class="hljs-params">{ leafletElement: map } = {}</span>) </span>{
}
</code></pre>
<p>Next, we don’t want a marker this time, so let’s remove the <code>&lt;Marker</code> component from our <code>&lt;Map</code> component:</p>
<pre><code class="lang-react">&lt;Map {...mapSettings} /&gt;
</code></pre>
<p>Now that we have those pieces cleared out, we can remove all of the following imports and variables from the top of our file:</p>
<ul>
<li>useRef</li>
<li>Marker</li>
<li>promiseToFlyTo</li>
<li>getCurrentLocation</li>
<li>gatsby_astronaut</li>
<li>timeToZoom</li>
<li>timeToOpenPopupAfterZoom</li>
<li>timeToUpdatePopupAfterZoom</li>
<li>ZOOM</li>
<li>popupContentHello</li>
<li>popupContentGatsby</li>
<li>markerRef</li>
</ul>
<p>After, our map should still work, but not do anything.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/new-empty-mapping-app-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New mapping app with nothing going on</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/a3e9cff3949bb7ebb7cc89166c875e97b6dcb5a8">Follow along with the commit!</a></p>
<h2 id="heading-step-2-fetching-the-coronavirus-data">Step 2: Fetching the Coronavirus data</h2>
<p>For our app, we’re going to use the <a target="_blank" href="https://github.com/NovelCOVID/API">NovelCOVID API</a>. Particularly, we’re going to use the <a target="_blank" href="https://corona.lmao.ninja/countries">countries endpoint</a> to fetch the list of our countries and the stats associated with them.</p>
<p>For making requests, I personally like to use <a target="_blank" href="https://github.com/axios/axios">axios</a> as it has a nice to use API. If you want to use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">fetch</a> or your own favorite request library, substitute that in for this step.</p>
<p>We’ll start by installing axios:</p>
<pre><code class="lang-shell">yarn add axios
</code></pre>
<p>Once that installs, remember to restart your server.</p>
<p>Import the axios package ta the top of our <code>pages/index.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;
</code></pre>
<p>Next we’ll actually make our request. Inside our <code>mapEffect</code> function, let’s try to make a request to the API endpoint:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mapEffect</span>(<span class="hljs-params">{ leafletElement: map } = {}</span>) </span>{
    <span class="hljs-keyword">let</span> response;

    <span class="hljs-keyword">try</span> {
      response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'https://corona.lmao.ninja/v2/countries'</span>);
    } <span class="hljs-keyword">catch</span>(e) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Failed to fetch countries: <span class="hljs-subst">${e.message}</span>`</span>, e);
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> { data = [] } = response;
}
</code></pre>
<p>In this snippet, we’re doing the following:</p>
<ul>
<li>Setting up a <code>response</code> variable that will allow us to store the response</li>
<li>Adding a <code>try/catch</code> block that will catch any API errors if the request fails</li>
<li>If the request is successful, we store the response in the <code>response</code> variable</li>
<li>If the request fails, we console log out the error and return out of the function so we don’t continue to run the code with a failed request</li>
<li>Once we have our response, we can destructure <code>data</code> from the response and set the default value to an empty array, as that will be the type of data we need</li>
</ul>
<p>After that’s set up, we can console log out the <code>data</code> object and we’ll see our data successfully fetched!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/coronavirus-location-data-in-browser.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Logging the Coronavirus location data to the browser console</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/86bebfee4a34b9bad516879b228921cdaad55126">Follow along with the commit!</a></p>
<p><strong>Update:</strong> The previous commit includes a link to the original NovelCOVID v1 API endpoint which has now been deprecated. Please use this instead: <a target="_blank" href="https://corona.lmao.ninja/v2/countries">https://corona.lmao.ninja/v2/countries</a>.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/e8f63c7ca60ec358b2edc9bc3ed8935be85b5573">See updated commit</a>.</p>
<h2 id="heading-step-3-transform-the-coronavirus-data-into-a-geographic-data-format">Step 3: Transform the Coronavirus data into a geographic data format</h2>
<p>Now that we have our data, we can transform it into a geographic data format, particularly <a target="_blank" href="https://geojson.org/">GeoJSON</a>, that will allow us to interface with Leaflet.</p>
<p>Let’s start by adding this block of code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { data = [] } = response;
<span class="hljs-keyword">const</span> hasData = <span class="hljs-built_in">Array</span>.isArray(data) &amp;&amp; data.length &gt; <span class="hljs-number">0</span>;

<span class="hljs-keyword">if</span> ( !hasData ) <span class="hljs-keyword">return</span>;

<span class="hljs-keyword">const</span> geoJson = {
  <span class="hljs-attr">type</span>: <span class="hljs-string">'FeatureCollection'</span>,
  <span class="hljs-attr">features</span>: data.map(<span class="hljs-function">(<span class="hljs-params">country = {}</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { countryInfo = {} } = country;
    <span class="hljs-keyword">const</span> { lat, <span class="hljs-attr">long</span>: lng } = countryInfo;
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">type</span>: <span class="hljs-string">'Feature'</span>,
      <span class="hljs-attr">properties</span>: {
       ...country,
      },
      <span class="hljs-attr">geometry</span>: {
        <span class="hljs-attr">type</span>: <span class="hljs-string">'Point'</span>,
        <span class="hljs-attr">coordinates</span>: [ lng, lat ]
      }
    }
  })
}
</code></pre>
<p>So what are we doing here?</p>
<ul>
<li>We create a new constant called <code>hasData</code> that checks if our <code>data</code> variable is an array and has data</li>
<li>If we don’t have data, we want to return out of the function, as we don’t want to try to add data we don’t have</li>
<li>We create a <code>geoJson</code> object that will be our GeoJSON document</li>
<li>Our document is of type <code>FeatureCollection</code> and as our <code>features</code> we loop through our dataset</li>
<li>For each country in our data, we obtain the <code>lat</code> and <code>lng</code> to create a point for our map</li>
<li>We additionally add our country data as properties so we can access it within our mapping APIs</li>
</ul>
<p>If you <code>console.log</code> this object our into your browser and copy the contents, you can paste this into geojson.io and see the location data show up correctly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/location-data-geojson-io.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Previewing Coronavirus location data on geojson.io</em></p>
<p>With this GeoJSON document, we'll now be able to add it to our map.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/f0da2d05cbc16783322684da7a3efaa61022f5b6">Follow along with the commit!</a></p>
<h2 id="heading-step-4-adding-the-coronavirus-data-to-the-map">Step 4: Adding the Coronavirus data to the map</h2>
<p>We have our GeoJSON document with our location data, so let’s add it to the map.</p>
<p>Let’s start with this code block. It's a long one, but we’ll break it down piece by piece:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> geoJsonLayers = <span class="hljs-keyword">new</span> L.GeoJSON(geoJson, {
  <span class="hljs-attr">pointToLayer</span>: <span class="hljs-function">(<span class="hljs-params">feature = {}, latlng</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { properties = {} } = feature;
    <span class="hljs-keyword">let</span> updatedFormatted;
    <span class="hljs-keyword">let</span> casesString;

    <span class="hljs-keyword">const</span> {
      country,
      updated,
      cases,
      deaths,
      recovered
    } = properties

    casesString = <span class="hljs-string">`<span class="hljs-subst">${cases}</span>`</span>;

    <span class="hljs-keyword">if</span> ( cases &gt; <span class="hljs-number">1000</span> ) {
      casesString = <span class="hljs-string">`<span class="hljs-subst">${casesString.slice(<span class="hljs-number">0</span>, <span class="hljs-number">-3</span>)}</span>k+`</span>
    }

    <span class="hljs-keyword">if</span> ( updated ) {
      updatedFormatted = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(updated).toLocaleString();
    }

    <span class="hljs-keyword">const</span> html = <span class="hljs-string">`
      &lt;span class="icon-marker"&gt;
        &lt;span class="icon-marker-tooltip"&gt;
          &lt;h2&gt;<span class="hljs-subst">${country}</span>&lt;/h2&gt;
          &lt;ul&gt;
            &lt;li&gt;&lt;strong&gt;Confirmed:&lt;/strong&gt; <span class="hljs-subst">${cases}</span>&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Deaths:&lt;/strong&gt; <span class="hljs-subst">${deaths}</span>&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Recovered:&lt;/strong&gt; <span class="hljs-subst">${recovered}</span>&lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;Last Update:&lt;/strong&gt; <span class="hljs-subst">${updatedFormatted}</span>&lt;/li&gt;
          &lt;/ul&gt;
        &lt;/span&gt;
        <span class="hljs-subst">${ casesString }</span>
      &lt;/span&gt;
    `</span>;

    <span class="hljs-keyword">return</span> L.marker( latlng, {
      <span class="hljs-attr">icon</span>: L.divIcon({
        <span class="hljs-attr">className</span>: <span class="hljs-string">'icon'</span>,
        html
      }),
      <span class="hljs-attr">riseOnHover</span>: <span class="hljs-literal">true</span>
    });
  }
});
</code></pre>
<p>So what are we doing here?</p>
<ul>
<li>We create a new instance of <code>L.GeoJSON</code> which will transform our GeoJSON document into something Leaflet will understand</li>
<li>Inside that instance, we define a custom <code>pointToLayer</code> function. This allows us to customize the map layer Leaflet creates for our map</li>
<li>In our function, we assign and create our datapoints that we want. Most of it is destructuring, but we format the cases count to show <code>1k+</code> instead of <code>1000</code> and a formatted date instead of the timestamp</li>
<li>We create an HTML string block which is used to define our map marker that will be added to the map. This also includes the HTML for the tooltip that will pop up when hovering over a marker</li>
<li>We return <code>L.marker</code> with our custom configuration that includes a class of <code>icon</code> for the container and our custom HTML.</li>
<li>Additionally, we add the <code>riseOnHover</code> property so when hoving over a marker, it surfaces itself above over the other markers on the map</li>
</ul>
<p>We also want to add a bit of CSS here so that we can make sure our markers show up in the map and are usable. Let’s add this snippet to our <code>assets/stylesheets/components/_map.scss</code> file:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.icon-marker</span> {

  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">3.6em</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">3.6em</span>;
  <span class="hljs-attribute">font-size</span>: .<span class="hljs-number">7em</span>;
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-variable">$red-800</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">5px</span> rgba(black, .<span class="hljs-number">9</span>);

  &amp;<span class="hljs-selector-pseudo">:hover</span> {

    <span class="hljs-selector-class">.icon-marker-tooltip</span> {
      <span class="hljs-attribute">display</span>: block;
    }

  }

}

<span class="hljs-selector-class">.icon-marker-tooltip</span> {

  <span class="hljs-attribute">display</span>: none;
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">16em</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.4em</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1em</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-variable">$blue-grey-900</span>;
  <span class="hljs-attribute">border-radius</span>: .<span class="hljs-number">4em</span>;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1em</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">3px</span> <span class="hljs-number">5px</span> rgba(black, .<span class="hljs-number">9</span>);

  &amp;<span class="hljs-selector-pseudo">:before</span> {
    <span class="hljs-attribute">display</span>: block;
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">bottom</span>: -.<span class="hljs-number">6em</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">content</span>: <span class="hljs-string">''</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">1.4em</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">1.4em</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-variable">$blue-grey-900</span>;
    <span class="hljs-attribute">transform</span>: rotate(<span class="hljs-number">45deg</span>);
    <span class="hljs-attribute">margin-left</span>: -.<span class="hljs-number">7em</span>;
  }

  <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5em</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.2</span>;
    <span class="hljs-attribute">margin-bottom</span>: .<span class="hljs-number">1em</span>;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0</span>;
  }

  <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2em</span>;
    <span class="hljs-attribute">margin</span>: .<span class="hljs-number">1em</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">font-weight</span>: normal;
    <span class="hljs-attribute">color</span>: <span class="hljs-variable">$blue-grey-100</span>;
  }

  <span class="hljs-selector-tag">ul</span>,
  <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">font-weight</span>: normal;
  }

  <span class="hljs-selector-tag">ul</span> {
    <span class="hljs-attribute">list-style</span>: none;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">margin</span>: .<span class="hljs-number">6em</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>;
  }

}
</code></pre>
<p>What we’re doing:</p>
<ul>
<li>We create our round markers using the <code>.icon-marker</code> class and set up our <code>.icon-marker-tooltip</code> class to show up when hovered over</li>
<li>Our <code>.icon-marker-tooltip</code> class is hidden by default, as it’s our tooltip, but we position it absolutely to appear over top of our marker and formatted the way we want it</li>
</ul>
<p>And finally, once we have our <code>geoJsonLayers</code> created with our styling added, we can add it to the map!</p>
<pre><code>geoJsonLayers.addTo(map)
</code></pre><p><img src="https://www.freecodecamp.org/news/content/images/2020/03/map-with-coronavirus-location-data.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Map with Coronavirus location data</em></p>
<p>Now you might be wondering why it doesn't appear to be centering properly. Go ahead and change the <code>LOCATION</code> variable at the top of the <code>index.js</code> file to:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> LOCATION = {
  <span class="hljs-attr">lat</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">lng</span>: <span class="hljs-number">0</span>
};
</code></pre>
<p>Once that’s set, when the page reloads, the map should be centered in the middle of the world!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/map-with-coronavirus-location-data-centered-tooltip.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Map with Coronavirus location data centered with a tooltip</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-coronavirus-map/commit/49c78e4ef3e98c974fab7bca10b6f8f7578b42c2">Follow along with the commit!</a></p>
<h2 id="heading-yay-we-did-it">Yay, we did it! ?</h2>
<p>If you followed along, you now have created your own Coronavirus map dashboard that gives some quick stats about the cases around the world.</p>
<p>Take what you learned and run with it. You can apply this to any other type of data that you can imagine.</p>
<h2 id="heading-what-else-can-we-do">What else can we do?</h2>
<h3 id="heading-add-more-styles-and-a-custom-basemap">Add more styles and a custom basemap</h3>
<p>In my original demo, I set up a custom basemap using <a target="_blank" href="https://mapbox.com/">Mapbox</a> that allows me to have a dark background making the markers easier to see.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/mapbox-studio-basemap.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new basemap in Mapbox Studio</em></p>
<p>Mapbox is great and has a nice free tier if you’re interested in getting started.</p>
<p>Once you have a Mapbox account, you can even <a target="_blank" href="https://api.mapbox.com/styles/v1/colbyfayock/ck8c2foj72lqk1jnug0g2haw0.html?fresh=true&amp;title=copy&amp;access_token=pk.eyJ1IjoiY29sYnlmYXlvY2siLCJhIjoiY2swODZzbXYxMGZzdzNjcXczczF6MnlvcCJ9.HCfgUYZUTP7uixjYF7tBSw">copy the style</a> I used and make it your own.</p>
<p><a target="_blank" href="https://api.mapbox.com/styles/v1/colbyfayock/ck8c2foj72lqk1jnug0g2haw0.html?fresh=true&amp;title=copy&amp;access_token=pk.eyJ1IjoiY29sYnlmYXlvY2siLCJhIjoiY2swODZzbXYxMGZzdzNjcXczczF6MnlvcCJ9.HCfgUYZUTP7uixjYF7tBSw">Basic Dark Mapbox Theme</a></p>
<p>To learn how to integrate it, you can try to check out the source code of <a target="_blank" href="https://github.com/colbyfayock/coronavirus-map-dashboard">my original demo</a>:</p>
<p><a target="_blank" href="https://github.com/colbyfayock/coronavirus-map-dashboard">https://github.com/colbyfayock/coronavirus-map-dashboard</a></p>
<h3 id="heading-add-overview-dashboard-stats">Add overview dashboard stats</h3>
<p>Dashboards with maps like the <a target="_blank" href="https://www.arcgis.com/apps/opsdashboard/index.html#/bda7594740fd40299423467b48e9ecf6">Johns Hopkins University app</a> allows us to see more than a look on the map, but a glimpse at quick stats about the cases around the world.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/johns-hopkins-coronavirus-map-march-29.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Johns Hopkins University Coronavirus Map Dashboard - March 29, 2020</em></p>
<p>The <a target="_blank" href="https://github.com/NovelCOVID/API">NovelCOVID API</a> has more endpoints like <code>/all</code> that provide a few global stats.</p>
<h2 id="heading-be-safe-and-stay-informed">Be safe and stay informed</h2>
<p>I want to reiterate that you should make sure you're staying up to date using official sources for information, such as the Johns Hopkins University dashboard. Though the data should be reliable, it should also be considered a proof of concept for building a map and referencing, but shouldn't be considered for any kind of statistical analysis.</p>
<p>Please take care of yourself during these times. We're all in this together! ❤️</p>
<h2 id="heading-want-to-learn-more-about-maps">Want to learn more about maps?</h2>
<p>You can check out a few of my other resources to get started:</p>
<ul>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping">Anyone Can Map! Inspiration and an introduction to the world of mapping</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/04/how-to-set-up-a-custom-mapbox-basemap-style-with-react-leaflet-and-leaflet-gatsby-starter/">How to set up a custom Mapbox basemap style with React Leaflet and Leaflet Gatsby Starter</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-summer-road-trip-mapping-app-with-gatsby-and-leaflet">How to Create a Summer Road Trip Mapping App with Gatsby and Leaflet</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2019/12/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/">How to Create your own Santa Tracker with Gatsby and React Leaflet</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">How to build a mapping app in React the easy way with Leaflet</a></li>
</ul>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="600" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Summer Road Trip Mapping App with Gatsby and Leaflet ]]>
                </title>
                <description>
                    <![CDATA[ Get ready for the summer by building your own road trip mapping app with this step-by-step guide! What are we going to build? What do we need before we get started? Step 1: Cleaning up some unneeded code Step 2: Create our road trip locations Step 3... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-summer-road-trip-mapping-app-with-gatsby-and-leaflet/</link>
                <guid isPermaLink="false">66b8e3556a98b2a27ee1f34a</guid>
                
                    <category>
                        <![CDATA[ front end ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mapping ]]>
                    </category>
                
                    <category>
                        <![CDATA[ maps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react-leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Applications ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Tue, 24 Mar 2020 13:01:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/road-trip-mapping-app-2.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Get ready for the summer by building your own road trip mapping app with this step-by-step guide!</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What are we going to build?</a></li>
<li><a class="post-section-overview" href="#heading-what-do-we-need-before-we-get-started">What do we need before we get started?</a></li>
<li><a class="post-section-overview" href="#id=&quot;step-1-cleaning-up-some-unneeded-code&quot;">Step 1: Cleaning up some unneeded code</a></li>
<li><a class="post-section-overview" href="#heading-step-2-create-our-road-trip-locations">Step 2: Create our road trip locations</a></li>
<li><a class="post-section-overview" href="#heading-step-3-prepare-our-app-with-some-functions">Step 3: Prepare our app with some functions</a></li>
<li><a class="post-section-overview" href="#heading-step-4-building-our-trip-path">Step 4: Building our trip path</a></li>
<li><a class="post-section-overview" href="#heading-step-5-styling-our-map-components">Step 5: Styling our map components</a></li>
<li><a class="post-section-overview" href="#heading-want-to-learn-more-about-maps">Want to learn more about maps?</a></li>
</ul>
<p><em>Author’s Note: Even though we’re going through some challenging times, we can still be optimistic that we’ll get through this together and be able to enjoy our summer. Stay safe and wash your hands. ❤️</em></p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/FkO8uggDEXY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We’ll be walking through building a new mapping app that shows a route representing the trip. Each location will have a little card where we can add a picture and some things we did.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/summer-road-trip-map.jpg" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://summer-road-trip.netlify.com/">Colbana's Summer Road Trip</a></em></p>
<p>To get started, we’re going to use this <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">Leaflet Gatsby Starter</a> I created to make the initial setup a little smoother. With our app bootstrapped, we’ll create our list of locations and use Leaflet’s API to draw our route on the map.</p>
<h2 id="heading-woah-a-mapping-app">Woah, a mapping app?</h2>
<p>Yup. If you haven’t played with maps before, don’t be discouraged! It's not as bad as you probably think. If you’d rather start with mapping basics, you can <a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">read more about how mapping works</a> first.</p>
<h2 id="heading-what-do-we-need-before-we-get-started">What do we need before we get started?</h2>
<p>If you followed along with my last tutorial for <a target="_blank" href="https://www.freecodecamp.org/news/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/">building a Santa Tracker</a>, you can follow the same steps to get started. If not, we’ll want to make sure we have the following set up:</p>
<ul>
<li><a target="_blank" href="https://nodejs.org/en/">node</a> or <a target="_blank" href="https://yarnpkg.com/en/">yarn</a> - I'll be using yarn, but you can substitute with npm where appropriate</li>
<li><a target="_blank" href="https://www.gatsbyjs.org/docs/gatsby-cli/">Gatsby’s CLI</a> - <code>yarn global add gatsby-cli</code></li>
</ul>
<p>If you’re not sure about one of the above items, you can try checking out the beginning of <a target="_blank" href="https://www.freecodecamp.org/news/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/">my previous tutorial</a>.</p>
<p>We’ll also want to set up a foundation for our map. We can do this by utilizing the Leaflet Gatsby Starter I put together that provides us a basic setup with <a target="_blank" href="https://leafletjs.com/">Leaflet</a> and <a target="_blank" href="https://react-leaflet.js.org/">React Leaflet</a>.</p>
<pre><code class="lang-shell">gatsby new my-road-trip https://github.com/colbyfayock/gatsby-starter-leaflet
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/terminal-creating-new-gatsby-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new Leaflet Gatsby app in the terminal</em></p>
<p>After that’s finished running, you can navigate to the newly created project directory and start your local development server:</p>
<pre><code class="lang-shell">cd my-road-trip
yarn develop
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/terminal-starting-gatsby-development-server.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Starting your Gatsby app in the terminal</em></p>
<p>If all goes as planned, your server should start and you should now be able to see your basic mapping app in your browser!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/gatsby-starter-leaflet-in-browser.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New Leaflet Gatsby app in the browser</em></p>
<h2 id="heading-step-1-cleaning-up-some-unneeded-code">Step 1: Cleaning up some unneeded code</h2>
<p>The Gatsby Starter we're using to spin up this app comes with some demo code that we don’t need here. We’ll want to make all of the changes below in the file <code>src/pages/index.js</code>, which is the homepage of our app.</p>
<p>First, let’s remove everything from the <code>mapEffect</code> function. This function is used to run code that fires when the map renders.</p>
<pre><code class="lang-js"><span class="hljs-comment">// In src/pages/index.js</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mapEffect</span>(<span class="hljs-params">{ leafletElement } = {}</span>) </span>{
  <span class="hljs-comment">// Get rid of everything in here</span>
}
</code></pre>
<p>Next, we don’t want a marker this time, so let’s remove the <code>&lt;Marker</code> component from our <code>&lt;Map</code> component:</p>
<pre><code class="lang-jsx">&lt;<span class="hljs-built_in">Map</span> {…mapSettings} /&gt;
</code></pre>
<p>Now that we have those pieces cleared out, we can remove all of the following imports and variables from the top of our file:</p>
<ul>
<li>useRef</li>
<li>Marker</li>
<li>promiseToFlyTo</li>
<li>getCurrentLocation</li>
<li>gatsby_astronaut</li>
<li>timeToZoom</li>
<li>timeToOpenPopupAfterZoom</li>
<li>timeToUpdatePopupAfterZoom</li>
<li>ZOOM</li>
<li>popupContentHello</li>
<li>popupContentGatsby</li>
<li>markerRef</li>
</ul>
<p>After, our map should still work, but not do anything.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/new-empty-mapping-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New mapping app with nothing going on</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-road-trip/commit/c92e8f970c8a2f2436f65ea0890680a88b747c49">Follow along with the commit</a></p>
<h2 id="heading-step-2-create-our-road-trip-locations">Step 2: Create our road trip locations</h2>
<p>This step will involve preparing our location data that will populate our road trip app. Our locations will include properties like a name, date, things we did, and a picture if we want.</p>
<p>First, create a new file in the <code>src/data</code> directory called <code>locations.js</code>.  Inside of that file, we want to create and export a new array.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> locations = [
  {
    <span class="hljs-attr">placename</span>: ‘Herndon, VA’,
    <span class="hljs-attr">date</span>: ‘August <span class="hljs-number">1</span>, <span class="hljs-number">2015</span>’,
    <span class="hljs-attr">location</span>: {
      <span class="hljs-attr">lat</span>: <span class="hljs-number">38.958988</span>,
      <span class="hljs-attr">lng</span>: <span class="hljs-number">-77.417320</span>
    },
    <span class="hljs-attr">todo</span>: [
      ‘Where we start! ?’
    ]
  },
  {
    <span class="hljs-attr">placename</span>: ‘Middlesboro, KY<span class="hljs-string">',
    date: ‘August 1, 2015’,
    location: {
      lat: 36.627517,
      lng: -83.621635
    },
    todo: [
      ‘Cumberland Gap ?’
    ]
  }
];</span>
</code></pre>
<p>You can use the above to get started, but you’ll eventually want to change the details to something of your choosing.</p>
<p>If you want to add an image to your location, you can do so by including an <code>image</code> property to the object. You can use either a URL string or you can import a local file if you have one available, like I’m doing in this example:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> imgHerndonStart <span class="hljs-keyword">from</span> <span class="hljs-string">'assets/images/herndon-start.jpg’;

export const locations = [
  {
    placename: ‘Herndon, VA’,
    date: ‘August 1, 2015’,
    image: imgHerndonStart,
    location: {
      lat: 38.958988,
      lng: -77.417320
    },
    todo: [
      ‘Where we start! ?’
    ]
  }
]</span>
</code></pre>
<p>Once we have that file created, we can now import our locations into our <code>src/pages/index.js</code> file so we can use it in our next step:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { locations } <span class="hljs-keyword">from</span> <span class="hljs-string">'data/locations’;</span>
</code></pre>
<p>If you add a <code>console.log(locations)</code> inside of your page, you should now see all of your location data in an array!</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-road-trip/commit/55f4eb32d402364a20ad0342ebfde995081c521e">Follow along with the commit</a></p>
<h2 id="heading-step-3-prepare-our-app-with-some-functions">Step 3: Prepare our app with some functions</h2>
<p>To try to keep things simple and focused, I grouped together 3 important components of creating our map into functions. Though it’s available to copy and paste, we’ll walk through what’s happening in each function.</p>
<p>You can place each of these functions at the bottom of the <code>src/pages/index.js</code> file so they’re ready to use in our next step.</p>
<h3 id="heading-createtrippointsgeojson">createTripPointsGeoJson</h3>
<p>Our first function is going to take the array of our locations and return a <a target="_blank" href="https://geojson.org/">GeoJSON document</a>, with our locations mapped into an individual Feature. We’ll use this function to create the individual points on our map.</p>
<p>What is a GeoJSON document? It's essentially a JavaScript object or JSON document with a specific structure that creates consistency with geographical data.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createTripPointsGeoJson</span>(<span class="hljs-params">{ locations } = {}</span>) </span>{
  <span class="hljs-keyword">return</span> {
    “type”: “FeatureCollection”,
    “features”: locations.map(<span class="hljs-function">(<span class="hljs-params">{ placename, location = {}, image, date, todo = [] } = {}</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> { lat, lng } = location;
      <span class="hljs-keyword">return</span> {
        “type”: “Feature”,
        “properties”: {
          placename,
          todo,
          date,
          image
        },
        “geometry”: {
          “type”: “Point”,
          “coordinates”: [ lng, lat ]
        }
      }
    })
  }
}
</code></pre>
<p>So what’s happening in the above?</p>
<ul>
<li>We take an argument of locations, which will be our array of destinations</li>
<li>We return an object with some dynamic properties associated with it</li>
<li>Within the object, we map our locations to individual <code>Feature</code> objects</li>
<li>Each object includes a <code>Point</code> shape using our coordinates</li>
<li>It additionally includes our properties that store our metadata</li>
</ul>
<p>When this function is invoked, we will have a newly created JavaScript object that includes an array of Points representing the locations we are stopping at on our road trip.</p>
<h3 id="heading-createtriplinesgeojson">createTripLinesGeoJson</h3>
<p>We’re going to create another function that’s similar to the previous one. This time however, instead of points, we want to create lines that represent going from one point to the next.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createTripLinesGeoJson</span>(<span class="hljs-params">{ locations } = {}</span>) </span>{
  <span class="hljs-keyword">return</span> {
    “type”: “FeatureCollection”,
    “features”: locations.map(<span class="hljs-function">(<span class="hljs-params">stop = {}, index</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> prevStop = locations[index - <span class="hljs-number">1</span>];

      <span class="hljs-keyword">if</span> ( !prevStop ) <span class="hljs-keyword">return</span> [];

      <span class="hljs-keyword">const</span> { placename, location = {}, date, todo = [] } = stop;
      <span class="hljs-keyword">const</span> { lat, lng } = location;
      <span class="hljs-keyword">const</span> properties = {
        placename,
        todo,
        date
      };

      <span class="hljs-keyword">const</span> { <span class="hljs-attr">location</span>: prevLocation = {} } = prevStop;
      <span class="hljs-keyword">const</span> { <span class="hljs-attr">lat</span>: prevLat, <span class="hljs-attr">lng</span>: prevLng } = prevLocation;

      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">type</span>: ‘Feature’,
        properties,
        <span class="hljs-attr">geometry</span>: {
          <span class="hljs-attr">type</span>: ‘LineString’,
          <span class="hljs-attr">coordinates</span>: [
            [ prevLng, prevLat ],
            [ lng, lat ]
          ]
        }
      }
    })
  }
}
</code></pre>
<p>So you’ll immediately notice that this is very similar to our last function. We’re returning an object and setting our metadata properties on a list of Features.</p>
<p>The big difference, however, is that we're creating a Line. To do this, we're looking up and referring to <code>prevStop</code> which will be the previous stop. We’ll use both the previous stop and our current stop in order to have 2 points which we can use to draw the line. </p>
<p>If we don’t have a previous stop, we return an empty array, which basically means we’re at the beginning of our journey with no line before it.</p>
<p>With the previous stop and current stop, we create a <code>LineString</code> type of Feature with our 2 points.</p>
<h3 id="heading-tripstoppointtolayer">tripStopPointToLayer</h3>
<p>Our last function is going to allow us to create custom content for each of the points that we will be adding to our map. We’ll actually be utilizing this function within a Leaflet property, so we’ll be conforming our arguments to that specification.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tripStopPointToLayer</span>(<span class="hljs-params"> feature = {}, latlng </span>) </span>{
  <span class="hljs-keyword">const</span> { properties = {} } = feature;
  <span class="hljs-keyword">const</span> { placename, todo = [], image, date } = properties;

  <span class="hljs-keyword">const</span> list = todo.map(<span class="hljs-function"><span class="hljs-params">what</span> =&gt;</span> <span class="hljs-string">`&lt;li&gt;<span class="hljs-subst">${ what }</span>&lt;/li&gt;`</span>);
  <span class="hljs-keyword">let</span> listString = ‘’;
  <span class="hljs-keyword">let</span> imageString = ‘’;

  <span class="hljs-keyword">if</span> ( <span class="hljs-built_in">Array</span>.isArray(list) &amp;&amp; list.length &gt; <span class="hljs-number">0</span> ) {
    listString = list.join(‘’);
    listString = <span class="hljs-string">`
      &lt;p&gt;Things we will or have done…&lt;/p&gt;
      &lt;ul&gt;<span class="hljs-subst">${listString}</span>&lt;/ul&gt;
    `</span>
  }

  <span class="hljs-keyword">if</span> ( image ) {
    imageString = <span class="hljs-string">`
      &lt;span class=“trip-stop-image” style=“background-image: url(<span class="hljs-subst">${image}</span>)”&gt;<span class="hljs-subst">${placename}</span>&lt;/span&gt;
    `</span>;
  }

  <span class="hljs-keyword">const</span> text = <span class="hljs-string">`
    &lt;div class=“trip-stop”&gt;
      <span class="hljs-subst">${ imageString }</span>
      &lt;div class=“trip-stop-content”&gt;
        &lt;h2&gt;<span class="hljs-subst">${placename}</span>&lt;/h2&gt;
        &lt;p class=“trip-stop-date”&gt;<span class="hljs-subst">${date}</span>&lt;/p&gt;
        <span class="hljs-subst">${ listString }</span>
      &lt;/div&gt;
    &lt;/div&gt;
  `</span>;

  <span class="hljs-keyword">const</span> popup = L.popup({
    <span class="hljs-attr">maxWidth</span>: <span class="hljs-number">400</span>
  }).setContent(text);

  <span class="hljs-keyword">const</span> layer = L.marker( latlng, {
    <span class="hljs-attr">icon</span>: L.divIcon({
      <span class="hljs-attr">className</span>: ‘icon’,
      <span class="hljs-attr">html</span>: <span class="hljs-string">`&lt;span class=“icon-trip-stop”&gt;&lt;/span&gt;`</span>,
      <span class="hljs-attr">iconSize</span>: <span class="hljs-number">20</span>
    }),
    <span class="hljs-attr">riseOnHover</span>: <span class="hljs-literal">true</span>
  }).bindPopup(popup);

  <span class="hljs-keyword">return</span> layer;
}
</code></pre>
<p>One thing you’ll notice as we work through this function is that we create strings of HTML text. Given that the Leaflet API we’re utilizing for this doesn’t interface directly with React, we have to build out HTML manually to pass it in to our functions.</p>
<p>Starting from the top:</p>
<ul>
<li>We take in 2 arguments, <code>feature</code> and <code>latlng</code>. Leaflet passes these 2 values in for us to use in our function.</li>
<li>We destructure our feature, allowing us to assign our metadata into variables</li>
<li>2 string variables are initialized that we’ll use for our HTML</li>
<li>If we include a <code>todo</code> property as an array, we add a new list with each item inside.</li>
<li>If we include an image, we create an image tag.</li>
<li>With our newly created HTML strings, we construct the entirety of what will be our popup card for each strop</li>
<li>With our popup HTML, we create a Leaflet <code>popup</code> instance</li>
<li>With the latlng argument and our popup, we create a new Leaflet <code>marker</code>  instance. This will represent the point on the map.</li>
<li>Inside of the Marker creation, we create a basic HTML tag that well use to style the marker</li>
<li>We then bind our popup to this new Marker instance. This will allow the popup to be associated with that individual Marker</li>
<li>Finally, we return our newly created layer</li>
</ul>
<p>Remember to make sure you put all of the functions above at the bottom of your <code>src/pages/index.js</code> page.</p>
<p>Once all of those functions are added, our map should still be the same thing, basically nothing happening.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-road-trip/commit/b27b644b32a11e4372963b9d16e0f7ec0ee74b65">Follow along with the commit</a></p>
<h2 id="heading-step-4-building-our-trip-path">Step 4: Building our trip path</h2>
<p>This is where things get interesting. We’ll now utilize the functions we created to build our road trip path. All of our work here will be within the <code>mapEffect</code> function inside of the <code>src/pages/index.js</code> file.</p>
<p>For context, our <code>mapEffect</code> function includes an argument called <code>leafletElement</code>. This value refers to the Map instance that Leaflet recognizes. This Map instance includes our map state as well as many utility functions to work with our map.</p>
<p>First, at the top of the function, we want to make sure we have a map. If not, we can return to bail out of the function.</p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span> ( !leafletElement ) <span class="hljs-keyword">return</span>;
</code></pre>
<p>Next, we want to use the <code>eachLayer</code> utility function and remove each <code>layer</code> from our map element. We do this to make sure we always have the correct map layer state.</p>
<pre><code class="lang-js">leafletElement.eachLayer(<span class="hljs-function">(<span class="hljs-params">layer</span>) =&gt;</span> leafletElement.removeLayer(layer));
</code></pre>
<p>With our cleaned up map, we can utilize 2 of the functions we created to create new GeoJSON objects.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> tripPoints = createTripPointsGeoJson({ locations });
<span class="hljs-keyword">const</span> tripLines = createTripLinesGeoJson({ locations });
</code></pre>
<p>With our GeoJSON objects, we need to convert those to Leaflet GeoJSON instances, which we’ll use to add to the map.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> tripPointsGeoJsonLayers = <span class="hljs-keyword">new</span> L.geoJson(tripPoints, {
  <span class="hljs-attr">pointToLayer</span>: tripStopPointToLayer
});

<span class="hljs-keyword">const</span> tripLinesGeoJsonLayers = <span class="hljs-keyword">new</span> L.geoJson(tripLines);
</code></pre>
<p>If you notice in the above, we're using our <code>tripStopPointToLayer</code> function. As I alluded to before, the <code>geoJson</code> instance we’re creating includes a property that allows us to pass in a function, giving us the ability to manipulate the layer creation. This is how we create our point and popup content.</p>
<p>We can proceed to adding both of those new layers to our map using the <code>addTo</code> .</p>
<pre><code class="lang-js">tripPointsGeoJsonLayers.addTo(leafletElement);
tripLinesGeoJsonLayers.addTo(leafletElement);
</code></pre>
<p>Next, to make sure we zoom and center on the right location, we want to grab the bounds of the map using the <code>getBounds</code> function on our GeoJSON layer instance.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> bounds = tripPointsGeoJsonLayers.getBounds();
</code></pre>
<p>Finally, we fit our map's view to those bounds using the <code>fitBounds</code> function on our Map instance.</p>
<pre><code class="lang-js">leafletElement.fitBounds(bounds);
</code></pre>
<p>Once you save  and reload the page, you should now see a blue path representing the jump from each of our locations on the map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/mapping-app-with-road-trip-path.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Mapping app with road trip path</em></p>
<p>One issue though. If you notice, we only see the path. This is because we need to add some CSS which we’ll get to in the next step.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-road-trip/commit/6b3f079bebdd60bf012c0886fc33547c98ea50f5">Follow along with the commit</a></p>
<h2 id="heading-step-5-styling-our-map-components">Step 5: Styling our map components</h2>
<p>Our last step will be adding some styles that will allow our markers to show and our popups to look just right.</p>
<p>In this step, we’ll be working inside of the <code>_home.scss</code> file, which you can find in <code>src/assets/stylesheets/pages</code>.</p>
<p>We can get started by copy and pasting this block of styles into the bottom of that file. With that done, we can walk through what’s happening.</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.trip-stop</span> {

  <span class="hljs-attribute">width</span>: <span class="hljs-number">400px</span>;
  <span class="hljs-attribute">overflow</span>: hidden;

  <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.4em</span>;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">margin-bottom</span>: .<span class="hljs-number">2em</span>;
  }

  <span class="hljs-selector-tag">p</span>,
  <span class="hljs-selector-tag">ul</span>,
  <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2em</span>;
    <span class="hljs-attribute">font-weight</span>: normal;
  }

  <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">margin</span>: .<span class="hljs-number">2em</span> <span class="hljs-number">0</span>;
  }

  <span class="hljs-selector-class">.trip-stop-date</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-variable">$grey-600</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1em</span>;
  }

  <span class="hljs-selector-tag">ul</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1.4em</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  }

}

<span class="hljs-selector-class">.trip-stop-image</span> {
  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">float</span>: left;
  <span class="hljs-attribute">overflow</span>: hidden;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">150px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">150px</span>;
  <span class="hljs-attribute">text-indent</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">color</span>: transparent;
  <span class="hljs-attribute">background-position</span>: center;
  <span class="hljs-attribute">background-size</span>: cover;
}

<span class="hljs-selector-class">.trip-stop-content</span> {
  <span class="hljs-attribute">float</span>: left;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">250px</span>;
  <span class="hljs-attribute">padding-left</span>: <span class="hljs-number">1em</span>;
}

<span class="hljs-selector-class">.icon-trip-stop</span> {

  <span class="hljs-attribute">display</span>: block;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">1.5em</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">1.5em</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-variable">$orange-500</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">5px</span> rgba(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,.<span class="hljs-number">5</span>);

  &amp;<span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-variable">$deep-orange-400</span>;
  }

}
</code></pre>
<p>There’s three components to our styles above:</p>
<ul>
<li><code>.trip-stop-images</code>: Inside of the marker popup, we optionally can include an image. These styles set the size, make the text transparent, (it’s there for accessibility), and float it to the left so that our popup content can align correctly side by side.</li>
<li><code>.trip-stop-content</code>: This refers to the other half of our popup content. All we need to do here is make sure our size is appropriate and that it floats next to our image.</li>
<li><code>.icon-trip-stop</code>: The HTML tag that we’re using as our icon designation gets styled here. We size it up, set a color using a predetermined Scss variable, and we’re good to go.</li>
</ul>
<p>Once those styles are saved, you should now see the points on the map representing each location. Additionally, you should be able to click each of these points to open up a popup containing information about the stop.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/road-trip-mapping-app-with-popup.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Road trip mapping app with popups</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-road-trip/commit/d2bac5c1b04a32837366de6f15f14d5342134d38">Follow along with the commit</a></p>
<h2 id="heading-optional-last-step-style-tweaks">Optional Last Step: Style Tweaks</h2>
<p>The last thing that's completely optional is to make a few style tweaks to give your site a little personality. I’m not going to go over this in details, but if you’d like to follow along and dress things up a little bit, you can follow along with <a target="_blank" href="https://github.com/colbyfayock/my-road-trip/commit/c2c667da6e34595bc6d8dd0ee66e55d4155feed2">this commit</a> which shows each code change I made.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/road-trip-mapping-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Final version of the road trip mapping app</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-road-trip/commit/c2c667da6e34595bc6d8dd0ee66e55d4155feed2">Follow along with the commit</a></p>
<h2 id="heading-yay-we-did-it">Yay, we did it!</h2>
<p>If you followed along with me, or skipped right to the starter, you should now have a mapping app that you can use for your next road trip.</p>
<p>The good news is this project can apply to anything! Want to map out your favorite restaurants in Washington, DC? Add your locations and remove the lines. Want to create line drawings over the map? That's certainly an option.</p>
<p>Whatever it is, if you enjoyed getting this map spun up, get creative and apply it to your next project!</p>
<h2 id="heading-want-to-learn-more-about-maps">Want to learn more about maps?</h2>
<p>You can check out a few of my other resources to get started:</p>
<ul>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet">How to create a Coronavirus (COVID-19) Dashboard &amp; Map App in React with Gatsby and Leaflet</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/04/how-to-set-up-a-custom-mapbox-basemap-style-with-react-leaflet-and-leaflet-gatsby-starter/">How to set up a custom Mapbox basemap style with React Leaflet and Leaflet Gatsby Starter</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping">Anyone Can Map! Inspiration and an introduction to the world of mapping</a></li>
<li><a target="_blank" href="https://www.colbyfayock.com/2019/12/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/">How to Create your own Santa Tracker with Gatsby and React Leaflet</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">How to build a mapping app in React the easy way with Leaflet</a></li>
</ul>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="600" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 3 ways to edit Markdown with TinaCMS + Gatsby ]]>
                </title>
                <description>
                    <![CDATA[ By Thomas Weibenfalk Supercharge your static site with real-time content editing! ? In this post, I will explore the three different methods Tina offers to edit Markdown on your Gatsby site. You’ll learn how to set up Tina with both Page Queries and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/3-ways-to-edit-markdown-with-tina-gatsby/</link>
                <guid isPermaLink="false">66d46149706b9fb1c166b9ad</guid>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 09 Jan 2020 18:27:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/01/tinacms.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Thomas Weibenfalk</p>
<p><strong>Supercharge your static site with real-time content editing!</strong> ?</p>
<p>In this post, I will explore <em>the three different methods</em> Tina offers to edit Markdown on your Gatsby site. You’ll learn how to set up Tina with both Page Queries and Static Queries.</p>
<p><em>This post will not cover the basics of using Tina with Gatsby. Please reference the <a target="_blank" href="https://tinacms.org/docs/gatsby/manual-setup">documentation</a> on how to initially set up Tina with Gatsby.</em></p>
<h2 id="heading-whats-the-deal-with-page-queries-and-static-queries">What’s the deal with Page Queries and Static Queries?</h2>
<p>Before we dive down into editing Markdown with Tina, we have to understand how Gatsby handles querying data with GraphQL. </p>
<p>You can source data from almost anywhere in Gatsby. In our case, we’re using <em>Markdown</em>. When you build your site, Gatsby creates a GraphQL schema for all the data. Then you use <a target="_blank" href="https://graphql.org/learn/">GraphQL</a> in your React components to query your sourced data.</p>
<p>Gatsby allows you to query your data in two ways: <a target="_blank" href="https://www.gatsbyjs.org/docs/static-vs-normal-queries/"><em>Page Queries and Static Queries</em></a>.
Since the release of the <a target="_blank" href="https://reactjs.org/docs/hooks-intro.html">React Hooks API</a> and the <a target="_blank" href="https://www.gatsbyjs.org/docs/use-static-query/"><code>useStaticQuery</code> hook</a> in Gatsby, it is very easy to query your data. </p>
<p>There are cases when you can’t use a Static Query though. First, let’s explore the differences.</p>
<h3 id="heading-the-two-main-differences-are">The two main differences are:</h3>
<ul>
<li>Page Queries can accept GraphQL variables. Static Queries can’t.</li>
<li>Page Queries can only be added to page components. Static Queries can be used in all components.</li>
</ul>
<p>So, why can’t we use GraphQL variables in a Static Query? The reason for that is a Static Query doesn’t have access to the page context like a Page Query does. The result is that a Static Query won’t be able to access variables that are defined in the page context. </p>
<p>You can define the page context in your <code>gatsby-node.js</code> file in your <code>createPage</code> function. Here you can supply your page with different variables that will get injected to your page on build time.</p>
<p>I use Static Queries as much as possible because I love the hooks API and the ease of composition possibilities it brings. For example, you can create custom hooks and reuse them in multiple components.</p>
<p>Let’s say that you have a GraphQL query that grabs metadata that you want on multiple pages. Create a custom React hook with the <code>useStaticQuery</code> Gatsby hook inside of it. Then you can use your custom hook wherever you want and always easily get that data into your component. When you need to have variables in your component, you have to use a Page Query. Page Queries cannot be used with the hooks API and have to be unique and attached to the specific page component.</p>
<p>Another great thing with Static Queries is that you can grab the data in the component that needs the data. That prevents <a target="_blank" href="https://kentcdodds.com/blog/prop-drilling"><em>prop drilling</em></a> and your data is more tightly coupled to the component where it is used.</p>
<h2 id="heading-react-overview">React overview</h2>
<p>For getting data, we can use Gatsby's query options. For structuring our components, React also offers a couple of options. You can either create your component as a <a target="_blank" href="https://reactjs.org/docs/components-and-props.html">class or a functional component</a>. Before the React Hooks API, you had to use class components to have state in your components. Now with hooks, you can do this with functional components.?</p>
<h2 id="heading-three-ways-to-edit-markdown-with-tina">Three ways to edit markdown with Tina</h2>
<p>Given all the options for creating components and souring data in Gatsby, we have to choose the most suitable approach for the project. Tina can work with all of these options, providing <strong>three different approaches</strong> for editing Markdown with Gatsby as described below.</p>
<ul>
<li><strong>remarkForm</strong> - A <a target="_blank" href="https://reactjs.org/docs/higher-order-components.html">Higher Order Component</a> used when you source data from a Page Query in Gatsby. This component can be utilized with both functional and class components. Please note the subtle difference here! The only difference in naming from the render props component is the lowercase “r”.</li>
<li><strong>useLocalRemarkForm</strong> - A <a target="_blank" href="https://reactjs.org/docs/hooks-overview.html">React Hook</a> that is intended for functional components sourcing data from either a Static or a Page Query. If the component is sourcing static data, Gatsby's <code>useStaticQuery</code> hook would be called.</li>
<li><strong>RemarkForm</strong> - A <a target="_blank" href="https://reactjs.org/docs/render-props.html">Render Props Component</a> that you can use in class components sourcing data from either a Static or a Page Query. Static data would be sourced via Gatsby’s <code>StaticQuery</code> render props component.</li>
</ul>
<h3 id="heading-remarkform-how-to-use-it-and-why-it-wont-work-with-static-queries">remarkForm - How to use it and why it won’t work with Static Queries.</h3>
<p>First, Let’s dive into how to hook up TinaCMS with a Page Query.
The <code>remarkForm</code> Component in TinaCMS is a <a target="_blank" href="https://reactjs.org/docs/higher-order-components.html">Higher Order Component</a>, a HOC in short. This means that it is a function that takes in another component and will return a new component that has added functionality to it.</p>
<p>If you’re not familiar with HOC's, I suggest you read about them in the <a href="https://reactjs.org/docs/higher-order-components.html"><b>React official docs</b></a>. They are considered “advanced usage” in the React world.</p>
<p>The <code>remarkForm</code> component wants another component as an argument and is intended for Page Queries. A Page Query injects the data as a prop to the component and we access the data from this prop. With a <code>useStaticQuery</code> hook, the data is collected in a variable, that you choose, inside the component itself. That means if you're using the <code>useStaticQuery</code> hook in Gatsby you won’t have a component to give the <code>remarkForm</code> HOC. Bummer!? That’s why you can only use the <code>remarkForm</code> component with Page Queries.</p>
<p>So <strong>how do you use this component with a Page Query</strong> in Gatsby? First, check out the fictive Star Wars component below. It will show the three steps needed to hook everything up:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// 1. Import the `remarkForm` HOC</span>
<span class="hljs-keyword">import</span> { remarkForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby-tinacms-remark'</span>

<span class="hljs-keyword">const</span> StarWarsMovie = <span class="hljs-function">(<span class="hljs-params">{ data: { markdownRemark } }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{markdownRemark.frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
}

<span class="hljs-comment">// 2. Wrap your component with `remarkForm`</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> remarkForm(StarWarsMovie)

<span class="hljs-comment">// 3. Add the required ...TinaRemark fragment to your Page Query</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> pageQuery = graphql<span class="hljs-string">`
  query StarWarsMovieById($id: String!) {
    markdownRemark(fields: { id: { eq: $id } }) {
      id
      html
      frontmatter {
        title
        releaseDate
        crawl
      }
      ...TinaRemark
    }
  }
`</span>
</code></pre>
<p>The above code is a component that displays information about Star Wars movies. For now, it just displays a title, but it could also display the release date and the crawl text in the intro to the film. But that’s another story in a galaxy far far away ... ⭐</p>
<p>The first step in this example is to import the <code>remarkForm</code> hook from the Gatsby plugin ‘gatsby-tinacms-remark’. This is the plugin that <em>makes TinaCMS work with Markdown files</em>.</p>
<p>There’s no need to do any additions to the code inside of the component itself. It could be any component — functional or class — structured in the way you want it. The only thing you have to do with the component itself is to wrap your component with the <code>remarkForm</code> HOC when you export it.</p>
<p>One more thing you have to do before you are good to go is to add the GraphQL fragment <code>...TinaRemark</code> in your query. This is needed for TinaCMS to recognize your data and create the required editor fields in the TinaCMS sidebar. After that, you only have to start up your dev server to show the site and the Tina sidebar.</p>
<p>Easy enough isn’t it? Just three small steps and you’ll have a beautiful sidebar to edit your content on your site. ?</p>
<p><em>But what if you want to use a Static Query and not a Page Query?</em></p>
<h3 id="heading-uselocalremarkform-to-the-rescue">useLocalRemarkForm to the rescue!</h3>
<p>We’ve learned that the <code>remarkForm</code> HOC won’t work on Static Queries. So we’ll have to find another solution for using Static Queries with TinaCMS.</p>
<p><strong>Great news!</strong></p>
<p>The <code>remarkForm</code> component is essentially a "wrapper" for the <code>useLocalRemarkForm</code> hook. ? It takes in a component as an argument, calls <code>useLocalRemarkForm</code> with the Page Query data and returns a new component with the query data and TinaCMS connected to it.</p>
<p>We can use the <code>useLocalRemarkForm</code> hook directly, without using the <code>remarkForm</code> HOC. This can be useful with Static Queries or if we just prefer working with hooks!</p>
<p>Take a look at the code example below to get an idea of how <code>useLocalRemarkForm</code> works.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// 1. Import useLocalRemarkForm hook</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> ‘react’;
<span class="hljs-keyword">import</span> { useLocalRemarkForm } <span class="hljs-keyword">from</span> ‘gatsby-tinacms-remark’;
<span class="hljs-keyword">import</span> { useStaticQuery } <span class="hljs-keyword">from</span> ‘gatsby’;

<span class="hljs-keyword">const</span> StarWarsMovie = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// 2. Add required TinaCMS fragment to the GrahpQL query</span>
    <span class="hljs-keyword">const</span> data = useStaticQuery(graphql<span class="hljs-string">`
      query StarWarsMovieById {
        markdownRemark(fields: { id: { eq: "sw-01" } }) {
          id
          html
          frontmatter {
            title
            releaseDate
            crawl
          }
          ...TinaRemark
        }
      }
    `</span>);
  <span class="hljs-comment">// 3. Call the useLocalRemarkForm hook and pass in the data</span>
  <span class="hljs-keyword">const</span> [markdownRemark] = useLocalRemarkForm(data.markdownRemark);
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{markdownRemark.frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> StarWarsMovie;
</code></pre>
<p>This is just an example component illustrating how <code>useLocalRemarkForm</code> works. In the real world, it would not be an optimal solution using a Static Query for this. That’s because, as you can see, you can’t use variables inside the <code>useStaticQuery</code> hook to make it dynamic. You have to hardcode the movie id. So this query will work for that specific movie only, which is no good.</p>
<p>Let’s break down what’s happening here:</p>
<ol>
<li>We import the <code>useLocalRemarkForm</code> custom hook so we can use it in our component.</li>
<li>Just as before, the <code>...TinaRemark</code> fragment is needed in the GraphQL query. So we add that one there.</li>
<li>When we’ve got our data back from the Gatsby <code>useStaticQuery</code> hook we can call the TinaCMS <code>useLocalRemarkForm</code> hook with that data. This hook will return an array with two elements. The first element is practically the data that we called the hook with. It has the same shape and is ready for us to use in our component. The second element is a reference to the Tina form. We don’t need that one so we don’t destructure it out as we do with the <code>markdownRemark</code>.</li>
</ol>
<p>If you're wondering about this line:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [markdownRemark] = useLocalRemarkForm(heroData.markdownRemark)
</code></pre>
<p>It is an example of <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">ES6 destructuring</a>. As we get an array with two elements back, I destructure out the first element (which is our data) and name it <code>markdownRemark</code>. You can name it whatever you want.</p>
<h3 id="heading-remarkform-the-render-prop-component">RemarkForm - The Render Prop Component</h3>
<p>You can’t use React Hooks on class components. That’s why Tina provides a <a target="_blank" href="https://tinacms.org/docs/gatsby/markdown/#2-the-render-props-component-remarkform"><code>RemarkForm</code></a> component that uses the <a target="_blank" href="https://reactjs.org/docs/render-props.html">Render Props</a> pattern.</p>
<p>This component works with both Page and Static Queries. I will show how to use it with a Page Query below.</p>
<p>Take a look at below example:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// 1. import the RemarkForm render prop component</span>
<span class="hljs-keyword">import</span> { RemarkForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@tinacms/gatsby-tinacms-remark'</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StarWarsMovie</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{
  render() {
    <span class="hljs-comment">/*
     ** 2. Return RemarkForm, pass in markdownRemark
     **    to the remark prop and pass in what you
     **    want to render to the render prop
     */</span>
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">RemarkForm</span>
        <span class="hljs-attr">remark</span>=<span class="hljs-string">{this.props.data.markdownRemark}</span>
        <span class="hljs-attr">render</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">markdownRemark</span> }) =&gt;</span> {
          return <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{markdownRemark.frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        }}
      /&gt;</span>
    )
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> StarWarsMovie

<span class="hljs-comment">// 3. Add the required ...TinaRemark fragment to your Page Query</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> pageQuery = graphql<span class="hljs-string">`
  query StarWarsMovieById($id: String!) {
    markdownRemark(fields: { id: { eq: $id } }) {
      id
      html
      frontmatter {
        title
        releaseDate
        crawl
      }
      ...TinaRemark
    }
  }
`</span>
</code></pre>
<p>Ok, yet again, let’s see what’s happening here:</p>
<ol>
<li>We import the <code>RemarkForm</code> component for us to use in our code.</li>
<li>In our return statement we return the <code>RemarkForm</code> component and pass in it's predefined, and required props. The remark prop provides <code>RemarkForm</code> with the markdown data sourced from the Page Query. The render prop gets the JSX that we want to render through a function, or a render prop. <code>RemarkForm</code> will hook up Tina for editing the data and then render whatever is specified in the render prop function.</li>
<li>Just as before we have to add the <code>...TinaRemark</code> fragment to the Page Query.</li>
</ol>
<h2 id="heading-next-steps">Next steps</h2>
<p><strong>That's it</strong>! Three ways of using Tina for editing Markdown files in Gatsby. ?</p>
<p>In this post, we learned about how to <em>set up Tina with both Static Queries and Page Queries in Gatsby</em>. We also learned about three different ways to edit markdown with Tina depending on your type of React component.</p>
<p>This is just the basics to get you started. If you like Tina and want to learn more you should check out the <a target="_blank" href="https://tinacms.org/docs/">official docs</a>. There’s a lot more stuff to read there and some interesting use cases.</p>
<p>For example, you can learn how to apply <a target="_blank" href="https://tinacms.org/docs/gatsby/inline-editing">inline editing</a> and also how to <a target="_blank" href="https://tinacms.org/docs/gatsby/markdown#customizing-remark-forms">customize the form fields</a> in the Tina sidebar.</p>
<p>Tina is a great addition to the React ecosystem and static site generators like Gatsby. It gives your site a pleasant and easy way to edit and interact with your content.
I’m thrilled to see how big TinaCMS will be and what it can do as it evolves!</p>
<blockquote>
<p>Originally published on <a target="_blank" href="https://tinacms.org/blog/three-ways-to-edit-md/">TinaCMS.org</a></p>
</blockquote>
<h2 id="heading-more-reading-and-learning">More reading and learning</h2>
<p><a target="_blank" href="https://tinacms.org/docs/">Tina Official Documentation</a></p>
<p><a target="_blank" href="https://community.tinacms.org/">Tina Community</a></p>
<p>Tina on Twitter: <a target="_blank" href="https://twitter.com/tina_cms">@tina_cms</a></p>
<hr>
<p>Feel free to visit me on these links:</p>
<p>Watch Weibenfalks tutorial for <a target="_blank" href="https://www.youtube.com/watch?v=eZWJ9ZtF61A&amp;t=265s">Tina &amp; Gatsby</a>.
Twitter — <a target="_blank" href="https://twitter.com/weibenfalk">@weibenfalk</a>,
Weibenfalk on <a target="_blank" href="https://www.youtube.com/c/weibenfalk">Youtube</a>,
Weibenfalk <a target="_blank" href="https://www.weibenfalk.com">Courses Website</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
