<?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[ GatsbyJS - 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[ GatsbyJS - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 16:31:07 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/gatsbyjs/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <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="2000" 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[ 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="2000" 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="2000" 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="2000" 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="2000" 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="2000" 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 your own Santa Tracker with Gatsby and React Leaflet ]]>
                </title>
                <description>
                    <![CDATA[ The Christmas season is a magical time of year. We have Santa flying around spreading cheer and Elf roaming around New York during our yearly rewatch with family and friends. Buddy the Elf waving To get in the spirit, we’re going to spin up a web ap... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/</link>
                <guid isPermaLink="false">66b8e32047e3b55b9fb6ee41</guid>
                
                    <category>
                        <![CDATA[ Santa ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Christmas ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ holidays ]]>
                    </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, 11 Dec 2019 15:14:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/12/santa-tracker.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The Christmas season is a magical time of year. We have Santa flying around spreading cheer and Elf roaming around New York during our yearly rewatch with family and friends.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/elf-waving.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Buddy the Elf waving</em></p>
<p>To get in the spirit, we’re going to spin up a web app that includes a map that tracks Santa on it!</p>
<p><em>Edit 12/23: Updated the app to request directly to Santa's route, just in case the original API doesn't work as originally expected.</em></p>
<h2 id="heading-what-are-we-going-to-build"><strong>What are we going to build?</strong></h2>
<p>We’re going to work through building a mapping app that tracks Santa’s route and his current location.</p>
<p>To achieve this, we’re going to spin up a premade Gatsby starter that will give us a basic foundation for a map, utilize Google’s unofficial API to grab Santa’s route, and overlay his position and route on top of the map with Leaflet.</p>
<h2 id="heading-woah-a-mapping-app"><strong>Woah, a mapping app?</strong></h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/ay-caramba.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Ay Caramba</em></p>
<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"><strong>What do we need before we get started?</strong></h2>
<p>For this exercise, I’m going to assume you have <a target="_blank" href="https://nodejs.org/en/">node</a> or <a target="_blank" href="https://yarnpkg.com/en/">yarn</a> installed. For each example, I'll use yarn, but use the tool of your choice.</p>
<p>You’ll also want to install <a target="_blank" href="https://www.gatsbyjs.org/docs/gatsby-cli/">Gatsby’s CLI</a> globally which will allow us to use their <a target="_blank" href="https://www.gatsbyjs.org/docs/starters/">Starter tools</a>.</p>
<p>To set up Gatsby’s CLI, run the following command:</p>
<pre><code>yarn <span class="hljs-built_in">global</span> add gatsby-cli
</code></pre><p>After, you should be able to run <code>gatsby -h</code> to see the available commands, which means it’s successfully installed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/gatsby-help-install-verify.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Running gatsby -h to verify install</em></p>
<p>For more info about the Gatsby CLI, you can <a target="_blank" href="https://www.gatsbyjs.org/docs/gatsby-cli/">check out their documentation</a>.</p>
<h2 id="heading-getting-started-with-our-map-foundation"><strong>Getting started with our map foundation</strong></h2>
<p>Once our command line tools are set up, the first thing we’ll want to do is create a new Gatsby project using <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">a Leaflet starter</a> I put together. It provides us with 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>
<p>Starting in your project directory, let’s install the project:</p>
<pre><code>gatsby <span class="hljs-keyword">new</span> [directory] https:<span class="hljs-comment">//github.com/colbyfayock/gatsby-starter-leaflet</span>
</code></pre><p>Make sure to replace <code>[directory]</code> with the location you want to set up your project.</p>
<p>Once you run that command, Gatsby will clone that project without any of the git references and install the packages required to start.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/installing-gatsby-starter-leaflet.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Installing Gatsby Starter Leaflet</em></p>
<p>To make sure it works, you can now navigate to that directory, spin up your server, and test it in the browser:</p>
<pre><code>cd [directory]
yarn develop
</code></pre><p>Where you see <code>[directory]</code> above, make sure to use the same path as you did before when setting up the new Gatsby project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/running-gatsby-starter-leaflet.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Running Gatsby Starter Leaflet</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/2019/12/gatsby-starter-leaflet-in-browser.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Gatsby Starter Leaflet in the browser</em></p>
<h2 id="heading-cleaning-things-up"><strong>Cleaning things up</strong></h2>
<p>This starter comes with a quick example of how we can interact with the map. We're not going to need this at all for our purposes so we can go ahead and clean things up.</p>
<p>To start, we’re going to open up our <code>index.js</code> file, the homepage file, and get rid of everything inside of the <code>mapEffect</code> function, which leaves us with:</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>Now, let’s remove the <code>Marker</code> component nested inside of our <code>Map</code>, so we end up with:</p>
<pre><code class="lang-jsx">&lt;<span class="hljs-built_in">Map</span> {…mapSettings} /&gt;
</code></pre>
<p>Now that we’re no longer using that functionality, we can get rid of the variables and references at the top of the file, so you can go ahead and remove:</p>
<ul>
<li>useRef</li>
<li>promiseToFlyTo</li>
<li>getCurrentLocation</li>
<li>Marker</li>
<li>gatsby_astronaut</li>
<li>ZOOM</li>
<li>timeToZoom</li>
<li>timeToOpenPopupAfterZoom</li>
<li>timeToUpdatePopupAfterZoom</li>
<li>popupContentHello</li>
<li>popupContentGatsby</li>
<li>markerRef</li>
</ul>
<p><a target="_blank" href="https://github.com/colbyfayock/my-santa-tracker/commit/58106bad98ff7491f56d580d01f70f1400120fce">Follow along with the commit.</a></p>
<h2 id="heading-finding-santa"><strong>Finding Santa</strong></h2>
<p>Now that we’re in a good place, let’s get our hands dirty and find Santa. To do this, we’re going to use Google’s unofficial, undocumented API. This means that it’s possible this API won’t be available the day after this get’s published, but let’s be optimistic.</p>
<p>Additionally, at the time of writing, it’s still showing last year’s destinations, so what we’re really going to be visualizing here is Santa’s previous year’s route, though the hope is this would reset on the 24th and we’ll all be merry!</p>
<p>Before we get Santa, let’s first add a line back to our <code>mapEffect</code> function:</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 } = {}</span>) </span>{
  <span class="hljs-keyword">if</span> ( !leafletElement ) <span class="hljs-keyword">return</span>;
}
</code></pre>
<p>What this will do is prevent the rest of our code from running in the event our map isn't ready yet. The <code>mapEffect</code> function itself, as you can see in the <code>Map</code> component, runs inside of an instance of <code>useEffect</code> passing an argument of a <code>ref</code> to the map, allowing us to run some code after our component renders.</p>
<p>So once we have that line, let’s now fetch Santa’s route inside of our <code>mapEffect</code> function:</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 } = {}</span>) </span>{
  <span class="hljs-keyword">if</span> ( !leafletElement ) <span class="hljs-keyword">return</span>;
  <span class="hljs-keyword">let</span> route, routeJson;
  <span class="hljs-keyword">try</span> {
    route = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'https://firebasestorage.googleapis.com/v0/b/santa-tracker-firebase.appspot.com/o/route%2Fsanta_en.json?alt=media&amp;2018b'</span>);
    routeJson = <span class="hljs-keyword">await</span> route.json();
  } <span class="hljs-keyword">catch</span>(e) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Failed to find Santa!: <span class="hljs-subst">${e}</span>`</span>);
  }
  <span class="hljs-built_in">console</span>.log(‘routeJson’, routeJson);
}
</code></pre>
<p>Let’s break this down:</p>
<ul>
<li>We grab Santa’s route via the API endpoint</li>
<li>Once we have his route, we grab the response in a JSON format to make it easier to work with</li>
<li>This is all wrapped in a try/catch so we can safely handle any response errors</li>
<li>Finally, we just <code>log</code> out our response for now</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/santas-route-object.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Santa's route object in the web console</em></p>
<p>Now we have Santa and his route, which means we can see all the destinations in his route. If you dig in the response a little bit, you can see some fun things like how many presents were delivered to each location and the weather at the time!</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-santa-tracker/commit/f42c48fb0f0d70b4d20f1c2a1410bde1a4f27e84">Follow along with the commit.</a></p>
<h2 id="heading-put-a-pin-in-his-location"><strong>Put a pin in his location</strong></h2>
<p>We found Santa! ? Now let’s put him on the map.</p>
<p>For our purposes, we’ll need to find the latitude and longitude of Santa. The problem is, we don’t get this exact value defined anywhere, we just get his destinations.</p>
<p>Since we don’t have his location specified anywhere, we can utilize his last known location where presents were delivered. Add the following after our last snippet inside the <code>mapEffect</code> function:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { destinations = [] } = routeJson || {};
    <span class="hljs-keyword">const</span> destinationsVisited = destinations.filter(<span class="hljs-function">(<span class="hljs-params">{arrival}</span>) =&gt;</span> arrival &lt; <span class="hljs-built_in">Date</span>.now());
    <span class="hljs-keyword">const</span> destinationsWithPresents = destinationsVisited.filter(<span class="hljs-function">(<span class="hljs-params">{presentsDelivered}</span>) =&gt;</span> presentsDelivered &gt; <span class="hljs-number">0</span>);
<span class="hljs-keyword">const</span> lastKnownDestination = destinationsWithPresents[destinationsWithPresents.length - <span class="hljs-number">1</span>]
</code></pre>
<p>Below our request code, we:</p>
<ul>
<li>Destructure <code>routeJson</code> to grab <code>destinations</code> into a constant, adding a fallback to an empty object</li>
<li>Filter the results to only find the destinations that he's visited, using the arrival time from the route object</li>
<li>Filter the results to find only the locations with presents</li>
<li>And finally grab the last item from the array, which shows his last known location</li>
</ul>
<p>At this point in time, 12/23, we don't actually have any destinations, as Santa is still at the North Pole. At any time, we can test this out to simulate a future date by replaceing <code>Date.now()</code> in <code>destinationsVisited</code> with a future date, such as <code>1577188980000</code> which would be around 7pm Eastern on 12/24. With that change, we can see what Santa's route actually looks like!</p>
<h2 id="heading-handle-a-missing-santa">Handle a missing Santa</h2>
<p>Now that it's close to Christmas, Santa will still be at the North Pole, so let's handle the case where we don't have a location.</p>
<p>Above the line where we set <code>lastKnownDestination</code>, let's add:</p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span> ( destinationsWithPresents.length === <span class="hljs-number">0</span> ) {
  <span class="hljs-comment">// Create a Leaflet Market instance using Santa's LatLng location</span>
  <span class="hljs-keyword">const</span> center = <span class="hljs-keyword">new</span> L.LatLng( <span class="hljs-number">0</span>, <span class="hljs-number">0</span> );
  <span class="hljs-keyword">const</span> noSanta = L.marker( center, {
    <span class="hljs-attr">icon</span>: L.divIcon({
      <span class="hljs-attr">className</span>: <span class="hljs-string">'icon'</span>,
      <span class="hljs-attr">html</span>: <span class="hljs-string">`&lt;div class="icon-santa"&gt;?&lt;/div&gt;`</span>,
      <span class="hljs-attr">iconSize</span>: <span class="hljs-number">50</span>
    })
  });
  noSanta.addTo( leafletElement );
  noSanta.bindPopup( <span class="hljs-string">`Santa's still at the North Pole!`</span> );
  noSanta.openPopup();
  <span class="hljs-keyword">return</span>;
}
</code></pre>
<p>Okay so what are we doing here?</p>
<ul>
<li>First, we’re checking if we have any destinations with presents, which here we don't</li>
<li>We first create a LatLng of the center of the map</li>
<li>We create a Leaflet marker, using that center, with a custom Icon of Santa</li>
<li>Next we add that Santa marker to the leafletElement, which is our map</li>
<li>To show a message, we first bind a popup with a custom message and open it</li>
<li>Finally we return so the rest of the code doesn’t run, as we don’t have Santa at this point</li>
</ul>
<p>This was a section added after published to handle the API resetting, but you can still follow along with the code I added in context of the rest of the rest of the code.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-santa-tracker/blob/master/src/pages/index.js#L40">Follow along in the code.</a></p>
<h2 id="heading-pinning-santa">Pinning Santa</h2>
<p><em>Edit 12/23: This section was originally written with the previous year's API, but this is still a good example of what you'll expect on the response, so you can follow right along.</em></p>
<p>And as we can see, since we’re looking at last year’s data, Santa is back home at the North Pole.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/santas-last-known-destination-object.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Santa's last known location in the web console</em></p>
<p>With his location, we can pull that apart, set up a Leaflet marker instance, and add our old friend to the map. Add the following after our last snippet inside the <code>mapEffect</code> function:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> santaLocation = <span class="hljs-keyword">new</span> L.LatLng( lastKnownDestination.location.lat, lastKnownDestination.location.lng );

<span class="hljs-keyword">const</span> santaMarker = L.marker( santaLocation, {
  <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;div class=“icon-santa”&gt;?&lt;/div&gt;`</span>,
    <span class="hljs-attr">iconSize</span>: <span class="hljs-number">50</span>
  })
});

santaMarker.addTo(leafletElement);
</code></pre>
<p>Here we:</p>
<ul>
<li>Create a Leaflet LatLng instance with his location</li>
<li>Create a Marker instance with our newly created LatLng instance</li>
<li>Add our new Marker to the map</li>
</ul>
<p>If we refresh our page, you’ll have to zoom out and pan up a little bit, but we'll see Santa on the map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/santa-on-the-map.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Santa on the map</em></p>
<p>Before we move on, let’s give Santa a little holiday cheer to make him easier to find. Find your <code>application.scss</code> file and toss these styles in:</p>
<pre><code class="lang-scss"><span class="hljs-comment">// In src/assets/stylesheets/application.scss</span>

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

  &amp; &gt; <span class="hljs-selector-tag">div</span> {

    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">overflow</span>: hidden;
    <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">3px</span> <span class="hljs-number">4px</span> rgba(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,.<span class="hljs-number">4</span>);
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">transition</span>: all .<span class="hljs-number">2s</span>;

    &amp;<span class="hljs-selector-pseudo">:hover</span> {
      <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">4px</span> <span class="hljs-number">8px</span> rgba(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,.<span class="hljs-number">6</span>);
    }

  }

}

<span class="hljs-selector-class">.icon-santa</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">3em</span>;
  <span class="hljs-attribute">background</span>: white;
}
</code></pre>
<p>This just adds a white circle around him, a little drop shadow, and increases the size a bit to make him a little easier to find on the map.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/santa-styled-on-the-map.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Santa styled on the map</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-santa-tracker/commit/1b636107078fce64068ce661903892c095cb4668">Follow along with the commit.</a></p>
<h2 id="heading-drawing-his-route"><strong>Drawing his route</strong></h2>
<p>The last thing we’re going to do here is draw a path on the map showing his route so we can follow along.</p>
<p>To get started, let’s update our code and add this last bit after our last snippet in the <code>mapEffect</code> function:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create a set of LatLng coordinates that make up Santa's route</span>

<span class="hljs-keyword">const</span> santasRouteLatLngs = destinationsWithPresents.map(<span class="hljs-function"><span class="hljs-params">destination</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> { location } = destination;
  <span class="hljs-keyword">const</span> { lat, lng } = location;
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> L.LatLng( lat, lng );
});

<span class="hljs-comment">// Utilize Leaflet's Polyline to add the route to the map</span>

<span class="hljs-keyword">const</span> santasRoute = <span class="hljs-keyword">new</span> L.Polyline( santasRouteLatLngs, {
  <span class="hljs-attr">weight</span>: <span class="hljs-number">2</span>,
  <span class="hljs-attr">color</span>: <span class="hljs-string">'green'</span>,
  <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>,
  <span class="hljs-attr">fillColor</span>: <span class="hljs-string">'green'</span>,
  <span class="hljs-attr">fillOpacity</span>: <span class="hljs-number">0.5</span>
});

<span class="hljs-comment">// Add Santa to the map!</span>

santasRoute.addTo(leafletElement);
</code></pre>
<p>What we’re doing:</p>
<ul>
<li>Creating an array of Leaflet LatLng instances that make up Santa’s route</li>
<li>Creating a Leaflet Polyline (a multi-point line) using that routes array</li>
<li>Make that Polyline green</li>
<li>Add our Polyline to the map</li>
</ul>
<p>What we get… is a bunch of squiggly lines!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/santas-route-on-the-map.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Santa's route on the map</em></p>
<p>This is expected. This gets technical really fast, but Leaflet by default can only understand 1 “portion” of the map as it wraps around in our browser. What this realistically means, is instead of drawing a line around a globe, the coordinates think it goes from one side of the world to the other as it hits the International Dateline. This is a bit out of scope for this tutorial, but you can check out <a target="_blank" href="https://github.com/briannaAndCo/Leaflet.Antimeridian">Leaflet.Antimeridian</a> to learn more and see if you can implement the solution to it.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-santa-tracker/commit/3b0c08f066212ff32c82d3df2a13d1419da8ac41">Follow along with the commit.</a></p>
<h2 id="heading-a-few-quick-style-tweaks"><strong>A few quick style tweaks</strong></h2>
<p>One last thing! And this is completely optional. Let’s make the map a little bit bigger, set the background color to match our oceans, and zoom out a little bit. So let’s make a few changes:</p>
<pre><code class="lang-js"><span class="hljs-comment">// In src/pages/index.js</span>

<span class="hljs-keyword">const</span> DEFAULT_ZOOM = <span class="hljs-number">1</span>;
</code></pre>
<p>We’re setting our default zoom to <code>1</code> instead of <code>2</code> to allow the map to be zoomed out a bit.</p>
<pre><code class="lang-scss"><span class="hljs-comment">// In src/assets/stylesheets/pages/_home.scss</span>

<span class="hljs-selector-class">.page-home</span> {

  <span class="hljs-selector-class">.map</span>,
  <span class="hljs-selector-class">.map-base</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">80vh</span>;
  }

}
</code></pre>
<p>We’re setting our map to a height of <code>80vh</code> instead of <code>50vh</code> to make it take up a little more of our screen.</p>
<pre><code class="lang-scss"><span class="hljs-comment">// In src/assets/stylesheets/components/_map.scss</span>
<span class="hljs-selector-class">.map</span> {

  &amp;,
  <span class="hljs-selector-class">.map-base</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#acd3de</span>;
  }

}
</code></pre>
<p>We’re setting the background color of our map to <code>#acd3de</code> instead of <code>$blue-grey-50</code> which allows us to match the color of the oceans on our map.</p>
<p>What this achieves is being able to see Santa’s full route and Santa on the first view. Additionally, since the map only covers part of the screen, setting the background color of the map allows us to not have a little bit of a weird cutoff.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/santas-route-zoomed-out.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Santa's route zoomed out</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-santa-tracker/commit/882ea5c0b1b48da86d81494b8b4ad5db7bc1bae6">Follow along with the commit.</a></p>
<h2 id="heading-want-a-challenge"><strong>Want a challenge?</strong></h2>
<p>To take this 1 step further, follow along with both how we added the routes and Santa to the map and try to see if you can add a marker to each destination location to show where all of the stops are. Bonus, add a popup to each one that says how many presents were delivered to that location!</p>
<p>To see the answer with some code organization and how I added the gift markers, check out the final version of the <a target="_blank" href="https://github.com/colbyfayock/santa-tracker">Santa Tracker demo</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/final-santa-tracker-demo.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Final Santa Tracker demo</em></p>
<p>While you’re there, you can also see how I utilized Leaflet.Antimeridian to fix our map's route.</p>
<h2 id="heading-what-did-we-learn"><strong>What did we learn?</strong></h2>
<p>Building basic apps with a map isn’t nearly as bad as we thought! We learned how to fetch some data from an API, grab the data we need, and draw representations of that data on a map.</p>
<p>Next time you want to add a map widget to your landing page, try Leaflet. Share what you create on <a target="_blank" href="https://twitter.com/colbyfayock">Twitter</a>! Would love to see what you come up with.</p>
<p>I hope you and your family have a fantastic holiday season!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/12/happy-holidays-dunder-mifflin.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Happy Holidays from Dunder Mifflin</em></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/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/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.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="2000" 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>

<p><em>Want to read some of my other articles? Check out my blog: <a target="_blank" href="https://www.colbyfayock.com/2019/12/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/">https://www.colbyfayock.com/2019/12/create-your-own-santa-tracker-with-gatsby-and-react-leaflet/</a></em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Your Coding Blog From Scratch Using Gatsby and MDX ]]>
                </title>
                <description>
                    <![CDATA[ By Scott Spence I have been a Gatsby user since around version 0 back in May 2017.  Back then, I was using a template called Lumen. It was just what I needed at the time. Since then I have gone from using a template to creating my blog. Over ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-developer-blog-from-scratch-with-gatsby-and-mdx/</link>
                <guid isPermaLink="false">66d85219afbaabf7a144af04</guid>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mdx ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 31 Oct 2019 19:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/10/cover-2.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Scott Spence</p>
<p>I have been a Gatsby user since around <a target="_blank" href="https://github.com/spences10/blog.scottspence.me/tree/a470e8563e1a040527cf2094fc1b377550a88c77">version 0 back in May 2017</a>. </p>
<p>Back then, I was using a template called <a target="_blank" href="https://github.com/alxshelepenok/gatsby-starter-lumen">Lumen</a>. It was just what I needed at the time. Since then I have gone from using a template to creating my blog.</p>
<p>Over the years I have built my own <a target="_blank" href="https://lengstorf.com/progressive-disclosure-of-complexity/">Progressive Disclosure of Complexity</a> with Gatsby to where I am now.</p>
<h2 id="heading-what-does-that-mean">What does that mean?</h2>
<p>It means that although there are an awesome amount of Gatsby starters and themes out there to get you up and running in minutes, this post is going to focus on what you need to do to build your own blog. Starting with the most basic “Hello World!” to deploying your code to production.</p>
<h2 id="heading-what-youre-going-to-build">What you’re going to build</h2>
<p>You’re going to build a developer blog with MDX support (for some React components in Markdown goodness), so you will be able to add your own React components into your Markdown posts.</p>
<p><strong>There’ll be:</strong></p>
<ul>
<li>Adding a Layout</li>
<li>Basic styling with styled-components</li>
<li>Code blocks with syntax highlighting</li>
<li>Copy code snippet to clipboard</li>
<li>Cover images for the posts</li>
<li>Configuring an SEO component</li>
<li>Deploying it to Netlify</li>
</ul>
<h2 id="heading-who-is-this-how-to-for">Who is this how-to for?</h2>
<p>People that may have used Gatsby before as a template and now want to get more involved in how to make changes.</p>
<p>If you want to have code syntax highlighting.</p>
<p>If you want to use styled-components in an app.</p>
<p><strong>I really want to avoid this!</strong></p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/ossia/status/588389121053200385"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h2 id="heading-requirements">Requirements</h2>
<p>You’re going to need a basic web development setup: node, terminal (bash, zsh or fish) and a text editor.</p>
<p>I do like to use <a target="_blank" href="https://codesandbox.io/">codesandbox.io</a> for these sort of guides to reduce the barrier to entry but in this case I have found there are some limitations with starting out from scratch on <a target="_blank" href="https://codesandbox.io/">codesandbox.io</a> which doesn’t make this possible.</p>
<p>I have made a guide on getting set up for web development with <a target="_blank" href="http://blog.scottspence.me/wsl-bootstrap-2019">Windows Web-Dev Bootstrap</a> and covered the same process in <a target="_blank" href="https://www.youtube.com/watch?v=eSAsdQuQ-1o">Ubuntu as well</a>.</p>
<p>Ok? Time to get started!</p>
<h2 id="heading-hello-world">Hello World</h2>
<p>To kick this off with the Gatsby ‘hello world’, you’ll need to initialise the project with:</p>
<pre><code class="lang-bash">npm init -y
git init
</code></pre>
<p>I suggest that you commit this code to a git repository, so you should start with a <code>.gitignore</code> file.</p>
<pre><code class="lang-bash">touch .gitignore

<span class="hljs-built_in">echo</span> <span class="hljs-string">"# Project dependencies
.cache
node_modules

# Build directory
public

# Other
.DS_Store
yarn-error.log"</span> &gt; .gitignore
</code></pre>
<p>Ok now is a good time to do a <code>git init</code> and if you’re using VSCode you’ll see the changes reflected in the sidebar.</p>
<h3 id="heading-basic-hello-world">basic hello world</h3>
<p>Ok a Gatsby hello world, get started with the bare minimum! Install the following:</p>
<pre><code class="lang-bash">yarn add gatsby react react-dom
</code></pre>
<p>You’re going to need to create a pages directory and add an index file. You can do that in the terminal by typing the following:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># -p is to create parent directories too if needed</span>
mkdir -p src/pages
touch src/pages/index.js
</code></pre>
<p>Now you can commence the hello word incantation! In the newly created <code>index.js</code> enter the following:</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;<span class="hljs-name">h1</span>&gt;</span>Hello World!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>;
};
</code></pre>
<p>Now you need to add the Gatsby develop script to the <code>package.json</code> file, <code>-p</code> specifies what port you want to run the project on and <code>-o</code> opens a new tab on your default browser, so in this case <code>localhost:9988</code>:</p>
<pre><code class="lang-json"><span class="hljs-string">"dev"</span>: <span class="hljs-string">"gatsby develop -p 9988 -o"</span>
</code></pre>
<p>And now it’s time to run the code! From the terminal type the npm script command you just created:</p>
<pre><code class="lang-bash">yarn dev
</code></pre>
<blockquote>
<p>Note I’m using Yarn for installing all my dependencies and running scripts. If you prefer you can use npm, just bear in mind that the content on here uses yarn, so swap out commands where needed.</p>
</blockquote>
<p>And with that the “Hello World” incantation is complete ?!</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/2vP3ZTDbN1g" 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-add-content">Add content</h2>
<p>Now you have the base for your blog you’re going to want to add some content. First up we’re going to get the convention out of the way. For this how-to, the date format will be logical – the most logical way for a date format is <strong>YYYYMMDD</strong>, fight me!</p>
<p>So you’re going to structure your posts content in years. In each one of those you’re going to have another folder relating to the post with the (correct) date format for the beginning of the file followed by the title of the post. </p>
<p>You could drill into this further if you like by separating out months and days. Depending on the volume of posts you've got going this may be a good approach. In this case and in the examples provided the convention detailed will be used.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># create multiple directories using curly braces</span>
mkdir -p posts/2019/{2019-06-01-hello-world,2019-06-10-second-post,2019-06-20-third-post}
touch posts/2019/2019-06-01-hello-world/index.mdx
touch posts/2019/2019-06-10-second-post/index.mdx
touch posts/2019/2019-06-20-third-post/index.mdx
</code></pre>
<p>Ok that’s how to set up your posts. Now you need to add some content to them, so each file you have in here should have frontmatter. Frontmatter is a way to assign properties to the contents, in this case a <code>title</code>, published <code>date</code> and a <code>published</code> flag (<code>true</code> or <code>false</code>).</p>
<pre><code class="lang-md">---
title: Hello World - from mdx!
date: 2019-06-01
<span class="hljs-section">published: true
---</span>

<span class="hljs-section"># h1 Heading</span>

My first post!!

<span class="hljs-section">## h2 Heading</span>

<span class="hljs-section">### h3 Heading</span>
</code></pre>
<pre><code class="lang-md">---
title: Second Post!
date: 2019-06-10
<span class="hljs-section">published: true
---</span>

This is my second post!

<span class="hljs-section">#### h4 Heading</span>

<span class="hljs-section">##### h5 Heading</span>

<span class="hljs-section">###### h6 Heading</span>
</code></pre>
<pre><code class="lang-md">---
title: Third Post!
date: 2019-06-20
<span class="hljs-section">published: true
---</span>

This is my third post!

<span class="hljs-quote">&gt; with a block quote!</span>
</code></pre>
<h2 id="heading-gatsby-config-api">Gatsby config API</h2>
<p>Next, you’re going to configure Gatsby so that it can read your super awesome content you just created. First up you need to create a the <code>gatsby-config.js</code> file. In the terminal create the file:</p>
<pre><code class="lang-bash">touch gatsby-config.js
</code></pre>
<h2 id="heading-plugins">Plugins</h2>
<p>And now you can add the plugins Gatsby needs to use for sourcing and displaying the the files you just created.</p>
<h3 id="heading-gatsby-source-filesystem">Gatsby source filesystem</h3>
<p>The <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-source-filesystem/">gatsby-source-filesystem</a> collects the files on the local filesystem for use in Gatsby once configured.</p>
<h3 id="heading-gatsby-plugin-mdx">Gatsby plugin MDX</h3>
<p>The <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-plugin-mdx/">gatsby-plugin-mdx</a> is what will be allowing us to write JSX in our Markdown documents and the heart of how the content is displayed in the blog.</p>
<p>Now is a good time to also add in dependent packages for the Gatsby plugin MDX which are <code>@mdx-js/mdx</code> and <code>@mdx-js/react</code>.</p>
<p>In the terminal install the dependencies:</p>
<pre><code class="lang-bash">yarn add gatsby-plugin-mdx @mdx-js/mdx @mdx-js/react gatsby-source-filesystem
</code></pre>
<p>Now it's time to configure <code>gatsby-config.js</code>:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">siteMetadata</span>: {
    <span class="hljs-attr">title</span>: <span class="hljs-string">`The Localhost Blog`</span>,
    <span class="hljs-attr">description</span>: <span class="hljs-string">`This is my coding blog where I write about my coding journey.`</span>,
  },
  <span class="hljs-attr">plugins</span>: [
    {
      <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-plugin-mdx`</span>,
      <span class="hljs-attr">options</span>: {
        <span class="hljs-attr">extensions</span>: [<span class="hljs-string">`.mdx`</span>, <span class="hljs-string">`.md`</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>/posts`</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">`posts`</span>,
      },
    },
  ],
};
</code></pre>
<h2 id="heading-query-data-from-graphql">Query data from GraphQL</h2>
<p>Now you can see what the <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-source-filesystem/">gatsby-source-filesystem</a> and <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-plugin-mdx/">gatsby-plugin-mdx</a> have done for us. You can now go to the Gatsby GraphQL GraphiQL explorer and check out the data:</p>
<pre><code class="lang-graphql">{
  allMdx {
    nodes {
      frontmatter {
        title
        date
      }
    }
  }
}
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/MPNJu24ad_s" 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-site-metadata">Site Metadata</h2>
<p>When you want to reuse common pieces of data across the site (for example, your site title), you can store that data in <code>siteMetadata</code>. You touched on this when defining the <code>gatsby-config.js</code>, and now you’re going to separate this out from the <code>module.exports</code>. Why? It will be nicer to reason about once the config is filled with plugins. </p>
<p>At the top of <code>gatsby-config.js</code> add a new object variable for the site metadata:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> siteMetadata = {
  <span class="hljs-attr">title</span>: <span class="hljs-string">`The Localhost Blog`</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">`This is my coding blog where I write about my coding journey.`</span>,
};
</code></pre>
<p>Now query the Site Metadata with GraphQL.</p>
<pre><code class="lang-graphql">{
  site {
    siteMetadata {
      title
      description
    }
  }
}
</code></pre>
<h2 id="heading-site-metadata-hook">Site metadata hook</h2>
<p>Ok, so, that’s cool n’ all but how am I meant to use it? We'll do some of the code stuff and make a React hook so you can get your site data in any component you need it.</p>
<p>Create a folder to keep all your hooks in and create a file for our hook. In the terminal do:</p>
<pre><code class="lang-bash">mkdir src/hooks
touch src/hooks/useSiteMetadata.js
</code></pre>
<p>Ok, and in your newly created file were going to use the Gatsby <code>useStaticQuery</code> hook to make your own hook:</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">const</span> useSiteMetadata = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { site } = useStaticQuery(
    graphql<span class="hljs-string">`
      query SITE_METADATA_QUERY {
        site {
          siteMetadata {
            title
            description
          }
        }
      }
    `</span>
  );
  <span class="hljs-keyword">return</span> site.siteMetadata;
};
</code></pre>
<p>Now you can use this hook anywhere in your site, so do that now in <code>src/pages/index.js</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> { useSiteMetadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'../hooks/useSiteMetadata'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> () =&gt; {
  <span class="hljs-keyword">const</span> { title, description } = useSiteMetadata();
  <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>{title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{description}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/TfycpV4yyqY" 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-styling">Styling</h2>
<p>You’re going to use styled-components for styling. Styled-components (for me) help with scoping styles in your components. Time to go over the basics now.</p>
<h3 id="heading-install-styled-components">install styled-components</h3>
<pre><code class="lang-bash">yarn add gatsby-plugin-styled-components styled-components babel-plugin-styled-components
</code></pre>
<p>So, what was all that I just installed?</p>
<p>The babel plugin is for automatic naming of components to help with debugging.</p>
<p>The Gatsby plugin is for built-in server-side rendering support.</p>
<h3 id="heading-configure">Configure</h3>
<p>Ok, with that detailed explanation out of the way, configure them in <code>gatsby-config.js</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> siteMetadata = {
  <span class="hljs-attr">title</span>: <span class="hljs-string">`The Localhost Blog`</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">`This is my coding blog where I write about my coding journey.`</span>,
};

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">siteMetadata</span>: siteMetadata,
  <span class="hljs-attr">plugins</span>: [
    <span class="hljs-string">`gatsby-plugin-styled-components`</span>,
    {
      <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-plugin-mdx`</span>,
      <span class="hljs-attr">options</span>: {
        <span class="hljs-attr">extensions</span>: [<span class="hljs-string">`.mdx`</span>, <span class="hljs-string">`.md`</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>/posts`</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">`posts`</span> },
    },
  ],
};
</code></pre>
<p>Time to go over a styled component. In  <code>index.js</code> you’re going to <code>import styled from 'styled-components'</code> and create a <code>StyledH1</code> variable.</p>
<p>So, you’re using the variable to wrap your <code>{title}</code> that you’re destructuring from the <code>useSiteMetadata</code> hook you made previously.</p>
<p>For this example make it the now iconic Gatsby <code>rebeccapurple</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> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components'</span>;
<span class="hljs-keyword">import</span> { useSiteMetadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'../hooks/useSiteMetadata'</span>;

<span class="hljs-keyword">const</span> StyledH1 = styled.h1<span class="hljs-string">`
  color: rebeccapurple;
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> () =&gt; {
  <span class="hljs-keyword">const</span> { title, description } = useSiteMetadata();
  <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">StyledH1</span>&gt;</span>{title}<span class="hljs-tag">&lt;/<span class="hljs-name">StyledH1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{description}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};
</code></pre>
<p>That is styled-components on a very basic level. Basically create the styling you want for your page elements you’re creating in the JSX.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/41aNkb2tLyg" 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-layout">Layout</h2>
<p>Gatsby doesn’t apply any layouts by default but instead uses the way you can compose React components for the layout. This means it’s up to you how you want to layout what you're building with Gatsby. </p>
<p>In this guide we're going to initially create a basic layout component that you’ll add to as you go along. For more detail on layout components take a look at the Gatsby <a target="_blank" href="https://www.gatsbyjs.org/docs/layout-components/">layout components</a> page.</p>
<p>Now you’re going to refactor the home page (<code>src/pages/index.js</code>) a little and make some components for your blog layout and header. In the terminal create a components directory and a <code>Header</code> and <code>Layout</code> component:</p>
<pre><code class="lang-bash">mkdir src/components
touch src/components/Header.js src/components/Layout.js
</code></pre>
<p>Now to move the title and description from <code>src/pages/index.js</code> to the newly created <code>src/components/Header.js</code> component, destructuring props for the <code>siteTitle</code> and <code>siteDescription</code>, you’ll pass these from the <code>Layout</code> component to here. You’re going to add Gatsby Link to this so users can click on the header to go back to the home page.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby'</span>;
<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">const</span> Header = <span class="hljs-function">(<span class="hljs-params">{ siteTitle, siteDescription }</span>) =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{siteTitle}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{siteDescription}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span></span>
);
</code></pre>
<p>Now to the Layout component: this is going to be a basic wrapper component for now. You’re going to use your site metadata hook for the title and description and pass them to the header component and return the children of the wrapper (<code>Layout</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> { useSiteMetadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'../hooks/useSiteMetadata'</span>;
<span class="hljs-keyword">import</span> { Header } <span class="hljs-keyword">from</span> <span class="hljs-string">'./Header'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Layout = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { title, description } = useSiteMetadata();
  <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">Header</span> <span class="hljs-attr">siteTitle</span>=<span class="hljs-string">{title}</span> <span class="hljs-attr">siteDescription</span>=<span class="hljs-string">{description}</span> /&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};
</code></pre>
<p>Now to add the slightest of styles for some alignment for <code>src/components/Layout.js</code>, create an <code>AppStyles</code> styled component and make it the main wrapper of your <code>Layout</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> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components'</span>;
<span class="hljs-keyword">import</span> { useSiteMetadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'../hooks/useSiteMetadata'</span>;
<span class="hljs-keyword">import</span> { Header } <span class="hljs-keyword">from</span> <span class="hljs-string">'./Header'</span>;

<span class="hljs-keyword">const</span> AppStyles = styled.main<span class="hljs-string">`
  width: 800px;
  margin: 0 auto;
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Layout = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { title, description } = useSiteMetadata();
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AppStyles</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> <span class="hljs-attr">siteTitle</span>=<span class="hljs-string">{title}</span> <span class="hljs-attr">siteDescription</span>=<span class="hljs-string">{description}</span> /&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">AppStyles</span>&gt;</span></span>
  );
};
</code></pre>
<p>Ok, now refactor your homepage (<code>src/pages/index.js</code>) with <code>Layout</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> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Layout'</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">Layout</span> /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Ase7bjxtQ3w" 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-index-page-posts-query">Index page posts query</h2>
<p>Now you can take a look at getting some of the posts you’ve created added to the index page of your blog. You’re going to do that by creating a GraphQL query to list out the posts by title, order by date, and add an excerpt of the post.</p>
<p>The query will look something like this:</p>
<pre><code class="lang-graphql">{
  allMdx {
    nodes {
      id
      excerpt(<span class="hljs-symbol">pruneLength:</span> <span class="hljs-number">250</span>)
      frontmatter {
        title
        date
      }
    }
  }
}
</code></pre>
<p>If you put that into the GraphiQL GUI, though, you’ll notice that the posts aren’t in any given order. So now add a sort to this - and you’ll also add in a filter for posts that are marked as published or not.</p>
<pre><code class="lang-graphql">{
  allMdx(
    <span class="hljs-symbol">sort:</span> { <span class="hljs-symbol">fields:</span> [frontmatter___date], <span class="hljs-symbol">order:</span> DESC }
    <span class="hljs-symbol">filter:</span> { <span class="hljs-symbol">frontmatter:</span> { <span class="hljs-symbol">published:</span> { <span class="hljs-symbol">eq:</span> <span class="hljs-variable">true</span> } } }
  ) {
    nodes {
      id
      excerpt(<span class="hljs-symbol">pruneLength:</span> <span class="hljs-number">250</span>)
      frontmatter {
        title
        date
      }
    }
  }
}
</code></pre>
<p>On the homepage (<code>src/pages/index.js</code>) you’re going to use the query we just put together to get a list of published posts in date order; add the following to the <code>index.js</code> file:</p>
<pre><code class="lang-js"><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> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</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">export</span> <span class="hljs-keyword">default</span> ({ data }) =&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">Layout</span>&gt;</span>
        {data.allMdx.nodes.map(({ excerpt, frontmatter }) =&gt; (
          <span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{frontmatter.date}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{excerpt}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <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;/&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date
        }
      }
    }
  }
`</span>;
</code></pre>
<p>Woah! WTF was all that yo!?</p>
<p>Ok, you’re looping through the data passed into the component via the GraphQL query. Gatsby <code>graphql</code> runs the query (<code>SITE_INDEX_QUERY</code>) at runtime and gives us the results as props to your component via the <code>data</code> prop.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/2GDbxZ0mHbM" 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-slugs-and-paths">Slugs and Paths</h2>
<p>Gatsby source filesystem will help with the creation of slugs (URL paths for the posts you’re creating). In Gatsby node you’re going to create the slugs for your posts.</p>
<p>First up you’re going to need to create a <code>gatsby-node.js</code> file:</p>
<pre><code class="lang-bash">touch gatsby-node.js
</code></pre>
<p>This will create the file path (URL) for each of the blog posts.</p>
<p>You’re going to be using the Gatsby Node API <code>onCreateNode</code> and destructuring out <code>node</code>, <code>actions</code> and <code>getNode</code> for use in creating the file locations and associated value.</p>
<pre><code class="lang-ja">const { createFilePath } = require(`gatsby-source-filesystem`);

exports.onCreateNode = ({ node, actions, getNode }) =&gt; {
  const { createNodeField } = actions;
  if (node.internal.type === `Mdx`) {
    const value = createFilePath({ node, getNode });
    createNodeField({
      name: `slug`,
      node,
      value,
    });
  }
};
</code></pre>
<p>Now to help visualise some of the data being passed into the components you’re going to use <a target="_blank" href="https://github.com/wesbos/dump">Dump.js</a> for debugging the data. Thanks to Wes Bos for the super handy <a target="_blank" href="https://github.com/wesbos/dump">Dump.js</a> component.</p>
<p>To get the component set up, create a <code>Dump.js</code> file in your <code>src\components</code> folder and copypasta the code from the linked GitHub page.</p>
<pre><code class="lang-bash">touch /src/components/Dump.js
</code></pre>
<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">const</span> Dump = <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> (
  <span class="xml"><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">fontSize:</span> <span class="hljs-attr">20</span>,
      <span class="hljs-attr">border:</span> '<span class="hljs-attr">1px</span> <span class="hljs-attr">solid</span> #<span class="hljs-attr">efefef</span>',
      <span class="hljs-attr">padding:</span> <span class="hljs-attr">10</span>,
      <span class="hljs-attr">background:</span> '<span class="hljs-attr">white</span>',
    }}&gt;</span>
    {Object.entries(props).map(([key, val]) =&gt; (
      <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{key}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">color:</span> '<span class="hljs-attr">white</span>', <span class="hljs-attr">background:</span> '<span class="hljs-attr">red</span>' }}&gt;</span>
          {key} ?
        <span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>
        {JSON.stringify(val, '', ' ')}
      <span class="hljs-tag">&lt;/<span class="hljs-name">pre</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> Dump;
</code></pre>
<p>Now you can use the <code>Dump</code> component anywhere in your project. To demonstrate, use it with the index page <code>data</code> to see the output.</p>
<p>So in the <code>src/pages/index.js</code> you’re going to import the Dump component and pass in the <code>data</code> prop and see what the output looks like.</p>
<pre><code class="lang-js"><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> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Dump <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Dump'</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">export</span> <span class="hljs-keyword">default</span> ({ data }) =&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">Layout</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Dump</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{data}</span> /&gt;</span>
        {data.allMdx.nodes.map(({ excerpt, frontmatter }) =&gt; (
          <span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{frontmatter.date}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{excerpt}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <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;/&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date
        }
      }
    }
  }
`</span>;
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/7eZZk7aJnUU" 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-link-paths">Link Paths</h2>
<p>Now you’ve created the paths you can link to them with Gatsby Link. First you’ll need to add the slug to your <code>SITE_INDEX_QUERY</code> Then you can add gatsby <code>Link</code> to <code>src/pages/index.js</code>.</p>
<p>You’re also going to create some styled-components for wrapping the list of posts and each individual post as well.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { graphql, Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby'</span>;
<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> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components'</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">const</span> IndexWrapper = styled.main<span class="hljs-string">``</span>;

<span class="hljs-keyword">const</span> PostWrapper = styled.div<span class="hljs-string">``</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ({ data }) =&gt; {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">IndexWrapper</span>&gt;</span>
        {data.allMdx.nodes.map(
          ({ id, excerpt, frontmatter, fields }) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">PostWrapper</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{fields.slug}</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{frontmatter.date}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{excerpt}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">PostWrapper</span>&gt;</span>
          )
        )}
      <span class="hljs-tag">&lt;/<span class="hljs-name">IndexWrapper</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date
        }
        fields {
          slug
        }
      }
    }
  }
`</span>;
</code></pre>
<h2 id="heading-adding-a-blog-post-template">Adding a Blog Post Template</h2>
<p>Now you have the links pointing to the blog posts you currently have no file associated with the path. This means that clicking a link will give you a 404 and the built-in gatsby 404 will list all the pages available in the project, currently only the <code>/</code> index/homepage.</p>
<p>So, for each one of your blog posts you’re going to use a template that will contain the information you need to make up your blog post. To start, create a <code>templates</code> directory and template file for that with:</p>
<pre><code class="lang-bash">mkdir -p src/templates
touch src/templates/blogPostTemplate.js
</code></pre>
<p>For now you’re going to scaffold out a basic template (you’ll be adding data to this shortly):</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">p</span>&gt;</span>post here<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};
</code></pre>
<p>To populate the template you’ll need to use Gatsby node to create your pages.</p>
<p>Gatsby Node has many internal APIs available to us. For this example you’re going to be using the <code>createPages</code> API.</p>
<p>More info on Gatsby <code>createPages</code> API can be found on the Gatsby docs, details here: <a target="_blank" href="https://www.gatsbyjs.org/docs/node-apis/#createPages">https://www.gatsbyjs.org/docs/node-apis/#createPages</a></p>
<p>In your <code>gatsby-node.js</code> file you’re going to add in the following in addition to the <code>onCreateNode</code> export you did earlier.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { createFilePath } = <span class="hljs-built_in">require</span>(<span class="hljs-string">`gatsby-source-filesystem`</span>);
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">`path`</span>);

<span class="hljs-built_in">exports</span>.createPages = <span class="hljs-function">(<span class="hljs-params">{ actions, graphql }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { createPage } = actions;
  <span class="hljs-keyword">const</span> blogPostTemplate = path.resolve(
    <span class="hljs-string">'src/templates/blogPostTemplate.js'</span>
  );

  <span class="hljs-keyword">return</span> graphql(<span class="hljs-string">`
    {
      allMdx {
        nodes {
          fields {
            slug
          }
          frontmatter {
            title
          }
        }
      }
    }
  `</span>).then(<span class="hljs-function"><span class="hljs-params">result</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (result.errors) {
      <span class="hljs-keyword">throw</span> result.errors;
    }

    <span class="hljs-keyword">const</span> posts = result.data.allMdx.nodes;

    <span class="hljs-comment">// create page for each mdx file</span>
    posts.forEach(<span class="hljs-function"><span class="hljs-params">post</span> =&gt;</span> {
      createPage({
        <span class="hljs-attr">path</span>: post.fields.slug,
        <span class="hljs-attr">component</span>: blogPostTemplate,
        <span class="hljs-attr">context</span>: {
          <span class="hljs-attr">slug</span>: post.fields.slug,
        },
      });
    });
  });
};

<span class="hljs-built_in">exports</span>.onCreateNode = <span class="hljs-function">(<span class="hljs-params">{ node, actions, getNode }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { createNodeField } = actions;
  <span class="hljs-keyword">if</span> (node.internal.type === <span class="hljs-string">`Mdx`</span>) {
    <span class="hljs-keyword">const</span> value = createFilePath({ node, getNode });
    createNodeField({
      <span class="hljs-attr">name</span>: <span class="hljs-string">`slug`</span>,
      node,
      value,
    });
  }
};
</code></pre>
<p>The part that you need to pay particular attention to right now is the <code>.forEach</code> loop where you’re using the <code>createPage</code> function we destructured from the <code>actions</code> object.</p>
<p>This is where you pass the data needed by <code>blogPostTemplate</code> you defined earlier. You’re going to be adding more to the <code>context</code> for post navigation soon.</p>
<pre><code class="lang-js"><span class="hljs-comment">// create page for each mdx node</span>
posts.forEach(<span class="hljs-function"><span class="hljs-params">post</span> =&gt;</span> {
  createPage({
    <span class="hljs-attr">path</span>: post.fields.slug,
    <span class="hljs-attr">component</span>: blogPostTemplate,
    <span class="hljs-attr">context</span>: {
      <span class="hljs-attr">slug</span>: post.fields.slug,
    },
  });
});
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/OyQfIvXr4YA" 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-build-out-blog-post-template">Build out Blog Post Template</h2>
<p>Now you’re going to take the context information passed to the <code>blogPostTemplate.js</code> to make the blog post page.</p>
<p>This is similar to the <code>index.js</code> homepage, whereas there’s GraphQL data used to create the page. But in this instance the template uses a variable (also known as a parameter or an identifier) so you can query data specific to that given variable.</p>
<p>Now quickly dig into that with a demo. In the GraphiQL GUI, create a named query and define the variable you’re going to pass in:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">query</span> PostBySlug(<span class="hljs-variable">$slug</span>: String!) {
  mdx(<span class="hljs-symbol">fields:</span> { <span class="hljs-symbol">slug:</span> { <span class="hljs-symbol">eq:</span> <span class="hljs-variable">$slug</span> } }) {
    frontmatter {
      title
      date(<span class="hljs-symbol">formatString:</span> <span class="hljs-string">"YYYY MMMM Do"</span>)
    }
  }
}
</code></pre>
<p>Here you’re defining the variable as slug with the <code>$</code> denoting that it’s a variable. You also need to define the variable type as (in this case) <code>String!</code>. The exclamation after the type means that it has to be a string being passed into the query.</p>
<p>Using <code>mdx</code> you’re going to filter on <code>fields</code> where the <code>slug</code> matches the variable being passed into the query.</p>
<p>Running the query now will show an error as there’s no variable being fed into the query. If you look to the bottom of the query pane you should notice <code>QUERY VARIABLES</code>. Click on that to bring up the variables pane.</p>
<p>This is where you can add in one of the post paths you created earlier. If you have your dev server up and running, go to one of the posts and take the path and paste it into the quotes <code>""</code> and try running the query again.</p>
<pre><code class="lang-graphql">{
  <span class="hljs-string">"slug"</span>: <span class="hljs-string">"/2019/2019-06-20-third-post/"</span>
}
</code></pre>
<p>Time to use that data to make the post. You’re going to add <code>body</code> to the query and have that at the bottom of your page file.</p>
<p>Right now you’re going to create a simple react component that will display the data you have queried.</p>
<p>Destructuring the <code>frontmatter</code> and <code>body</code> from the GraphQL query, you’ll get the Title and the Data from the frontmatter object and wrap the <code>body</code> in the <code>MDXRenderer</code>.</p>
<pre><code class="lang-js"><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> { MDXRenderer } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby-plugin-mdx'</span>;
<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> { Layout } <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Layout'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ({ data }) =&gt; {
  <span class="hljs-keyword">const</span> { frontmatter, body } = data.mdx;
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{frontmatter.date}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">MDXRenderer</span>&gt;</span>{body}<span class="hljs-tag">&lt;/<span class="hljs-name">MDXRenderer</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query PostsBySlug($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      body
      frontmatter {
        title
        date(formatString: "YYYY MMMM Do")
      }
    }
  }
`</span>;
</code></pre>
<p>If you haven’t done so already now would be a good time to restart your dev server.</p>
<p>Now you can click on one of the post links and see your blog post template in all it’s basic glory!</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/S7cnkRoCjsc" 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-previous-and-next-navigation">Previous and Next navigation</h2>
<p>Coolio! Now you have your basic blog where you can list available posts and click a link to see the full post in a predefined template. Once you’re in a post you have to navigate back to the home page to pick out a new post to read. In this section you’re going to work on adding in some previous and next navigation.</p>
<p>Remember the <code>.forEach</code> snippet you looked at earlier? That’s where you’re going to pass some additional context to the page by selecting out the previous and next posts.</p>
<pre><code class="lang-js"><span class="hljs-comment">// create page for each mdx node</span>
posts.forEach(<span class="hljs-function">(<span class="hljs-params">post, index</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> previous =
    index === posts.length - <span class="hljs-number">1</span> ? <span class="hljs-literal">null</span> : posts[index + <span class="hljs-number">1</span>];
  <span class="hljs-keyword">const</span> next = index === <span class="hljs-number">0</span> ? <span class="hljs-literal">null</span> : posts[index - <span class="hljs-number">1</span>];

  createPage({
    <span class="hljs-attr">path</span>: post.fields.slug,
    <span class="hljs-attr">component</span>: blogPostTemplate,
    <span class="hljs-attr">context</span>: {
      <span class="hljs-attr">slug</span>: post.fields.slug,
      previous,
      next,
    },
  });
});
</code></pre>
<p>So this should now match up with the query you have on the homepage (<code>src/pages/index.js</code>) except you currently have no filter or sort applied here. So do that now in <code>gatsby-node.js</code> and apply the same filters as on the homepage query:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { createFilePath } = <span class="hljs-built_in">require</span>(<span class="hljs-string">`gatsby-source-filesystem`</span>);
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">`path`</span>);

<span class="hljs-built_in">exports</span>.createPages = <span class="hljs-function">(<span class="hljs-params">{ actions, graphql }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { createPage } = actions;
  <span class="hljs-keyword">const</span> blogPostTemplate = path.resolve(
    <span class="hljs-string">'src/templates/blogPostTemplate.js'</span>
  );

  <span class="hljs-keyword">return</span> graphql(<span class="hljs-string">`
    {
      allMdx(
        sort: { fields: [frontmatter___date], order: DESC }
        filter: { frontmatter: { published: { eq: true } } }
      ) {
        nodes {
          fields {
            slug
          }
          frontmatter {
            title
          }
        }
      }
    }
  `</span>).then(<span class="hljs-function"><span class="hljs-params">result</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (result.errors) {
      <span class="hljs-keyword">throw</span> result.errors;
    }

    <span class="hljs-keyword">const</span> posts = result.data.allMdx.nodes;

    <span class="hljs-comment">// create page for each mdx node</span>
    posts.forEach(<span class="hljs-function">(<span class="hljs-params">post, index</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> previous =
        index === posts.length - <span class="hljs-number">1</span> ? <span class="hljs-literal">null</span> : posts[index + <span class="hljs-number">1</span>];
      <span class="hljs-keyword">const</span> next = index === <span class="hljs-number">0</span> ? <span class="hljs-literal">null</span> : posts[index - <span class="hljs-number">1</span>];

      createPage({
        <span class="hljs-attr">path</span>: post.fields.slug,
        <span class="hljs-attr">component</span>: blogPostTemplate,
        <span class="hljs-attr">context</span>: {
          <span class="hljs-attr">slug</span>: post.fields.slug,
          previous,
          next,
        },
      });
    });
  });
};

<span class="hljs-built_in">exports</span>.onCreateNode = <span class="hljs-function">(<span class="hljs-params">{ node, actions, getNode }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { createNodeField } = actions;
  <span class="hljs-keyword">if</span> (node.internal.type === <span class="hljs-string">`Mdx`</span>) {
    <span class="hljs-keyword">const</span> value = createFilePath({ node, getNode });
    createNodeField({
      <span class="hljs-attr">name</span>: <span class="hljs-string">`slug`</span>,
      node,
      value,
    });
  }
};
</code></pre>
<p>Now you will be able to expose the <code>previous</code> and <code>next</code> objects passed in as context from Gatsby node.</p>
<p>You can destructure <code>previous</code> and <code>next</code> from <code>pageContext</code> and for now pop them into your super handy <code>Dump</code> component to take a look at their contents.</p>
<pre><code class="lang-js"><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> { MDXRenderer } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby-plugin-mdx'</span>;
<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> Dump <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Dump'</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">export</span> <span class="hljs-keyword">default</span> ({ data, pageContext }) =&gt; {
  <span class="hljs-keyword">const</span> { frontmatter, body } = data.mdx;
  <span class="hljs-keyword">const</span> { previous, next } = pageContext;
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Dump</span> <span class="hljs-attr">previous</span>=<span class="hljs-string">{previous}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Dump</span> <span class="hljs-attr">next</span>=<span class="hljs-string">{next}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{frontmatter.date}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">MDXRenderer</span>&gt;</span>{body}<span class="hljs-tag">&lt;/<span class="hljs-name">MDXRenderer</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query PostsBySlug($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      body
      frontmatter {
        title
        date(formatString: "YYYY MMMM Do")
      }
    }
  }
`</span>;
</code></pre>
<p>Add in previous and next navigation, this is a couple of ternary operations. If the variable is empty then return <code>null</code> else render a Gatsby <code>Link</code> component with the page slug and the frontmatter title:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { graphql, Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby'</span>;
<span class="hljs-keyword">import</span> { MDXRenderer } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby-plugin-mdx'</span>;
<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> Dump <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Dump'</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">export</span> <span class="hljs-keyword">default</span> ({ data, pageContext }) =&gt; {
  <span class="hljs-keyword">const</span> { frontmatter, body } = data.mdx;
  <span class="hljs-keyword">const</span> { previous, next } = pageContext;
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Dump</span> <span class="hljs-attr">previous</span>=<span class="hljs-string">{previous}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Dump</span> <span class="hljs-attr">next</span>=<span class="hljs-string">{next}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{frontmatter.date}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">MDXRenderer</span>&gt;</span>{body}<span class="hljs-tag">&lt;/<span class="hljs-name">MDXRenderer</span>&gt;</span>
      {previous === false ? null : (
        <span class="hljs-tag">&lt;&gt;</span>
          {previous &amp;&amp; (
            <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{previous.fields.slug}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{previous.frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
          )}
        <span class="hljs-tag">&lt;/&gt;</span></span>
      )}
      {next === <span class="hljs-literal">false</span> ? <span class="hljs-literal">null</span> : (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
          {next &amp;&amp; (
            <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{next.fields.slug}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{next.frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
          )}
        <span class="hljs-tag">&lt;/&gt;</span></span>
      )}
    &lt;/Layout&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query PostsBySlug($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      body
      frontmatter {
        title
        date(formatString: "YYYY MMMM Do")
      }
    }
  }
`</span>;
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/AjLimYEwDOk" 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-code-blocks">Code Blocks</h2>
<p>Now to add some syntax highlighting for adding code blocks to your blog pages. To do that you’re going to add dependencies for <a target="_blank" href="https://github.com/FormidableLabs/prism-react-renderer">prism-react-renderer</a> and <a target="_blank" href="https://github.com/FormidableLabs/react-live">react-live</a>. You’ll also create the files you’re going to need to use:</p>
<pre><code class="lang-bash">yarn add prism-react-renderer react-live
touch root-wrapper.js gatsby-ssr.js gatsby-browser.js
</code></pre>
<p>You’ll come onto <code>react-live</code> soon. For now, you’re going to get <code>prism-react-render</code> up and running for syntax highlighting for any code you’re going to add to the blog. But before that you’re going to go over the root wrapper concept.</p>
<p>So, to change the rendering of a page element, such as a heading or a code block, you’re going to need to use the <code>MDXProvider</code>. The <code>MDXProvider</code> is a component you can use anywhere higher in the React component tree than the MDX content you want to render.</p>
<p>Gatsby browser and a Gatsby SSR both have <code>wrapRootElement</code> available to them and that is as high up the tree as you can get. So you’re going to create the <code>root-wrapper.js</code> file and add elements you want to override there and import it into both <code>gatsby-browser.js</code> and <code>gatsby-ssr.js</code> so you’re not duplicating code.</p>
<p>Before you go any further I want to add that there is a top quality <a target="_blank" href="https://egghead.io/lessons/vue-js-introduction-to-mdx?pl=building-websites-with-mdx-and-gatsby-161e9529">egghead.io playlist</a> resource for using MDX with Gatsby by Chris <a target="_blank" href="https://twitter.com/chrisbiscardi">Chris Biscardi</a>. There’s a ton of useful information in there on MDX in Gatsby.</p>
<p>Ok, first up you’re going to import the <code>root-wrapper.js</code> file into both <code>gatsby-browser.js</code> and <code>gatsby-ssr.js</code>. Into both code modules paste the following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { wrapRootElement <span class="hljs-keyword">as</span> wrap } <span class="hljs-keyword">from</span> <span class="hljs-string">'./root-wrapper'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> wrapRootElement = wrap;
</code></pre>
<p>Ok, now you can work on the code that will be used in both modules. MDX allows you to control the rendering of page elements in your markdown. <code>MDXProvider</code> is used to give React components to override the markdown page elements.</p>
<p>Quick demonstration, in <code>root-wrapper.js</code> add the following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { MDXProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'@mdx-js/react'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> components = {
  <span class="hljs-attr">h2</span>: <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">color:</span> '<span class="hljs-attr">rebeccapurple</span>' }}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span></span>
  ),
  <span class="hljs-string">'p.inlineCode'</span>: <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">code</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">backgroundColor:</span> '<span class="hljs-attr">lightgray</span>' }} {<span class="hljs-attr">...props</span>} /&gt;</span></span>
  ),
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> wrapRootElement = <span class="hljs-function">(<span class="hljs-params">{ element }</span>) =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MDXProvider</span> <span class="hljs-attr">components</span>=<span class="hljs-string">{components}</span>&gt;</span>{element}<span class="hljs-tag">&lt;/<span class="hljs-name">MDXProvider</span>&gt;</span></span>
);
</code></pre>
<p>You’re now overriding any <code>h2</code> in your rendered markdown along with any <code>code</code> blocks (that’s words wrapped in <code>backticks</code>).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/kN8ld7iLQso" 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>Ok, now for the syntax highlighting, create a post with a block of code in it:</p>
<pre><code class="lang-bash">mkdir posts/2019-07-01-code-blocks
touch posts/2019-07-01-code-blocks/index.mdx
</code></pre>
<p>Paste in some content:</p>
<pre><code class="lang-markdown">---
title: Code Blocks
date: 2019-07-01
<span class="hljs-section">published: true
---</span>

<span class="hljs-section">## Yes! Some code!</span>

Here is the <span class="hljs-code">`Dump`</span> component!

<span class="hljs-code">```jsx
import React from 'react';

const Dump = props =&gt; (
  &lt;div
    style={{
      fontSize: 20,
      border: '1px solid #efefef',
      padding: 10,
      background: 'white',
    }}&gt;
    {Object.entries(props).map(([key, val]) =&gt; (
      &lt;pre key={key}&gt;
        &lt;strong style={{ color: 'white', background: 'red' }}&gt;
          {key} ?
        &lt;/strong&gt;
        {JSON.stringify(val, '', ' ')}
      &lt;/pre&gt;
    ))}
  &lt;/div&gt;
);

export default Dump;</span>
</code></pre>
<pre><code>
Go to the [prism-react-renderer](https:<span class="hljs-comment">//github.com/FormidableLabs/prism-react-renderer) GitHub page and copy the example code into `root-wrapper.js` for the `pre` element.</span>

You’re going to copy the provided code <span class="hljs-keyword">for</span> highlighting to validate that it works.

<span class="hljs-string">``</span><span class="hljs-string">`js
import { MDXProvider } from '@mdx-js/react';
import Highlight, { defaultProps } from 'prism-react-renderer';
import React from 'react';

const components = {
  h2: ({ children }) =&gt; (
    &lt;h2 style={{ color: 'rebeccapurple' }}&gt;{children}&lt;/h2&gt;
  ),
  'p.inlineCode': props =&gt; (
    &lt;code style={{ backgroundColor: 'lightgray' }} {...props} /&gt;
  ),
  pre: props =&gt; (
    &lt;Highlight
      {...defaultProps}
      code={`</span>
        (<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">someDemo</span>(<span class="hljs-params"></span>) </span>{
          <span class="hljs-keyword">var</span> test = <span class="hljs-string">"Hello World!"</span>;
          <span class="hljs-built_in">console</span>.log(test);
        })();

        <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>;
      <span class="hljs-string">`}
      language="jsx"&gt;
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) =&gt; (
        &lt;pre className={className} style={style}&gt;
          {tokens.map((line, i) =&gt; (
            &lt;div {...getLineProps({ line, key: i })}&gt;
              {line.map((token, key) =&gt; (
                &lt;span {...getTokenProps({ token, key })} /&gt;
              ))}
            &lt;/div&gt;
          ))}
        &lt;/pre&gt;
      )}
    &lt;/Highlight&gt;
  ),
};

export const wrapRootElement = ({ element }) =&gt; (
  &lt;MDXProvider components={components}&gt;{element}&lt;/MDXProvider&gt;
);</span>
</code></pre><p>Cool, cool! Now you want to replace the pasted in code example with the props of the child component of the pre component. You can do that with <code>props.children.props.children.trim()</code> ?.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { MDXProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'@mdx-js/react'</span>;
<span class="hljs-keyword">import</span> Highlight, { defaultProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> components = {
  <span class="hljs-attr">pre</span>: <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Highlight</span>
      {<span class="hljs-attr">...defaultProps</span>}
      <span class="hljs-attr">code</span>=<span class="hljs-string">{props.children.props.children.trim()}</span>
      <span class="hljs-attr">language</span>=<span class="hljs-string">"jsx"</span>&gt;</span>
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>
          {tokens.map((line, i) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> {<span class="hljs-attr">...getLineProps</span>({ <span class="hljs-attr">line</span>, <span class="hljs-attr">key:</span> <span class="hljs-attr">i</span> })}&gt;</span>
              {line.map((token, key) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> {<span class="hljs-attr">...getTokenProps</span>({ <span class="hljs-attr">token</span>, <span class="hljs-attr">key</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">pre</span>&gt;</span>
      )}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Highlight</span>&gt;</span></span>
  ),
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> wrapRootElement = <span class="hljs-function">(<span class="hljs-params">{ element }</span>) =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MDXProvider</span> <span class="hljs-attr">components</span>=<span class="hljs-string">{components}</span>&gt;</span>{element}<span class="hljs-tag">&lt;/<span class="hljs-name">MDXProvider</span>&gt;</span></span>
);
</code></pre>
<p>Then to match the language, for now you’re going to add in a <code>matches</code> function to match the language class assigned to the code block.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { MDXProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'@mdx-js/react'</span>;
<span class="hljs-keyword">import</span> Highlight, { defaultProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> components = {
  <span class="hljs-attr">h2</span>: <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">color:</span> '<span class="hljs-attr">rebeccapurple</span>' }}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span></span>
  ),
  <span class="hljs-string">'p.inlineCode'</span>: <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">code</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">backgroundColor:</span> '<span class="hljs-attr">lightgray</span>' }} {<span class="hljs-attr">...props</span>} /&gt;</span></span>
  ),
  <span class="hljs-attr">pre</span>: <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> className = props.children.props.className || <span class="hljs-string">''</span>;
    <span class="hljs-keyword">const</span> matches = className.match(<span class="hljs-regexp">/language-(?&lt;lang&gt;.*)/</span>);
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Highlight</span>
        {<span class="hljs-attr">...defaultProps</span>}
        <span class="hljs-attr">code</span>=<span class="hljs-string">{props.children.props.children.trim()}</span>
        <span class="hljs-attr">language</span>=<span class="hljs-string">{</span>
          <span class="hljs-attr">matches</span> &amp;&amp; <span class="hljs-attr">matches.groups</span> &amp;&amp; <span class="hljs-attr">matches.groups.lang</span>
            ? <span class="hljs-attr">matches.groups.lang</span>
            <span class="hljs-attr">:</span> ''
        }&gt;</span>
        {({
          className,
          style,
          tokens,
          getLineProps,
          getTokenProps,
        }) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>
            {tokens.map((line, i) =&gt; (
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> {<span class="hljs-attr">...getLineProps</span>({ <span class="hljs-attr">line</span>, <span class="hljs-attr">key:</span> <span class="hljs-attr">i</span> })}&gt;</span>
                {line.map((token, key) =&gt; (
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> {<span class="hljs-attr">...getTokenProps</span>({ <span class="hljs-attr">token</span>, <span class="hljs-attr">key</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">pre</span>&gt;</span>
        )}
      <span class="hljs-tag">&lt;/<span class="hljs-name">Highlight</span>&gt;</span></span>
    );
  },
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> wrapRootElement = <span class="hljs-function">(<span class="hljs-params">{ element }</span>) =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MDXProvider</span> <span class="hljs-attr">components</span>=<span class="hljs-string">{components}</span>&gt;</span>{element}<span class="hljs-tag">&lt;/<span class="hljs-name">MDXProvider</span>&gt;</span></span>
);
</code></pre>
<p><a target="_blank" href="https://github.com/FormidableLabs/prism-react-renderer">prism-react-renderer</a> comes with additional themes over the default theme which is <a target="_blank" href="https://github.com/FormidableLabs/prism-react-renderer/blob/master/themes/duotoneDark.js">duotoneDark</a>. You’re going to use <a target="_blank" href="https://github.com/FormidableLabs/prism-react-renderer/blob/master/themes/nightOwl.js">nightOwl</a> in this example, but feel free to take a look at <a target="_blank" href="https://github.com/FormidableLabs/prism-react-renderer/blob/master/themes">the other examples</a> if you like.</p>
<p>Import the <code>theme</code> then use it in the props of the <code>Highlight</code> component.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { MDXProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'@mdx-js/react'</span>;
<span class="hljs-keyword">import</span> Highlight, { defaultProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer'</span>;
<span class="hljs-keyword">import</span> theme <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer/themes/nightOwl'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> components = {
  <span class="hljs-attr">pre</span>: <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> className = props.children.props.className || <span class="hljs-string">''</span>;
    <span class="hljs-keyword">const</span> matches = className.match(<span class="hljs-regexp">/language-(?&lt;lang&gt;.*)/</span>);

    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Highlight</span>
        {<span class="hljs-attr">...defaultProps</span>}
        <span class="hljs-attr">code</span>=<span class="hljs-string">{props.children.props.children.trim()}</span>
        <span class="hljs-attr">language</span>=<span class="hljs-string">{</span>
          <span class="hljs-attr">matches</span> &amp;&amp; <span class="hljs-attr">matches.groups</span> &amp;&amp; <span class="hljs-attr">matches.groups.lang</span>
            ? <span class="hljs-attr">matches.groups.lang</span>
            <span class="hljs-attr">:</span> ''
        }
        <span class="hljs-attr">theme</span>=<span class="hljs-string">{theme}</span>&gt;</span>
        {({
          className,
          style,
          tokens,
          getLineProps,
          getTokenProps,
        }) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>
            {tokens.map((line, i) =&gt; (
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> {<span class="hljs-attr">...getLineProps</span>({ <span class="hljs-attr">line</span>, <span class="hljs-attr">key:</span> <span class="hljs-attr">i</span> })}&gt;</span>
                {line.map((token, key) =&gt; (
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> {<span class="hljs-attr">...getTokenProps</span>({ <span class="hljs-attr">token</span>, <span class="hljs-attr">key</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">pre</span>&gt;</span>
        )}
      <span class="hljs-tag">&lt;/<span class="hljs-name">Highlight</span>&gt;</span></span>
    );
  },
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> wrapRootElement = <span class="hljs-function">(<span class="hljs-params">{ element }</span>) =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MDXProvider</span> <span class="hljs-attr">components</span>=<span class="hljs-string">{components}</span>&gt;</span>{element}<span class="hljs-tag">&lt;/<span class="hljs-name">MDXProvider</span>&gt;</span></span>
);
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/k6gI3jVxjKg" 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>Ok, now time to abstract this out into its own component so your <code>root-wrapper.js</code> isn’t so crowded.</p>
<p>Make a <code>Code.js</code> component, and move the code from <code>root-wrapper.js</code> into there:</p>
<pre><code class="lang-bash">touch src/components/Code.js
</code></pre>
<p>Remember this?</p>
<blockquote>
<p>Cool, cool! Now you want to replace the pasted in code example with the props of the child component of the pre component. You can do that with <code>props.children.props.children.trim()</code> ?.</p>
</blockquote>
<p>If that ☝ makes no real amount of sense for you (I’ve had to read it many, many times myself), don’t worry: now you’re going to dig into that a bit more for the creation of the code block component.</p>
<p>So, for now in the <code>components</code> you’re adding into the <code>MDXProvider</code>, take a look at the <code>props</code> coming into the <code>pre</code> element.</p>
<p>Comment out the code you added earlier and add in a <code>console.log</code>:</p>
<pre><code class="lang-js">pre: <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'====================='</span>);
  <span class="hljs-built_in">console</span>.log(props);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'====================='</span>);
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">pre</span> /&gt;</span></span>;
};
</code></pre>
<p>Now if you pop open the developer tools of your browser you can see the output.</p>
<pre><code class="lang-json">{children: {…}}
  children:
    $$typeof: Symbol(react.element)
    key: <span class="hljs-literal">null</span>
    props: {parentName: <span class="hljs-string">"pre"</span>, className: <span class="hljs-string">"language-jsx"</span>, originalType: <span class="hljs-string">"code"</span>, mdxType: <span class="hljs-string">"code"</span>, children: <span class="hljs-string">"import React from 'react'↵↵const Dump = props =&gt; (…  &lt;/pre&gt;↵    ))}↵  &lt;/div&gt;↵)↵↵export default Dump↵"</span>}
    ref: <span class="hljs-literal">null</span>
    type: ƒ (re....
</code></pre>
<p>If you drill into the props of that output you can see the <code>children</code> of those props. If you take a look at the contents of that you will see that it is the code string for your code block. This is what you’re going to be passing into the <code>Code</code> component you’re about to create. Other properties to note here are the <code>className</code> and <code>mdxType</code>.</p>
<p>So, take the code you used earlier for <code>Highlight</code>, everything inside and including the <code>return</code> statement, and paste it into the <code>Code.js</code> module you created earlier.</p>
<p><code>Highlight</code> requires several props:</p>
<pre><code class="lang-jsx">&lt;Highlight
  {...defaultProps}
  code={codeString}
  language={language}
  theme={theme}
&gt;
</code></pre>
<p>The <code>Code</code> module should look something like this now:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Highlight, { defaultProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer'</span>;
<span class="hljs-keyword">import</span> theme <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer/themes/nightOwl'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> Code = <span class="hljs-function">(<span class="hljs-params">{ codeString, language }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Highlight</span>
      {<span class="hljs-attr">...defaultProps</span>}
      <span class="hljs-attr">code</span>=<span class="hljs-string">{codeString}</span>
      <span class="hljs-attr">language</span>=<span class="hljs-string">{language}</span>
      <span class="hljs-attr">theme</span>=<span class="hljs-string">{theme}</span>&gt;</span>
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>
          {tokens.map((line, i) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> {<span class="hljs-attr">...getLineProps</span>({ <span class="hljs-attr">line</span>, <span class="hljs-attr">key:</span> <span class="hljs-attr">i</span> })}&gt;</span>
              {line.map((token, key) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> {<span class="hljs-attr">...getTokenProps</span>({ <span class="hljs-attr">token</span>, <span class="hljs-attr">key</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">pre</span>&gt;</span>
      )}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Highlight</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Code;
</code></pre>
<p>Back to the <code>root-wrapper</code> where you’re going to pass the <code>props</code> needed to the <code>Code</code> component.</p>
<p>The first check you’re going to do is if the <code>mdxType</code> is <code>code</code> then you can get the additional props you need to pass to your <code>Code</code> component.</p>
<p>You’re going to get <code>defaultProps</code> and the <code>theme</code> from <code>prism-react-renderer</code> so all that’s needed is the <code>code</code> and <code>language</code>.</p>
<p>The <code>codeString</code> you can get from the <code>props</code>, and the <code>children</code> by destructuring from the <code>props</code> being passed into the <code>pre</code> element. The <code>language</code> can either be the tag assigned to the meta property of the backticks, like <code>js</code>, <code>jsx</code> or equally empty. So you check for that with some JavaScript and also remove the <code>language-</code> prefix, then pass in the elements <code>{...props}</code>:</p>
<pre><code class="lang-js">pre: <span class="hljs-function">(<span class="hljs-params">{ children: { props } }</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (props.mdxType === <span class="hljs-string">'code'</span>) {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Code</span>
        <span class="hljs-attr">codeString</span>=<span class="hljs-string">{props.children.trim()}</span>
        <span class="hljs-attr">language</span>=<span class="hljs-string">{</span>
          <span class="hljs-attr">props.className</span> &amp;&amp; <span class="hljs-attr">props.className.replace</span>('<span class="hljs-attr">language-</span>', '')
        }
        {<span class="hljs-attr">...props</span>}
      /&gt;</span></span>
    );
  }
};
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/m0tWxa9Ip5E" 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>Ok, now you’re back to where you were before abstracting out the <code>Highlight</code> component to it’s own module. Add some additional styles with <code>styled-components</code> and replace the <code>pre</code> with a styled <code>Pre</code>. You can also add in some line numbers with a styled span and style that as well.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Highlight, { defaultProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer'</span>;
<span class="hljs-keyword">import</span> theme <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer/themes/nightOwl'</span>;
<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> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Pre = styled.pre<span class="hljs-string">`
  text-align: left;
  margin: 1em 0;
  padding: 0.5em;
  overflow-x: auto;
  border-radius: 3px;

  &amp; .token-line {
    line-height: 1.3em;
    height: 1.3em;
  }
  font-family: 'Courier New', Courier, monospace;
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> LineNo = styled.span<span class="hljs-string">`
  display: inline-block;
  width: 2em;
  user-select: none;
  opacity: 0.3;
`</span>;

<span class="hljs-keyword">const</span> Code = <span class="hljs-function">(<span class="hljs-params">{ codeString, language, ...props }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Highlight</span>
      {<span class="hljs-attr">...defaultProps</span>}
      <span class="hljs-attr">code</span>=<span class="hljs-string">{codeString}</span>
      <span class="hljs-attr">language</span>=<span class="hljs-string">{language}</span>
      <span class="hljs-attr">theme</span>=<span class="hljs-string">{theme}</span>&gt;</span>
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">Pre</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>
          {tokens.map((line, i) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> {<span class="hljs-attr">...getLineProps</span>({ <span class="hljs-attr">line</span>, <span class="hljs-attr">key:</span> <span class="hljs-attr">i</span> })}&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">LineNo</span>&gt;</span>{i + 1}<span class="hljs-tag">&lt;/<span class="hljs-name">LineNo</span>&gt;</span>
              {line.map((token, key) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> {<span class="hljs-attr">...getTokenProps</span>({ <span class="hljs-attr">token</span>, <span class="hljs-attr">key</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">Pre</span>&gt;</span>
      )}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Highlight</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Code;
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/PPH153kWpqc" 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>
<h3 id="heading-copy-code-to-clipboard">Copy code to clipboard</h3>
<p>What if you had some way of getting that props code string into the clipboard?</p>
<p>I had a look around and found the majority of the components available for this sort of thing expected an input until <a target="_blank" href="https://github.com/gatsbyjs/gatsby/blob/master/www/src/utils/copy-to-clipboard.js">this</a> in the Gatsby source code. Which is creating the input for you ?</p>
<p>So, create a <code>utils</code> directory and the <code>copy-to-clipboard.js</code> file and add in the code from the Gatsby source code.</p>
<pre><code class="lang-bash">mkdir src/utils
touch src/utils/copy-to-clipboard.js
</code></pre>
<pre><code class="lang-js"><span class="hljs-comment">// https://github.com/gatsbyjs/gatsby/blob/master/www/src/utils/copy-to-clipboard.js</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> copyToClipboard = <span class="hljs-function"><span class="hljs-params">str</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> clipboard = <span class="hljs-built_in">window</span>.navigator.clipboard;
  <span class="hljs-comment">/*
   * fallback to older browsers (including Safari)
   * if clipboard API not supported
   */</span>
  <span class="hljs-keyword">if</span> (!clipboard || <span class="hljs-keyword">typeof</span> clipboard.writeText !== <span class="hljs-string">`function`</span>) {
    <span class="hljs-keyword">const</span> textarea = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">`textarea`</span>);
    textarea.value = str;
    textarea.setAttribute(<span class="hljs-string">`readonly`</span>, <span class="hljs-literal">true</span>);
    textarea.setAttribute(<span class="hljs-string">`contenteditable`</span>, <span class="hljs-literal">true</span>);
    textarea.style.position = <span class="hljs-string">`absolute`</span>;
    textarea.style.left = <span class="hljs-string">`-9999px`</span>;
    <span class="hljs-built_in">document</span>.body.appendChild(textarea);
    textarea.select();
    <span class="hljs-keyword">const</span> range = <span class="hljs-built_in">document</span>.createRange();
    <span class="hljs-keyword">const</span> sel = <span class="hljs-built_in">window</span>.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
    textarea.setSelectionRange(<span class="hljs-number">0</span>, textarea.value.length);
    <span class="hljs-built_in">document</span>.execCommand(<span class="hljs-string">`copy`</span>);
    <span class="hljs-built_in">document</span>.body.removeChild(textarea);

    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.resolve(<span class="hljs-literal">true</span>);
  }

  <span class="hljs-keyword">return</span> clipboard.writeText(str);
};
</code></pre>
<p>Now you’re going to want a way to trigger copying the code to the clipboard.</p>
<p>Let's create a styled button. But first add a <code>position: relative;</code> to the <code>Pre</code> component which will let us position the styled button:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> CopyCode = styled.button<span class="hljs-string">`
  position: absolute;
  right: 0.25rem;
  border: 0;
  border-radius: 3px;
  margin: 0.25em;
  opacity: 0.3;
  &amp;:hover {
    opacity: 1;
  }
`</span>;
</code></pre>
<p>And now you need to use the <code>copyToClipboard</code> function in the <code>onClick</code> of the button:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Highlight, { defaultProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer'</span>;
<span class="hljs-keyword">import</span> theme <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer/themes/nightOwl'</span>;
<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> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components'</span>;
<span class="hljs-keyword">import</span> { copyToClipboard } <span class="hljs-keyword">from</span> <span class="hljs-string">'../utils/copy-to-clipboard'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Pre = styled.pre<span class="hljs-string">`
  text-align: left;
  margin: 1rem 0;
  padding: 0.5rem;
  overflow-x: auto;
  border-radius: 3px;

  &amp; .token-line {
    line-height: 1.3rem;
    height: 1.3rem;
  }
  font-family: 'Courier New', Courier, monospace;
  position: relative;
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> LineNo = styled.span<span class="hljs-string">`
  display: inline-block;
  width: 2rem;
  user-select: none;
  opacity: 0.3;
`</span>;

<span class="hljs-keyword">const</span> CopyCode = styled.button<span class="hljs-string">`
  position: absolute;
  right: 0.25rem;
  border: 0;
  border-radius: 3px;
  margin: 0.25em;
  opacity: 0.3;
  &amp;:hover {
    opacity: 1;
  }
`</span>;

<span class="hljs-keyword">const</span> Code = <span class="hljs-function">(<span class="hljs-params">{ codeString, language }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> handleClick = <span class="hljs-function">() =&gt;</span> {
    copyToClipboard(codeString);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Highlight</span>
      {<span class="hljs-attr">...defaultProps</span>}
      <span class="hljs-attr">code</span>=<span class="hljs-string">{codeString}</span>
      <span class="hljs-attr">language</span>=<span class="hljs-string">{language}</span>
      <span class="hljs-attr">theme</span>=<span class="hljs-string">{theme}</span>&gt;</span>
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">Pre</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">CopyCode</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>&gt;</span>Copy<span class="hljs-tag">&lt;/<span class="hljs-name">CopyCode</span>&gt;</span>
          {tokens.map((line, i) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> {<span class="hljs-attr">...getLineProps</span>({ <span class="hljs-attr">line</span>, <span class="hljs-attr">key:</span> <span class="hljs-attr">i</span> })}&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">LineNo</span>&gt;</span>{i + 1}<span class="hljs-tag">&lt;/<span class="hljs-name">LineNo</span>&gt;</span>
              {line.map((token, key) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> {<span class="hljs-attr">...getTokenProps</span>({ <span class="hljs-attr">token</span>, <span class="hljs-attr">key</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">Pre</span>&gt;</span>
      )}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Highlight</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Code;
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/j-EINXVe2WA" 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-react-live">React live</h2>
<p>So with React Live you need to add two snippets to your <code>Code.js</code> component.</p>
<p>You’re going to import the components:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> {
  LiveEditor,
  LiveError,
  LivePreview,
  LiveProvider,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-live'</span>;
</code></pre>
<p>Then you’re going to check if <code>react-live</code> has been added to the language tag on your mdx file via the props:</p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span> (props[<span class="hljs-string">'react-live'</span>]) {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">LiveProvider</span> <span class="hljs-attr">code</span>=<span class="hljs-string">{codeString}</span> <span class="hljs-attr">noInline</span>=<span class="hljs-string">{true}</span> <span class="hljs-attr">theme</span>=<span class="hljs-string">{theme}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">LiveEditor</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">LiveError</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">LivePreview</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">LiveProvider</span>&gt;</span></span>
  );
}
</code></pre>
<p>Here’s the full component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Highlight, { defaultProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer'</span>;
<span class="hljs-keyword">import</span> theme <span class="hljs-keyword">from</span> <span class="hljs-string">'prism-react-renderer/themes/nightOwl'</span>;
<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> {
  LiveEditor,
  LiveError,
  LivePreview,
  LiveProvider,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-live'</span>;
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components'</span>;
<span class="hljs-keyword">import</span> { copyToClipboard } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../utils/copy-to-clipboard'</span>;

<span class="hljs-keyword">const</span> Pre = styled.pre<span class="hljs-string">`
  position: relative;
  text-align: left;
  margin: 1em 0;
  padding: 0.5em;
  overflow-x: auto;
  border-radius: 3px;

  &amp; .token-lline {
    line-height: 1.3em;
    height: 1.3em;
  }
  font-family: 'Courier New', Courier, monospace;
`</span>;

<span class="hljs-keyword">const</span> LineNo = styled.span<span class="hljs-string">`
  display: inline-block;
  width: 2em;
  user-select: none;
  opacity: 0.3;
`</span>;

<span class="hljs-keyword">const</span> CopyCode = styled.button<span class="hljs-string">`
  position: absolute;
  right: 0.25rem;
  border: 0;
  border-radius: 3px;
  margin: 0.25em;
  opacity: 0.3;
  &amp;:hover {
    opacity: 1;
  }
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Code = <span class="hljs-function">(<span class="hljs-params">{ codeString, language, ...props }</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (props[<span class="hljs-string">'react-live'</span>]) {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">LiveProvider</span> <span class="hljs-attr">code</span>=<span class="hljs-string">{codeString}</span> <span class="hljs-attr">noInline</span>=<span class="hljs-string">{true}</span> <span class="hljs-attr">theme</span>=<span class="hljs-string">{theme}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">LiveEditor</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">LiveError</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">LivePreview</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">LiveProvider</span>&gt;</span></span>
    );
  }

  <span class="hljs-keyword">const</span> handleClick = <span class="hljs-function">() =&gt;</span> {
    copyToClipboard(codeString);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Highlight</span>
      {<span class="hljs-attr">...defaultProps</span>}
      <span class="hljs-attr">code</span>=<span class="hljs-string">{codeString}</span>
      <span class="hljs-attr">language</span>=<span class="hljs-string">{language}</span>
      <span class="hljs-attr">theme</span>=<span class="hljs-string">{theme}</span>&gt;</span>
      {({
        className,
        style,
        tokens,
        getLineProps,
        getTokenProps,
      }) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">Pre</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{className}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">CopyCode</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>&gt;</span>Copy<span class="hljs-tag">&lt;/<span class="hljs-name">CopyCode</span>&gt;</span>
          {tokens.map((line, i) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> {<span class="hljs-attr">...getLineProps</span>({ <span class="hljs-attr">line</span>, <span class="hljs-attr">key:</span> <span class="hljs-attr">i</span> })}&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">LineNo</span>&gt;</span>{i + 1}<span class="hljs-tag">&lt;/<span class="hljs-name">LineNo</span>&gt;</span>
              {line.map((token, key) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> {<span class="hljs-attr">...getTokenProps</span>({ <span class="hljs-attr">token</span>, <span class="hljs-attr">key</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">Pre</span>&gt;</span>
      )}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Highlight</span>&gt;</span></span>
  );
};
</code></pre>
<p>To test this, add <code>react-live</code> next to the language on your <code>Dump</code> component, so you have added to the blog post you made:</p>
<pre><code class="lang-markdown"><span class="hljs-code">```jsx react-live</span>
</code></pre>
<p>Now you can edit the code directly. Try changing a few things like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Dump = <span class="hljs-function"><span class="hljs-params">props</span> =&gt;</span> (
  <span class="xml"><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">fontSize:</span> <span class="hljs-attr">20</span>,
      <span class="hljs-attr">border:</span> '<span class="hljs-attr">1px</span> <span class="hljs-attr">solid</span> #<span class="hljs-attr">efefef</span>',
      <span class="hljs-attr">padding:</span> <span class="hljs-attr">10</span>,
      <span class="hljs-attr">background:</span> '<span class="hljs-attr">white</span>',
    }}&gt;</span>
    {Object.entries(props).map(([key, val]) =&gt; (
      <span class="hljs-tag">&lt;<span class="hljs-name">pre</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{key}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">color:</span> '<span class="hljs-attr">white</span>', <span class="hljs-attr">background:</span> '<span class="hljs-attr">red</span>' }}&gt;</span>
          {key} ?
        <span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>
        {JSON.stringify(val, '', ' ')}
      <span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span>
    ))}
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
);

render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Dump</span> <span class="hljs-attr">props</span>=<span class="hljs-string">{[</span>'<span class="hljs-attr">One</span>', '<span class="hljs-attr">Two</span>', '<span class="hljs-attr">Three</span>', '<span class="hljs-attr">Four</span>']} /&gt;</span></span>);
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/AlOdd-TvqHE" 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-cover-image">Cover Image</h2>
<p>Now to add a cover image to go with each post, you’ll need to install a couple of packages to manage images in Gatsby.</p>
<p>install:</p>
<pre><code class="lang-bash">yarn add gatsby-transformer-sharp gatsby-plugin-sharp gatsby-remark-images gatsby-image
</code></pre>
<p>Now you should config <code>gatsby-config.js</code> to include the newly added packages. Remember to add <code>gatsby-remark-images</code> to <code>gatsby-plugin-mdx</code> as both a <code>gatsbyRemarkPlugins</code> option and as a <code>plugins</code> option.</p>
<p>config:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">siteMetadata</span>: siteMetadata,
  <span class="hljs-attr">plugins</span>: [
    <span class="hljs-string">`gatsby-plugin-styled-components`</span>,
    <span class="hljs-string">`gatsby-transformer-sharp`</span>,
    <span class="hljs-string">`gatsby-plugin-sharp`</span>,
    {
      <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-plugin-mdx`</span>,
      <span class="hljs-attr">options</span>: {
        <span class="hljs-attr">extensions</span>: [<span class="hljs-string">`.mdx`</span>, <span class="hljs-string">`.md`</span>],
        <span class="hljs-attr">gatsbyRemarkPlugins</span>: [
          {
            <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-remark-images`</span>,
            <span class="hljs-attr">options</span>: {
              <span class="hljs-attr">maxWidth</span>: <span class="hljs-number">590</span>,
            },
          },
        ],
        <span class="hljs-attr">plugins</span>: [
          {
            <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-remark-images`</span>,
            <span class="hljs-attr">options</span>: {
              <span class="hljs-attr">maxWidth</span>: <span class="hljs-number">590</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>/posts`</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">`posts`</span> },
    },
  ],
};
</code></pre>
<p>Add image to index query in <code>src/pages.index.js</code>:</p>
<pre><code class="lang-graphql">cover {
  publicURL
  childImageSharp {
    sizes(
      <span class="hljs-symbol">maxWidth:</span> <span class="hljs-number">2000</span>
      <span class="hljs-symbol">traceSVG:</span> { <span class="hljs-symbol">color:</span> <span class="hljs-string">"#639"</span> }
    ) {
      <span class="hljs-punctuation">...GatsbyImageSharpSizes_tracedSVG
</span>    }
  }
}
</code></pre>
<p>Fix up the date in the query too:</p>
<pre><code class="lang-graphql">date(<span class="hljs-symbol">formatString:</span> <span class="hljs-string">"YYYY MMMM Do"</span>)
</code></pre>
<p>This will show the date as full year, full month and the day as a ‘st’, ‘nd’, ‘rd’ and ‘th’. So if today’s date were 1970/01/01 it would read 1970 January 1st.</p>
<p>Add <code>gatsby-image</code> use that in a styled component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Image = styled(Img)<span class="hljs-string">`
  border-radius: 5px;
`</span>;
</code></pre>
<p>Add some JavaScript to determine if there’s anything to render:</p>
<pre><code>{
  !!frontmatter.cover ? (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Image</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">{frontmatter.cover.childImageSharp.sizes}</span> /&gt;</span></span>
  ) : <span class="hljs-literal">null</span>;
}
</code></pre><p>Here’s what the full module should look like now:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby'</span>;
<span class="hljs-keyword">import</span> Img <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby-image'</span>;
<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> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components'</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">const</span> IndexWrapper = styled.main<span class="hljs-string">``</span>;

<span class="hljs-keyword">const</span> PostWrapper = styled.div<span class="hljs-string">``</span>;

<span class="hljs-keyword">const</span> Image = styled(Img)<span class="hljs-string">`
  border-radius: 5px;
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ({ data }) =&gt; {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">IndexWrapper</span>&gt;</span>
        {/* <span class="hljs-tag">&lt;<span class="hljs-name">Dump</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{data}</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">Dump</span>&gt;</span> */}
        {data.allMdx.nodes.map(
          ({ id, excerpt, frontmatter, fields }) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">PostWrapper</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{fields.slug}</span>&gt;</span>
                {!!frontmatter.cover ? (
                  <span class="hljs-tag">&lt;<span class="hljs-name">Image</span>
                    <span class="hljs-attr">sizes</span>=<span class="hljs-string">{frontmatter.cover.childImageSharp.sizes}</span>
                  /&gt;</span>
                ) : null}
                <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{frontmatter.date}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{excerpt}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">PostWrapper</span>&gt;</span>
          )
        )}
      <span class="hljs-tag">&lt;/<span class="hljs-name">IndexWrapper</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date(formatString: "YYYY MMMM Do")
          cover {
            publicURL
            childImageSharp {
              sizes(maxWidth: 2000, traceSVG: { color: "#639" }) {
                ...GatsbyImageSharpSizes_tracedSVG
              }
            }
          }
        }
        fields {
          slug
        }
      }
    }
  }
`</span>;
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/9S5GNtql02w" 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><strong>Additional resources:</strong></p>
<ul>
<li>this helped me for my own blog: <a target="_blank" href="https://juliangaramendy.dev/custom-open-graph-images-in-gatsby-blog/">https://juliangaramendy.dev/custom-open-graph-images-in-gatsby-blog/</a></li>
<li>and the Gatsby docs: <a target="_blank" href="https://www.gatsbyjs.org/docs/working-with-images/">https://www.gatsbyjs.org/docs/working-with-images/</a></li>
</ul>
<h2 id="heading-creating-an-seo-component-with-react-helmet">Creating an SEO component with React Helmet</h2>
<p>There’s a Gatsby <a target="_blank" href="https://github.com/gatsbyjs/gatsby/issues/14125">github PR on seo</a> with some <a target="_blank" href="https://github.com/gatsbyjs/gatsby/pull/10780#issuecomment-451048608">great notes from Andrew Welch</a> on SEO and a link to a presentation he did back in 2017.</p>
<p><strong>Crafting Modern SEO with Andrew Welch:</strong></p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/I9qQslUJknw" 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><strong>Crafting Modern SEO with Andrew Welch:</strong></p>
<div class="embed-wrapper">
        <iframe width="640" height="360" src="https://player.vimeo.com/video/246846978" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="Vimeo embed" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen="" loading="lazy"></iframe></div>
<p>In the following comments of that PR, Gatsby’s <a target="_blank" href="https://github.com/LekoArts">LekoArts</a> details <a target="_blank" href="https://github.com/LekoArts/gatsby-starter-prismic/blob/master/src/components/SEO/SEO.jsx">his own implementation</a> which I have implemented <a target="_blank" href="https://github.com/spences10/react-seo-component">as a React component</a>. You’re going to be configuring that now in this how-to.</p>
<p>First up, install and configure <code>gatsby-plugin-react-helmet</code>. This is used for server rendering data added with React Helmet.</p>
<pre><code class="lang-bash">yarn add gatsby-plugin-react-helmet
</code></pre>
<p>You’ll need to add the plugin to your <code>gatsby-config.js</code>. If you haven’t done so already now is a good time to also configure the <code>gatsby-plugin-styled-components</code> as well.</p>
<h3 id="heading-configure-seo-component-for-homepage">Configure SEO Component for Homepage</h3>
<p>To visualise the data you’re going to need to get into the SEO component use the <code>Dump</code> component to begin with to validate the data.</p>
<p>The majority of the information needed for <code>src/pages/index.js</code> can be first added to the <code>gatsby-config.js</code>, <code>siteMetadata</code> object then queried with the <code>useSiteMetadata</code> hook. Some of the data added here can then be used in <code>src/templates/blogPostTemplate.js</code> – more on that in the next section.</p>
<p>For now add the following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> siteMetadata = {
  <span class="hljs-attr">title</span>: <span class="hljs-string">`The Localhost Blog`</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">`This is my coding blog where I write about my coding journey.`</span>,
  <span class="hljs-attr">image</span>: <span class="hljs-string">`/default-site-image.jpg`</span>,
  <span class="hljs-attr">siteUrl</span>: <span class="hljs-string">`https://thelocalhost.blog`</span>,
  <span class="hljs-attr">siteLanguage</span>: <span class="hljs-string">`en-GB`</span>,
  <span class="hljs-attr">siteLocale</span>: <span class="hljs-string">`en_gb`</span>,
  <span class="hljs-attr">twitterUsername</span>: <span class="hljs-string">`@spences10`</span>,
  <span class="hljs-attr">authorName</span>: <span class="hljs-string">`Scott Spence`</span>,
}

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">siteMetadata</span>: siteMetadata,
  <span class="hljs-attr">plugins</span>: [
    ...
</code></pre>
<p>You don’t have to abstract out the <code>siteMetadata</code> into its own component here. It’s only a suggestion on how to manage it.</p>
<p>The <code>image</code> is going to be the default image for your site. You should create a <code>static</code> folder at the root of the project and add in an image you want to be shown when the homepage of your site is shared on social media.</p>
<p>For <code>siteUrl</code> at this stage it doesn’t necessarily have to be valid. You can add a dummy url for now and change it later.</p>
<p>The <code>siteLanguage</code> is your language of choice for the site. Take a look at <a target="_blank" href="https://www.w3.org/International/articles/language-tags/">w3 language tags</a> for more info.</p>
<p>Facebook OpenGraph is the only place the <code>siteLocale</code> is used and it is different from language tags.</p>
<p>Add your <code>twitterUsername</code> and your <code>authorName</code>.</p>
<p>Update the <code>useSiteMetadata</code> hook now to reflect the newly added properties:</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">const</span> useSiteMetadata = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { site } = useStaticQuery(
    graphql<span class="hljs-string">`
      query SITE_METADATA_QUERY {
        site {
          siteMetadata {
            description
            title
            image
            siteUrl
            siteLanguage
            siteLocale
            twitterUsername
            authorName
          }
        }
      }
    `</span>
  );
  <span class="hljs-keyword">return</span> site.siteMetadata;
};
</code></pre>
<p>Begin with importing the <code>Dump</code> component in <code>src/pages/index.js</code> then plug in the props as they are detailed in the docs of the <a target="_blank" href="https://github.com/spences10/react-seo-component"><code>react-seo-component</code></a>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Dump <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Dump'</span>
<span class="hljs-keyword">import</span> { useSiteMetadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'../hooks/useSiteMetadata'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ({ data }) =&gt; {
  <span class="hljs-keyword">const</span> {
    description,
    title,
    image,
    siteUrl,
    siteLanguage,
    siteLocale,
    twitterUsername,
  } = useSiteMetadata()
  <span class="hljs-keyword">return</span> (
    &lt;Layout&gt;
      &lt;Dump
        title={title}
        description={description}
        image={`${siteUrl}${image}`}
        pathname={siteUrl}
        siteLanguage={siteLanguage}
        siteLocale={siteLocale}
        twitterUsername={twitterUsername}
      /&gt;
      &lt;IndexWrapper&gt;
        {data.allMdx.nodes.map(
          ...
</code></pre>
<p>Check that all the props are displaying valid values. Then you can swap out the <code>Dump</code> component with the <code>SEO</code> component.</p>
<p>The complete <code>src/pages/index.js</code> should look like this now:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { graphql, Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby'</span>;
<span class="hljs-keyword">import</span> Img <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby-image'</span>;
<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> SEO <span class="hljs-keyword">from</span> <span class="hljs-string">'react-seo-component'</span>;
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components'</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> { useSiteMetadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'../hooks/useSiteMetadata'</span>;

<span class="hljs-keyword">const</span> IndexWrapper = styled.main<span class="hljs-string">``</span>;

<span class="hljs-keyword">const</span> PostWrapper = styled.div<span class="hljs-string">``</span>;

<span class="hljs-keyword">const</span> Image = styled(Img)<span class="hljs-string">`
  border-radius: 5px;
`</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ({ data }) =&gt; {
  <span class="hljs-keyword">const</span> {
    description,
    title,
    image,
    siteUrl,
    siteLanguage,
    siteLocale,
    twitterUsername,
  } = useSiteMetadata();
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SEO</span>
        <span class="hljs-attr">title</span>=<span class="hljs-string">{title}</span>
        <span class="hljs-attr">description</span>=<span class="hljs-string">{description</span> || `<span class="hljs-attr">nothin</span>’`}
        <span class="hljs-attr">image</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">siteUrl</span>}${<span class="hljs-attr">image</span>}`}
        <span class="hljs-attr">pathname</span>=<span class="hljs-string">{siteUrl}</span>
        <span class="hljs-attr">siteLanguage</span>=<span class="hljs-string">{siteLanguage}</span>
        <span class="hljs-attr">siteLocale</span>=<span class="hljs-string">{siteLocale}</span>
        <span class="hljs-attr">twitterUsername</span>=<span class="hljs-string">{twitterUsername}</span>
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">IndexWrapper</span>&gt;</span>
        {/* <span class="hljs-tag">&lt;<span class="hljs-name">Dump</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{data}</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">Dump</span>&gt;</span> */}
        {data.allMdx.nodes.map(
          ({ id, excerpt, frontmatter, fields }) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">PostWrapper</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{fields.slug}</span>&gt;</span>
                {!!frontmatter.cover ? (
                  <span class="hljs-tag">&lt;<span class="hljs-name">Image</span>
                    <span class="hljs-attr">sizes</span>=<span class="hljs-string">{frontmatter.cover.childImageSharp.sizes}</span>
                  /&gt;</span>
                ) : null}
                <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{frontmatter.date}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{excerpt}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">PostWrapper</span>&gt;</span>
          )
        )}
      <span class="hljs-tag">&lt;/<span class="hljs-name">IndexWrapper</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query SITE_INDEX_QUERY {
    allMdx(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { published: { eq: true } } }
    ) {
      nodes {
        id
        excerpt(pruneLength: 250)
        frontmatter {
          title
          date(formatString: "YYYY MMMM Do")
          cover {
            publicURL
            childImageSharp {
              sizes(maxWidth: 2000, traceSVG: { color: "#639" }) {
                ...GatsbyImageSharpSizes_tracedSVG
              }
            }
          }
        }
        fields {
          slug
        }
      }
    }
  }
`</span>;
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/O0jk9AqM_ls" 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>
<h3 id="heading-configure-seo-component-for-blog-posts">Configure SEO Component for Blog Posts</h3>
<p>This will be the same approach as with the homepage. Import the <code>Dump</code> component and validate the props before swapping out the <code>Dump</code> component with the <code>SEO</code> component.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Dump <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Dump'</span>
<span class="hljs-keyword">import</span> { useSiteMetadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'../hooks/useSiteMetadata'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ({ data, pageContext }) =&gt; {
  <span class="hljs-keyword">const</span> {
    image,
    siteUrl,
    siteLanguage,
    siteLocale,
    twitterUsername,
    authorName,
  } = useSiteMetadata()
  <span class="hljs-keyword">const</span> { frontmatter, body, fields, excerpt } = data.mdx
  <span class="hljs-keyword">const</span> { title, date, cover } = frontmatter
  <span class="hljs-keyword">const</span> { previous, next } = pageContext
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Dump</span>
        <span class="hljs-attr">title</span>=<span class="hljs-string">{title}</span>
        <span class="hljs-attr">description</span>=<span class="hljs-string">{excerpt}</span>
        <span class="hljs-attr">image</span>=<span class="hljs-string">{</span>
          <span class="hljs-attr">cover</span> === <span class="hljs-string">null</span>
            ? `${<span class="hljs-attr">siteUrl</span>}${<span class="hljs-attr">image</span>}`
            <span class="hljs-attr">:</span> `${<span class="hljs-attr">siteUrl</span>}${<span class="hljs-attr">cover.publicURL</span>}`
        }
        <span class="hljs-attr">pathname</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">siteUrl</span>}${<span class="hljs-attr">fields.slug</span>}`}
        <span class="hljs-attr">siteLanguage</span>=<span class="hljs-string">{siteLanguage}</span>
        <span class="hljs-attr">siteLocale</span>=<span class="hljs-string">{siteLocale}</span>
        <span class="hljs-attr">twitterUsername</span>=<span class="hljs-string">{twitterUsername}</span>
        <span class="hljs-attr">author</span>=<span class="hljs-string">{authorName}</span>
        <span class="hljs-attr">article</span>=<span class="hljs-string">{true}</span>
        <span class="hljs-attr">publishedDate</span>=<span class="hljs-string">{date}</span>
        <span class="hljs-attr">modifiedDate</span>=<span class="hljs-string">{new</span> <span class="hljs-attr">Date</span>(<span class="hljs-attr">Date.now</span>())<span class="hljs-attr">.toISOString</span>()}
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      ...</span>
</code></pre>
<p>Add <code>fields.slug</code>, <code>excerpt</code> and <code>cover.publicURL</code> to the <code>PostsBySlug</code> query and destructure them from <code>data.mdx</code> and <code>frontmatter</code>, respectively.</p>
<p>For the image you’ll need to do some logic as to whether the <code>cover</code> exists and default to the default site image if it doesn’t.</p>
<p>The complete <code>src/templates/blogPostTemplate.js</code> should look like this now:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { graphql, Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby'</span>;
<span class="hljs-keyword">import</span> { MDXRenderer } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby-plugin-mdx'</span>;
<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> SEO <span class="hljs-keyword">from</span> <span class="hljs-string">'react-seo-component'</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> { useSiteMetadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'../hooks/useSiteMetadata'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ({ data, pageContext }) =&gt; {
  <span class="hljs-keyword">const</span> {
    image,
    siteUrl,
    siteLanguage,
    siteLocale,
    twitterUsername,
    authorName,
  } = useSiteMetadata();
  <span class="hljs-keyword">const</span> { frontmatter, body, fields, excerpt } = data.mdx;
  <span class="hljs-keyword">const</span> { title, date, cover } = frontmatter;
  <span class="hljs-keyword">const</span> { previous, next } = pageContext;
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SEO</span>
        <span class="hljs-attr">title</span>=<span class="hljs-string">{title}</span>
        <span class="hljs-attr">description</span>=<span class="hljs-string">{excerpt}</span>
        <span class="hljs-attr">image</span>=<span class="hljs-string">{</span>
          <span class="hljs-attr">cover</span> === <span class="hljs-string">null</span>
            ? `${<span class="hljs-attr">siteUrl</span>}${<span class="hljs-attr">image</span>}`
            <span class="hljs-attr">:</span> `${<span class="hljs-attr">siteUrl</span>}${<span class="hljs-attr">cover.publicURL</span>}`
        }
        <span class="hljs-attr">pathname</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">siteUrl</span>}${<span class="hljs-attr">fields.slug</span>}`}
        <span class="hljs-attr">siteLanguage</span>=<span class="hljs-string">{siteLanguage}</span>
        <span class="hljs-attr">siteLocale</span>=<span class="hljs-string">{siteLocale}</span>
        <span class="hljs-attr">twitterUsername</span>=<span class="hljs-string">{twitterUsername}</span>
        <span class="hljs-attr">author</span>=<span class="hljs-string">{authorName}</span>
        <span class="hljs-attr">article</span>=<span class="hljs-string">{true}</span>
        <span class="hljs-attr">publishedDate</span>=<span class="hljs-string">{date}</span>
        <span class="hljs-attr">modifiedDate</span>=<span class="hljs-string">{new</span> <span class="hljs-attr">Date</span>(<span class="hljs-attr">Date.now</span>())<span class="hljs-attr">.toISOString</span>()}
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{frontmatter.date}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">MDXRenderer</span>&gt;</span>{body}<span class="hljs-tag">&lt;/<span class="hljs-name">MDXRenderer</span>&gt;</span>
      {previous === false ? null : (
        <span class="hljs-tag">&lt;&gt;</span>
          {previous &amp;&amp; (
            <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{previous.fields.slug}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{previous.frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
          )}
        <span class="hljs-tag">&lt;/&gt;</span></span>
      )}
      {next === <span class="hljs-literal">false</span> ? <span class="hljs-literal">null</span> : (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
          {next &amp;&amp; (
            <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{next.fields.slug}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{next.frontmatter.title}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
          )}
        <span class="hljs-tag">&lt;/&gt;</span></span>
      )}
    &lt;/Layout&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> query = graphql<span class="hljs-string">`
  query PostBySlug($slug: String!) {
    mdx(fields: { slug: { eq: $slug } }) {
      frontmatter {
        title
        date(formatString: "YYYY MMMM Do")
        cover {
          publicURL
        }
      }
      body
      excerpt
      fields {
        slug
      }
    }
  }
`</span>;
</code></pre>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/a2fcgYIQRIU" 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>
<h3 id="heading-build-site-and-validate-meta-tags">Build Site and Validate Meta Tags</h3>
<p>Add in the build script to <code>package.json</code> and also a script for serving the built site locally.</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
  <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"gatsby develop -p 9988 -o"</span>,
  <span class="hljs-attr">"build"</span>: <span class="hljs-string">"gatsby build"</span>,
  <span class="hljs-attr">"serve"</span>: <span class="hljs-string">"gatsby serve -p 9500 -o"</span>
},
</code></pre>
<p>Now it’s time to run:</p>
<pre><code class="lang-bash">yarn build &amp;&amp; yarn serve
</code></pre>
<p>This will build the site and open a browser tab so you can see the site as it will appear when it is on the internet. Validate meta tags have been added to the build by selecting “View page source” (Crtl+u in Windows and Linux) on the page. You can do a Ctrl+f to find them.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/c6bB9ddAgjc" 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-adding-the-project-to-github">Adding the Project to GitHub</h2>
<p>Add your code to GitHub by either selecting the plus (+) icon next to your avatar on GitHub or by going directly to <a target="_blank" href="https://github.com/new">https://github.com/new</a></p>
<p>Name your repository and click create repository. Then you will be given the instructions to link your local code to the repository you created via the command line.</p>
<p>How you authenticate with GitHub will depend on what the command looks like.</p>
<p>Some good resources for authenticating with GitHub via SSH are <a target="_blank" href="https://egghead.io/lessons/javascript-how-to-authenticate-with-github-using-ssh">Kent Dodds Egghead.io video</a> and also a how-to <a target="_blank" href="https://www.cheatsheets.xyz/git/#how-to-authenticate-with-github-using-ssh">on CheatSheets.xyz</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/r2eiJ8E_YT0" 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-deploy-to-netlify">Deploy to Netlify</h2>
<p>To deploy your site to Netlify, if you haven’t done so already, you’ll need to add the GitHub integration to your GitHub profile. If you got to <a target="_blank" href="https://egghead.io/lessons/javascript-how-to-authenticate-with-github-using-ssh">app.netlify.com</a> the wizard will walk you through the process.</p>
<p>From here you can add your built site’s <code>public</code> folder drag ‘n drop style directly to the Netlify global CDNs.</p>
<p>You, however, are going to load your site via the Netlify CLI! In your terminal, if you haven’t already got the CLI installed, run:</p>
<pre><code class="lang-bash">yarn global add netlify-cli
</code></pre>
<p>Then once the CLI is installed:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># authenticate via the CLI</span>
netlify login
<span class="hljs-comment"># initialise the site</span>
netlify init
</code></pre>
<p>Enter the details for your team: the site name is optional, the build command will be <code>yarn build</code>, and directory to deploy is <code>public</code>.</p>
<p>You will be prompted to commit the changes and push them to GitHub (with <code>git push</code>). Once you have done that your site will be published and ready for all to see!</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/voWeHvIGB0g" 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-validate-metadata-with-heymeta">Validate Metadata with Heymeta</h2>
<p>Last up is validating the metadata for the OpenGraph fields. To do that you’ll need to make sure that the <code>siteUrl</code> reflects what you have in your Netlify dashboard.</p>
<p>If you needed to change the url you’ll need to commit and push the changes to GitHub again.</p>
<p>Once your site is built with a valid url you can then test the homepage and a blog page for the correct meta tags with <a target="_blank" href="http://heymeta.com/">heymeta.com</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/JH1AVanYhwo" 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><strong>OpenGraph checking tools:</strong></p>
<ul>
<li><a target="_blank" href="https://www.heymeta.com/">https://www.heymeta.com/</a></li>
<li><a target="_blank" href="https://opengraphcheck.com/">https://opengraphcheck.com/</a></li>
<li><a target="_blank" href="https://cards-dev.twitter.com/validator">https://cards-dev.twitter.com/validator</a></li>
<li><a target="_blank" href="https://developers.facebook.com/tools/debug/sharing">https://developers.facebook.com/tools/debug/sharing</a></li>
<li><a target="_blank" href="https://www.linkedin.com/post-inspector/">https://www.linkedin.com/post-inspector/</a></li>
</ul>
<p><strong>Additional resources:</strong></p>
<ul>
<li><a target="_blank" href="https://css-tricks.com/essential-meta-tags-social-media/">The Essential Meta Tags for Social Media</a></li>
</ul>
<p><strong>Example Code for this Blog can be found <a target="_blank" href="https://codesandbox.io/s/the-localhost-blog-3bn45">here</a>:</strong></p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codesandbox.io/embed/the-localhost-blog-3bn45" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodeSandbox embed" allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin" loading="lazy"></iframe></div>
<p><strong>Or <a target="_blank" href="https://github.com/spences10/the-localhost-blog/tree/blog-post-code">here</a>.</strong></p>
<h2 id="heading-thanks-for-reading">Thanks for reading ?</h2>
<p>That’s all folks! If there is anything I have missed, or if there is a better way to do something, then please let me know.</p>
<p>Follow me on <a target="_blank" href="https://twitter.com/spences10">Twitter</a> or <a target="_blank" href="https://github.com/spences10/ama">Ask Me Anything</a> on GitHub.</p>
<blockquote>
<p><strong>You can read other articles like this on <a target="_blank" href="https://scottspence.com/garden">my digital garden</a>.</strong></p>
</blockquote>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build a mapping app in React the easy way with Leaflet ]]>
                </title>
                <description>
                    <![CDATA[ Mapping is hard, but spinning up a new app that renders maps doesn’t have to be. Here’s how you can easily get started working with maps in a new React app. Not that AAA map under your car seat Maps have been around for thousands of years, but they’v... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/</link>
                <guid isPermaLink="false">66b8e3249232d58aac300b11</guid>
                
                    <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, 30 Oct 2019 14:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/10/mapping-with-leaflet.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Mapping is hard, but spinning up a new app that renders maps doesn’t have to be. Here’s how you can easily get started working with maps in a new React app.</p>
<h1 id="heading-not-that-aaa-map-under-your-car-seat">Not that AAA map under your car seat</h1>
<p>Maps have been around for thousands of years, but they’ve become more complex and powerful within the last couple decades simply due to the fact that computers exist. This has enabled the creation of products we use every day - like Google Maps that help us get home from work and avoid traffic, or weather maps that allow us to check real time radar images. Taking that a step further, scientists use maps every day using data from satellite imagery to try to get a better understanding of our humble planet.</p>
<p>This sounds hard…</p>
<h1 id="heading-building-maps">Building maps</h1>
<p>Plot twist, it’s not hard!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/plot-twist.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Final Space — What a twist!</em></p>
<p>At least it’s not hard to get started. Thankfully, the parts that are the hardest are already built into libraries that can easily be tapped into with JavaScript.</p>
<p>Enter Leaflet…</p>
<h1 id="heading-mapping-libraries">Mapping Libraries</h1>
<p>There are a few libraries in the mapping space right now (like <a target="_blank" href="https://openlayers.org/">OpenLayers</a>), but we like <a target="_blank" href="https://leafletjs.com/">Leaflet</a>.</p>
<p>To get started with Leaflet, first include the library’s assets on your page. Next, mount the application onto a root element within the DOM with some basic settings. You can kind of think of it like how React mounts to a DOM node, but Leaflet itself doesn’t use React. Once initialized, Leaflet allows you to start utilizing it’s API to project a basemap, add layers, tiles on those layers, and even start to draw on it.</p>
<h3 id="heading-basemap-layers-tiles">Basemap? Layers? Tiles?</h3>
<p>To get the basic gist, think of a cake. Traditionally, cakes have different layers, some on the bottom, some on the top, some might just cover one side with icing. Your map layers function similarly. The bottom layer, which is your foundation, will be your “basemap”. Below, we’re seeing a snapshot of the 2018 California Camp Fire wildfires on top of NASA’s <a target="_blank" href="https://terra.nasa.gov/about/terra-instruments/modis">MODIS Aqua</a> satellite imagery.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/modis-aqua-campfire-california-wildfires-1024x535.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>MODIS Aqua – California “Campfire” Wildfires</em></p>
<p>Now, to get a basemap, we need the imagery to produce it, which is where tiles come in. A tile is a single image block that makes up your group of tiles that represent your layer.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/modis-aqua-tms-tile-diagram-1024x535.jpg" alt="Image" width="600" height="400" loading="lazy">
_MODIS Aqua single tile and URI scheme – <a target="_blank" href="https://gibs-a.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/2018-11-08/EPSG3857_250m/8/97/41.jpg">Tile Link</a>_</p>
<p>Your tiles are really just a simple image, but alongside the rest, coordinated by geographic positions and zoom levels, make up what you see when you’re looking at a web map like the basemap shown above. The goal of including these smaller individual pieces rather than 1 huge image is that between dealing with the entire globe, the different zoom levels available, and the resolutions available beyond that, we’re talking about gigabytes upon gigabytes of image assets that just wouldn’t be reliable or realistic to serve as a whole.</p>
<p>Once you’ve established your basemap, you can then overlay additional layers using more imagery, vector tiles, or datapoints that get transformed to layers. In the screenshot below, we’re zoomed in beyond the highest resolution of our basemap. Notice though the imagery on the left, is an individual overlay tile from <a target="_blank" href="http://blog.digitalglobe.com/news/open-data-response-for-the-california-wildfires/">Digital Globe</a> that provides us with a higher resolution of part of the area surrounding the fire zone.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/modis-aqua-with-digital-globe-tile-1024x535.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>MODIS Aqua with tile overlay from Digital Globe</em></p>
<p>Another example on top of that is adding points representing fires collected from NASA’s <a target="_blank" href="https://earthdata.nasa.gov/earth-observation-data/near-real-time/download-nrt-data/viirs-nrt">VIIRS</a> imagery.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/modis-aqua-with-viirs-fire-data-1024x535.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>MODIS Aqua with VIIRS fire datapoint layer</em></p>
<p>This allows us to have the context of the basemap as well as being able to cast any type of data we’d like to better understand its effects.</p>
<p>In addition to the VIIRS data, there are many sources of imagery, vector tiles, and datasets published by governments and municipalities that you can use to help build interesting maps and data visualizations. NASA is one good source of these types of assets, but many commercial providers release <a target="_blank" href="https://www.digitalglobe.com/ecosystem/open-data">open access to disaster datasets</a> that help others build solutions around relief efforts.</p>
<h3 id="heading-whats-this-about-drawing-stuff">What’s this about drawing stuff?</h3>
<p>Usually when people use maps, they want to look at points of interest. Drawing gives us the ability to frame those areas of interest with different drawing tools such as creating a rectangle using a bounding box tool or drawing a circle. These are simple shapes, but those shapes represent a geographic space that can then be used to gather data about that area.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/bounding-box-alexadria-va-1024x535.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Rectangular bounding box around Alexandria, VA</em></p>
<h1 id="heading-react-leaflet">React ❤️ Leaflet</h1>
<p>Leaflet in itself gives you a lot to work with, but there’s still a lot of manual effort that goes along with it. If you’re used to building a React app, you’re probably not as used to building an entire UI using nothing but APIs based on the browser’s window, and this is where <a target="_blank" href="https://react-leaflet.js.org/">React Leaflet</a> shines.</p>
<p>React Leaflet is a React library that takes the map building and bundles it into intuitive components that represents those parts of the map. Consider the above, where we talked about your basemap and layers to along with it, you might see it looking something along the lines of:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/map-component-code.png" alt="Image" width="600" height="400" loading="lazy">
<em>Pseudo map component code</em></p>
<p>While you would probably expect that it’s not <em>as</em> flexible as utilizing the Leaflet APIs directly, this completely opens up one’s world to being able to easily spin up simple map solutions in an intuitive way without all the effort. After all, at that point, you’re spinning up a React app which you’re probably already familiar with.</p>
<h1 id="heading-taking-it-a-bit-further-with-gatsby">Taking it a bit further with Gatsby</h1>
<p>You want it easier, you say? You want me to build the map for you, you say? Well, you’re in luck! First, let’s give a brief introduction to another tool.</p>
<p>For the unfamiliar, <a target="_blank" href="https://www.gatsbyjs.org/">Gatsby</a> is a javascript framework that allows developers to easily spin up full, completely working React applications in a matter of minutes. They have all the nuts and bolts in place and moved out of the way to let you do what you do best: focus on the important parts of your application.</p>
<p>The beautiful part about Gatsby is that it supports extensions of their default installation which they call <em>Starters</em>. What better way to make it easier for people to spin up maps than to create a Gatsby Starter?</p>
<h1 id="heading-gatsby-starter-leaflet">Gatsby Starter Leaflet</h1>
<p>Combining the ease of a Gatsby Starter and the flexibility of Leaflet, we have <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">Gatsby Starter Leaflet</a>. This simple tool allows you to scaffold a new React application running Leaflet along side React Leaflet in the matter of seconds (or minutes depending on your computer).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/gatsby-starter-leaflet-map-1024x535.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Starting page for Gatsby Starter Leaflet</em></p>
<p>With <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">a few basic commands</a>, including installing your dependencies, you have an app that’s ready for you to start building on top of to create maps that will save the world. Even better, it includes some out of the box integrations like <a target="_blank" href="https://www.openstreetmap.org/">OpenStreetMap</a> and an easy to setup map service configuration to the foundational React Leaflet component APIs that allow you to easily get product and have more flexibility to create smarter Mapping apps.</p>
<h1 id="heading-theres-gotta-be-some-downsides">There’s gotta be some downsides…</h1>
<p>No library or framework isn’t without its downsides. The more complicated your mapping application gets, the more pain points you run into. Here are a few from our experience that might help you settle in.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/bad-news.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Bob Kelso — Scrubs</em></p>
<h1 id="heading-leaflet-from-the-window-to-react">Leaflet — from the Window to React</h1>
<p>Trying to manage state and the lifecycle between your Leaflet map and your React components can prove to be tricky. Trying to constantly maintain and update your component using props will immediately start to create issues between stale map state or memory leaks due to maps not properly unmounting when the component does.</p>
<p><strong>Advice:</strong> mount your map with React, interact with it using the native Leaflet API. Once your map is rendered and settled down, you can use Leaflet to fly your user around the world and draw on your map without running into the state issues of multiple component renders.</p>
<h1 id="heading-limited-use-of-public-tiles">Limited Use of Public Tiles</h1>
<p>While there are a few tiling services available that allow you to easily plug in and create a basemap, not all of these are actually intended to be heavily used. Take for instance OpenStreetMap, while you may be able to play around and develop basic solutions on their public endpoint, heavy use will be throttled and potentially blocked without explicit permission from those who maintain their servers.</p>
<p><strong>Advice</strong>: when you’re just starting out playing around, you shouldn’t have to worry too much. Worst case the maps will be a little slow to download. As your application starts to get more traffic, you’ll want to look into <a target="_blank" href="https://github.com/Overv/openstreetmap-tile-server">spinning up your own tiling service</a> or paying for an out of the box solution such as <a target="_blank" href="https://www.mapbox.com/">Mapbox</a>.</p>
<h1 id="heading-get-mapping">Get mapping!</h1>
<p>It has never been easier to build a map-based web application. There is enough tooling, documentation, and public data available to help you get off the ground and start building maps to explore our world in the time it takes you to set up a blog or static website. So what are you waiting for?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/dora-explorer.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Go explore with Dora!</em></p>
<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="2000" 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>

<p><em>Originally published at <a target="_blank" href="https://www.element84.com/blog/mapping-with-leaflet-and-react">https://www.element84.com/blog/mapping-with-leaflet-and-react</a></em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is linting and how can it save you time? ]]>
                </title>
                <description>
                    <![CDATA[ One of the biggest challenges in software development is time. It’s something we can’t easily get more of, but linting can help us make the most out of the time we have. So what is linting? lint, or a linter, is a tool that analyzes source code to fl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-linting-and-how-can-it-save-you-time/</link>
                <guid isPermaLink="false">66b8e39893a17625e9eef113</guid>
                
                    <category>
                        <![CDATA[ clean code ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Code Quality ]]>
                    </category>
                
                    <category>
                        <![CDATA[ code review ]]>
                    </category>
                
                    <category>
                        <![CDATA[ eslint ]]>
                    </category>
                
                    <category>
                        <![CDATA[ front end ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Wed, 09 Oct 2019 14:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/10/linting.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>One of the biggest challenges in software development is time. It’s something we can’t easily get more of, but linting can help us make the most out of the time we have.</p>
<h2 id="heading-so-what-is-linting">So what is linting?</h2>
<p><strong>lint</strong>, or a <strong>linter</strong>, is a tool that analyzes source code to flag programming errors, bugs, stylistic errors, and suspicious constructs. <a target="_blank" href="https://en.wikipedia.org/wiki/Lint(software)">https://en.wikipedia.org/wiki/Lint(software)</a></p>
<p>Simply put, a linter is a tool that programmatically scans your code with the goal of finding issues that can lead to bugs or inconsistencies with code health and style. Some can even help fix them for you!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/miachel-scott-tell-me-more.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Michael Scott - Tell me more</em></p>
<p>Take for instance, the following example:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> test = <span class="hljs-string">'I am a test'</span>;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Test: <span class="hljs-subst">${test}</span>`</span>);
<span class="hljs-keyword">const</span> test = <span class="hljs-string">'Another one.'</span>;
</code></pre>
<p>We’re declaring the constant <code>test</code> twice, which our javascript engine won’t be happy about. With the proper linter settings and watch configuration, instead of getting caught later as an error when the code runs, you’ll immediately get an error through your linter running in the background:</p>
<pre><code>  <span class="hljs-number">10</span>:<span class="hljs-number">9</span>  error  Parsing error: Identifier <span class="hljs-string">'test'</span> has already been declared

   <span class="hljs-number">8</span> |   <span class="hljs-keyword">const</span> test = <span class="hljs-string">'I am a test'</span>;
   <span class="hljs-number">9</span> |   <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Test: <span class="hljs-subst">${<span class="hljs-number">2</span>}</span>`</span>);
&gt; <span class="hljs-number">10</span> |   <span class="hljs-keyword">const</span> test = <span class="hljs-string">'Another one.'</span>;
     |         ^
</code></pre><p>It might be pretty obvious that you have 2 of the same <code>const</code> declarations given this is only 3 lines, but in a more complex application, this can save tons of time having to hunt down a pesky bug that’s not always obvious.</p>
<h2 id="heading-what-all-can-linting-help-with"><strong>What all can linting help with?</strong></h2>
<p><a target="_blank" href="https://eslint.org/docs/rules/">Lots of things</a>, including but not limited to:</p>
<ul>
<li>Flagging bugs in your code from syntax errors</li>
<li>Giving you warnings when code may not be intuitive</li>
<li>Providing suggestions for common best practices</li>
<li>Keeping track of TODO’s and FIXME’s</li>
<li>Keeping a consistent code style</li>
</ul>
<p>Most things you can think of probably already <a target="_blank" href="https://github.com/dustinspecker/awesome-eslint">exist in one form or another</a>, and if not, you can even <a target="_blank" href="https://gist.github.com/sindresorhus/1656c46f23545deff8cc713649dcff26">create custom rules</a> that fit your needs!</p>
<h2 id="heading-how-is-this-actually-helping-or-why-should-i-care"><strong>How is this actually helping or why should I care?</strong></h2>
<p>Probably the biggest overlying theme of the list above is the fact that these issues will be called out immediately. No longer will these issues creep up on you in the middle of running your app or give someone anxiety during a code review. No longer will you and your reviewer endlessly fight passive aggressively through the comments about whether or not to include a semicolon at the end of JS statements (<a target="_blank" href="https://stackoverflow.com/a/444082">you should</a> ?).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/looking-for-semicolon.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Grandma looking for a semicolon</em></p>
<p>All of those moments that stop you from being productive because of a silly syntax error or the micro-interactions you and your teammates have during a review take time. They add up and end up taking away the time you can spend fixing another bug or developing the next great feature of your product.</p>
<h2 id="heading-so-how-do-i-actually-get-started"><strong>So how do I actually get started?</strong></h2>
<p>Even though there are linters for most, if not all, other mainstream languages, for the purpose of this post, I’m going to focus on Javascript. The same principles apply, but the tooling may be a bit different.</p>
<p>I’m going to run through how you can get set up for basic linting in a React app. You can easily follow along by spinning up your own React app or using my <a target="_blank" href="https://www.gatsbyjs.org/">Gatsby</a> starter: <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-sass#starting-from-scratch">https://github.com/colbyfayock/gatsby-starter-sass#starting-from-scratch</a></p>
<h3 id="heading-your-linter"><strong>Your Linter</strong></h3>
<p>To get started, we first need a linter. <a target="_blank" href="https://trends.google.com/trends/explore?geo=US&amp;q=eslint,jshint,jslint">Probably the most popular</a> in the Javascript world is <a target="_blank" href="https://eslint.org/">ESLint</a>. Your linter will actually be the engine for defining rules and parsing your files to test against. ESLint is available as an <a target="_blank" href="https://www.npmjs.com/package/eslint">npm package</a> by itself and <a target="_blank" href="https://eslint.org/docs/user-guide/getting-started">once installed</a>, out of the box it allows you to set up a basic configuration file and hit the ground running with some command line tools.</p>
<p>Let’s first add our ESLint dependency:</p>
<pre><code>yarn add eslint -D
</code></pre><p>We’re installing this as a <code>devDependency</code> (hence the <code>-D</code> flag), because this isn’t something our application needs to run. After it’s successfully installed, let’s add it to our <code>package.json</code> as a script:</p>
<pre><code class="lang-json">...
<span class="hljs-string">"scripts"</span>: {
  ...
  <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint .  --ext .js"</span>
  ...
},
...
</code></pre>
<p>In the above, we’re running our linter on the entire project directory on any file that has an extension of <code>.js</code>. If you're working with a large project with many file types, maybe even some you don't want linted, you can <a target="_blank" href="https://eslint.org/docs/user-guide/command-line-interface">change that flag or be more specific</a> with other options.</p>
<p>To support ESLint, we’ll need to do one more thing. Let’s add a file at the root of our project (probably where your <code>package.json</code> is) called <code>.eslintrc.js</code> and make the contents of the file simply:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {};
</code></pre>
<p>Once you’re ready, you can run <code>yarn lint</code> and… get an error.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/lint-with-import-errors.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Lint results - Import errors</em></p>
<p>This is okay, and expected in our project, so let’s move on.</p>
<h3 id="heading-your-parser"><strong>Your Parser</strong></h3>
<p>A common tool in the chain for Javascript developers is <a target="_blank" href="https://babeljs.io/">Babel</a>, which allows you to write code with features that may not be supported in all browsers, such as using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">arrow functions</a>, that are available in <a target="_blank" href="http://es6-features.org/#Constants">ES6</a>, and other conventions like importing files via <code>import</code>.</p>
<p>The code you write may already run through Babel to work in a browser, but that doesn’t apply to ESLint by default, so ESLint allows you to specify a parser that allows the linting processing to look at the same code as your browser sees. In this case we’ll want to use <a target="_blank" href="https://github.com/babel/babel-eslint">Babel’s ESLint</a> parser that’s made available to us.</p>
<p>To set that up, we’ll want to first install our dependency:</p>
<pre><code>yarn add babel-eslint -D
</code></pre><p>Typically if you're using <code>babel-eslint</code> you'll want to make sure <code>babel</code> is installed next to it, but in our case, Gatsby already uses <code>babel</code>, so we don’t necessarily need to add it. After that’s set up, we’ll want to update our <code>.eslintrc.js</code> config file with some new options:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
    <span class="hljs-string">"env"</span>: {
        <span class="hljs-string">"browser"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-string">"node"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-string">"es6"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"parser"</span>: <span class="hljs-string">"babel-eslint"</span>
};
</code></pre>
<p>Here, we’re letting ESLint know that our environment will be run in node (Gatsby’s precompiling), inside the browser (the app), and it will use ES6. This helps ESLint know how to run your code. Additionally, we want to set up our parser to be <code>babel-eslint</code>.</p>
<p>Once we’re ready to go, run <code>yarn lint</code> again and… well nothing really happened.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/lint-with-nothing-happening.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Lint results - Nothing happened</em></p>
<p>This is still expected, as we don’t have any rules set up!</p>
<h3 id="heading-plugins-for-your-code"><strong>Plugins for your code</strong></h3>
<p>Writing a <a target="_blank" href="https://reactjs.org/">React</a> app? The Babel parser may help you transform your code, but you might have a hard time being productive, as ESLint needs to understand how it should work to lint your React files.</p>
<p>Part of the beauty of ESLint is that it allows you to <a target="_blank" href="https://eslint.org/docs/developer-guide/working-with-plugins">configure plugins</a> that have the opportunity to create and set rules for you. Luckily, along with our Babel parser above that does some of the heavy lifting, we have a <a target="_blank" href="https://github.com/yannickcr/eslint-plugin-react">React plugin</a> available that does just that and takes care of linting the JSX for us.</p>
<p>Let’s first install our dependency:</p>
<pre><code>yarn add eslint-plugin-react -D
</code></pre><p>Further, let’s update our <code>.eslintrc.js</code> file again:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
    <span class="hljs-string">"settings"</span>: {
        <span class="hljs-string">"react"</span>: {
            <span class="hljs-string">"version"</span>: <span class="hljs-string">"detect"</span>
        }
    },
    <span class="hljs-string">"env"</span>: {
        <span class="hljs-string">"browser"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-string">"node"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-string">"es6"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"plugins"</span>: [
        <span class="hljs-string">"react"</span>
    ],
    <span class="hljs-string">"parser"</span>: <span class="hljs-string">"babel-eslint"</span>
};
</code></pre>
<p>What we’re adding here is a setting that automatically detects what React version you’re using, which is helpful to let your linting get parsed properly, and then set up our react plugin that we installed above.</p>
<p>For one last final time, we will run our <code>lint</code> script and get nothing:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/lint-with-nothing-happening-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Lint results - Nothing happened</em></p>
<h3 id="heading-rules-defined-by-otherss-opinions"><strong>Rules defined by others’s opinions</strong></h3>
<p>If you’re not really sure where to get started or just want to quickly get up and running, it’s recommend that you enable <a target="_blank" href="https://eslint.org/docs/rules/">ESLint’s own recommended rules</a>. Let’s add this to our <code>.eslintrc.js</code> config file:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
    <span class="hljs-string">"settings"</span>: {
        <span class="hljs-string">"react"</span>: {
            <span class="hljs-string">"version"</span>: <span class="hljs-string">"detect"</span>
        }
    },
    <span class="hljs-string">"env"</span>: {
        <span class="hljs-string">"browser"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-string">"node"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-string">"es6"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"plugins"</span>: [
        <span class="hljs-string">"react"</span>
    ],
    <span class="hljs-string">"extends"</span>: [
        <span class="hljs-string">"eslint:recommended"</span>
    ],
    <span class="hljs-string">"parser"</span>: <span class="hljs-string">"babel-eslint"</span>
};
</code></pre>
<p>And let’s run our <code>yarn lint</code>.</p>
<p>Woah! This will immediately give you a lot errors, it seems like something’s wrong.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/lint-with-react-errors.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Lint results - React errors</em></p>
<p>Since we’re running a React app, we also want to make sure our linter understands the rules it should follow, so let’s also add our React plugin to the <code>.eslintrc.js</code> config setup:</p>
<pre><code class="lang-json">    <span class="hljs-string">"extends"</span>: [
        <span class="hljs-string">"eslint:recommended"</span>,
        <span class="hljs-string">"plugin:react/recommended"</span>
    ],
</code></pre>
<p>Now if you run <code>yarn lint</code>, you get something a little more logical.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/lint-with-errors.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Lint results - Normal errors</em></p>
<p>If you’re following along, it looks like our starter app had a misplaced semicolon and a missing prop in our <code>propTypes</code> validation for <code>Layout.js</code>. Writing this helped me fix my own repo! ?</p>
<p>Going further, if those don’t seem to fit your needs, lots of developers and teams have published their own configurations that ESLint allows you to easily tap into.</p>
<p>Some popular ones include:</p>
<ul>
<li><a target="_blank" href="https://www.npmjs.com/package/eslint-config-airbnb">Airbnb’s config</a></li>
<li><a target="_blank" href="https://github.com/standard/eslint-config-semistandard">Semistandard</a></li>
<li><a target="_blank" href="https://github.com/google/eslint-config-google">Google’s JS Style Guide</a></li>
</ul>
<p>Not happy with the options available? You can even <a target="_blank" href="https://eslint.org/docs/6.0.0/developer-guide/shareable-configs">create and publish your own</a> to either layer on top of others as a starting point or give it a go from scratch.</p>
<h3 id="heading-let-it-do-the-work-most-of-it"><strong>Let it do the work (most of it)</strong></h3>
<p>You don’t think I’m going to make you fix all of those thing yourself do you? Well, you may have to fix some, but let’s try to get ESLint to fix some of it for us.</p>
<p>If you noticed after we ran the command above, ESLint gave us an extra message:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/lint-fix-option.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Lint results - Option to fix</em></p>
<p>So let’s give it a try! Let’s run:</p>
<pre><code>yarn lint --fix
</code></pre><p>And what do you know, it now only gives us 1 linting error.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/lint-with-one-error.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Lint results - 1 error</em></p>
<p>Turns out ESLint was able to fix our semicolon issue automatically, but we’ll still have to add <code>pageName</code> to our <code>Layout</code>’s <code>propTypes</code> manually, not too much of a lift.</p>
<p>Running one more time and finally nothing again! But this time because everything's looking good.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/lint-with-nothing-happening-2.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Lint results - No errors</em></p>
<h2 id="heading-go-forth-and-write-messy-code"><strong>Go forth and write messy code!</strong></h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/bruce-almighty-typing.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Bruce Almighty - Typing</em></p>
<p>Kidding ? The good news here, is now you can easily take a look at the general health of your codebase as well as hopefully fix most of it automatically. This is going to save a lot of headaches as you work with others on your team, and generally, it’s nice to have all of your code neat and tidy.</p>
<p>This post is just getting started. ESLint is a wide open book with tons of plugins and rules, and it’s not the only linting tool in the game. Play around and find what fits best for you and your team. The little time spent configuring it to your app will save you lots more time in the long run.</p>
<h2 id="heading-other-linting-tools-to-check-out"><strong>Other linting tools to check out</strong></h2>
<ul>
<li><a target="_blank" href="https://jshint.com/">JSHint</a>: an alternative to ESLint</li>
<li><a target="_blank" href="https://github.com/stylelint/stylelint">Stylelint</a>: a linting tool for CSS and CSS-like syntaxes like <a target="_blank" href="https://sass-lang.com/">Sass</a></li>
<li><a target="_blank" href="https://github.com/dustinspecker/awesome-eslint">Awesome ESLint</a>: a simple list of awesome configs, parsers, plugins, and other tools to boost your ESLint game</li>
<li><a target="_blank" href="https://webhint.io/">Webhint</a>: linting tool for accessibility, speed, and more website best practices</li>
<li><a target="_blank" href="https://github.com/evcohen/eslint-plugin-jsx-a11y">A11y JSX Plugin</a>: ESLint plugin for checking accessibility rules on JSX elements</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="2000" 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>

<p><em>Originally published</em> at <a target="_blank" href="https://www.colbyfayock.com/2019/10/what-is-linting-and-how-can-it-save-you-time">https://www.colbyfayock.com/2019/10/what-is-linting-and-how-can-it-save-you-time</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Gatsby and why it's time to get on the hype train ]]>
                </title>
                <description>
                    <![CDATA[ Frameworks come and go, and while Gatsby may eventually drift as tech does, the performance and productivity boosts are strong arguments for diving in right away. Wait up, what is Gatsby? Gatsby is a free and open source framework based on React tha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-gatsby-and-why-its-time-to-get-on-the-hype-train/</link>
                <guid isPermaLink="false">66b8e39693a17625e9eef111</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[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Wed, 25 Sep 2019 14:54:54 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/09/gatsby-train.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Frameworks come and go, and while Gatsby may eventually drift as tech does, the performance and productivity boosts are strong arguments for diving in right away.</p>
<h2 id="heading-wait-up-what-is-gatsby"><strong>Wait up, what is Gatsby?</strong></h2>
<blockquote>
<p><a target="_blank" href="https://www.gatsbyjs.org/">Gatsby</a> is a free and open source framework based on React that helps developers build blazing fast <strong>websites</strong> and <strong>apps</strong></p>
</blockquote>
<p>Their emphasis (I’ll explain later). I like to describe it as <a target="_blank" href="https://facebook.github.io/create-react-app/">Create React App</a> on steroids, where it’s an easy way to bootstrap a React application and focus on getting productive with the truly different parts of your application. At it’s core, it’s a glorified <a target="_blank" href="https://webpack.js.org/">Webpack</a> app, where everything gets built upon that same Webpack pipeline that you’ve struggled to write and fully understand til this day (or maybe that’s just me).</p>
<p>The beauty though is that what it outputs is a static website (simple HTML file) with your application prerendered for optimal delivery. This means it can essentially run anywhere like simply dumping it in <a target="_blank" href="https://aws.amazon.com/s3/">S3</a> and <a target="_blank" href="https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html">serving it as a static webpage</a> or even easier, have <a target="_blank" href="https://www.netlify.com/">Netlify</a> <a target="_blank" href="https://www.netlify.com/blog/2016/02/24/a-step-by-step-guide-gatsby-on-netlify/">build and serve it for you</a>.</p>
<h3 id="heading-its-all-in-the-scripts"><strong>It’s all in the scripts</strong></h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/chubbs-happy-gilmore.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Chubbs with Happy Gilmore</em></p>
<p>Gatsby is one of the many tools available that supports the <a target="_blank" href="https://jamstack.org/">JAMstack</a> architecture. For the unfamiliar, JAM stands for Javascript APIs and Markup, or pretty much a static HTML website that utilizes javascript to make the magic happen.</p>
<p>JAMstack apps have a lot of benefits right out of the box including:</p>
<ul>
<li>Easy to host</li>
<li>Cheap to host</li>
<li>It loads super quick (most of the time)</li>
</ul>
<p>This means generally more often than not, you’re going to have a fast site that doesn’t cost you a lot of money to both host and maintain.</p>
<h3 id="heading-sound-a-bit-familiar"><strong>Sound a bit familiar?</strong></h3>
<p>It’s easy to compare this to <a target="_blank" href="https://rubyonrails.org/">Rails</a>, as I hear often from others on my team, and rightfully so. There’s a lot of magic behind the scenes in Gatsby!</p>
<p>But unless you’re purely leaning on plugins and themes (which is fine), at the end of the day you’re still building a React application as you would anywhere else. Your app can <em>pretty much</em> be ported to any other framework (aside from the data sourcing and page generation part). Components are components, styles are styles.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Nav = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>A normal nav component in a normal Gatsby app.<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span>
  )
}
</code></pre>
<p>Gatsby is opinionated on many aspects, but those opinions mostly fall outside of the idea that you’re still building a webpack app and those conventions are of webpack and the config behind it, not necessarily Gatsby.</p>
<h2 id="heading-so-what-actually-makes-it-so-great"><strong>So, what actually makes it so great?</strong></h2>
<h3 id="heading-literally-bootstrap-in-under-a-minute"><strong>Literally bootstrap in under a minute</strong></h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/literally.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Parks and Recreation "Literally"</em></p>
<p>Seriously. Spinning up a new Gatsby site is the literal definition of all those click bait articles saying you can do something in 5 minutes. <a target="_blank" href="https://www.gatsbyjs.org/tutorial/part-zero/#using-the-gatsby-cli">Install the CLI</a> and you’re 3 commands away from your app spun up locally or statically built.</p>
<p>For example, if you wanted to spin up a new barebones project with <a target="_blank" href="https://sass-lang.com/">Sass</a>:</p>
<pre><code class="lang-shell">$ gatsby new my-cool-gatsby-project https://github.com/colbyfayock/gatsby-starter-sass
$ cd my-cool-gatsby-project
$ yarn develop
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/gatsby-bootstrap-app.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Bootstrapping a Gatsby app</em></p>
<p>A community of Starters provides an easy entry point to your new app (or the whole thing you want).</p>
<p><em>Note: the whole “under a minute” depends on what kind of connection you’re on, as you’ll need to wait for the dependencies to download and install.</em></p>
<h3 id="heading-bringing-it-all-together-in-the-content-mesh"><strong>Bringing it all together in the content mesh</strong></h3>
<p>One of the concepts behind Gatsby is the idea of the “<a target="_blank" href="https://www.gatsbyjs.org/blog/2018-10-04-journey-to-the-content-mesh/">content mesh</a>” and Gatsby being the solution to pull it all together. Many apps, and growing, are being built with the JAMstack architecture, which helps with the idea that we can source as much of our content from as many places as we want and bring it into one application in a performant way.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/content-mesh.jpg" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://www.gatsbyjs.org/blog/2018-10-04-journey-to-the-content-mesh/">https://www.gatsbyjs.org/blog/2018-10-04-journey-to-the-content-mesh/</a></em></p>
<p>When all is said and finished, you can pull in data from many sources into one statically compiled application. That is indeed blazing fast. ?</p>
<h3 id="heading-free-performance-boost"><strong>Free performance boost!</strong></h3>
<p>Out of the box you get your supercharged webpack config including <a target="_blank" href="https://developers.google.com/web/fundamentals/performance/optimizing-javascript/code-splitting/">code splitting</a>, <a target="_blank" href="https://developers.google.com/web/fundamentals/performance/resource-prioritization">prefetching</a>, inline styles, minification of assets, and a <a target="_blank" href="https://www.gatsbyjs.org/docs/gatsby-internals/">ton of other greatness</a>. Upgrade that easily with a wealth of plugins that are easy to configure like setting up your app as a <a target="_blank" href="https://www.gatsbyjs.org/docs/progressive-web-app/">PWA</a> (<a target="_blank" href="https://developers.google.com/web/progressive-web-apps/">progressive web app</a>) and providing the ability for <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-plugin-offline/%3E">offline mode</a></p>
<p>Lots of buzzwords in there, but at the end of the day, that means your website should be snappy based on mostly best practices that are optimizing all of your site’s assets for speed and delivery, all while making it as accessible as possible by <a target="_blank" href="https://www.gatsbyjs.org/docs/babel/">transpiling for older browsers</a> and making sure slow connections aren’t wasting valuable resources. It’s difficult to confirm as a hard stat, but <a target="_blank" href="https://twitter.com/kylemathews">Kyle Mathews</a> (Gatsby Founder) claims <a target="_blank" href="https://www.gatsbyjs.org/blog/2017-09-13-why-is-gatsby-so-fast/">Gatsby sites are 2-3x faster than similar types of sites</a>.</p>
<h3 id="heading-increased-productivity"><strong>Increased productivity!</strong></h3>
<p>No longer do you have to concern yourself with the complexities of making sure your application is performant by rewriting and tweaking your webpack config to the new best practices for each new app you bootstrap. Gatsby does this all for you. It lets you focus on the import bits that makes your app special, not the scaffolding.</p>
<p>This is taken a step further with Gatsby’s <a target="_blank" href="https://www.gatsbyjs.org/plugins/">plugins</a> and the addition of <a target="_blank" href="https://www.gatsbyjs.org/docs/themes/">Themes</a>. Themes are not what you would expect in the traditional sense, or what you would expect from <a target="_blank" href="https://wordpress.org/">Wordpress</a>, but they provide a way to abstract any part of your Gatsby application so you can reuse it app to app.</p>
<p>Have a core component library you use every time? Make it a theme. Have <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-source-wordpress/">a particular configuration for data sourcing</a> that you don’t want to rewrite every time? Make it a plugin. Being able to abstract these key pieces to your app saves you time and stress on each new project you spin up, not to mention the ability to maintain those pieces in one place, fixing bugs and improvements with a simple package update.</p>
<h3 id="heading-large-community"><strong>Large community</strong></h3>
<p>Gatsby itself has a decently large community already, but on top of that you have Webpack and React, which have been around for a while. React is the <a target="_blank" href="https://2018.stateofjs.com/front-end-frameworks/overview/">most popular UI framework</a> at the moment, which makes debugging via a simple Google search much easier. It’s hard to find a problem that you’re the first and only to stumble upon.</p>
<p>If you have a Gatsby question in particular, their Github Issues is swarming with people willing to help you debug or tackle the next bug. All they ask is that you politely provide them a way to reproduce, which just makes it easier for them to help you in the first place!</p>
<h3 id="heading-friend-of-hackerman"><strong>Friend of Hackerman</strong></h3>
<p>Like to roll up your sleeve and tweak the pipeline yourself? Gatsby provides easy ways to patch into the processing whether it’s <a target="_blank" href="https://www.gatsbyjs.org/docs/node-apis/">hooking into the lifecycle of the build</a> or tweaking the <a target="_blank" href="https://www.gatsbyjs.org/docs/add-custom-webpack-config/">webpack config</a>. This allows the core of your app to be as easy or advanced as you want, though if you’re that advanced, maybe <a target="_blank" href="https://www.gatsbyjs.org/docs/creating-plugins/">help write a plugin</a> or two!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/hackerman.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Hackerman</em></p>
<h2 id="heading-the-maybe-not-so-great"><strong>The maybe not so great…</strong></h2>
<h3 id="heading-building-fast-yields-more-bugs"><strong>Building fast yields more bugs</strong></h3>
<p>You should expect that you’ll hit some snags here and there staying on the latest and greatest, after all it is open source software developing for more than just your individual site.</p>
<p>The Gatsby team has been <a target="_blank" href="https://github.com/gatsbyjs/gatsby/pulse/monthly">building very rapidly</a>, which is awesome, but moving fast is prone to overlooking things as they build. Luckily, they’re starting to push automated testing all over to help harden the code and they’re intentionally trying to avoid rushing work to address this type of concern.</p>
<p>Just make sure to be thorough on testing your package upgrades and don’t be afraid to downgrade and lock your package version if you’re running into issues.</p>
<h3 id="heading-static-sites-are-first-class-not-web-apps"><strong>Static sites are first class, not web apps</strong></h3>
<p>Gatsby pens itself as an all inclusive solution for static and non-static apps alike, but is it? Support for client only routes requires a <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-plugin-create-client-paths/">plugin</a> or <a target="_blank" href="https://www.gatsbyjs.org/docs/client-only-routes-and-user-authentication/">page creation tweak</a> which is fine, but their <a target="_blank" href="https://github.com/gatsbyjs/gatsby/issues/15398">tone</a> on <a target="_blank" href="https://github.com/gatsbyjs/gatsby/issues/16097">issues</a> tends to suggest they’re not always equally focused on the two.</p>
<p>Really the only argument here is why would you use Gatsby over Create React App for that use case? Although it may not be first class, it’s still quite functional with a bonus of the standard underlying Gatsby benefits, but clearly bugs and features aren’t going to be prioritized towards that goal.</p>
<h2 id="heading-just-give-it-a-shot-already"><strong>Just give it a shot already!</strong></h2>
<p>My opinion or any other’s doesn’t matter until you try it. Worst case you literally wasted 5 minutes between installing the dependencies and deleting the folder with the project in it, which on a positive note is a learning experience. Best case you just discovered an awesome tool that’s going to help you do great things.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/lego-build.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Lego - we could build a spaceship!</em></p>
<p>Go build, go experiment, and share what great things you make!</p>
<p><em>Edit: Changed “slowing down” to “avoid rushing” to clarify statement intent, as they’re not slowing down the amount of actual work getting put in, but trying to be more careful with it.</em></p>
<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="2000" 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>

<p><em>Originally published</em> at <a target="_blank" href="https://www.colbyfayock.com/2019/09/what-is-gatsby-and-why-its-time-to-get-on-the-hype-train">https://www.colbyfayock.com/2019/09/what-is-gatsby-and-why-its-time-to-get-on-the-hype-train</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Myth of Inaccessible React ]]>
                </title>
                <description>
                    <![CDATA[ By Ben Robertson On Twitter, in Slack, on Discord, in IRC, or wherever you  hang out with other developers on the internet, you may have heard some  formulation of the following statements: React doesn't support accessibility React makes websites in... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-myth-of-inaccessible-react/</link>
                <guid isPermaLink="false">66d45ddd8812486a37369c81</guid>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 28 Jun 2019 12:38:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/06/storybook-preview.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Ben Robertson</p>
<p>On Twitter, in Slack, on Discord, in IRC, or wherever you  hang out with other developers on the internet, you may have heard some  formulation of the following statements:</p>
<ul>
<li>React doesn't support accessibility</li>
<li>React makes websites inaccessible</li>
<li>People should write accessible HTML instead of React</li>
<li>React is ruining the internet</li>
</ul>
<p>There's a somewhat common misperception that JavaScript frameworks and <a target="_blank" href="https://www.mediacurrent.com/videos/common-accessibility-mistakes-and-how-avoid-them/">web accessibility</a> don't mix. React, being one of the largest JavaScript libraries, is often the target.</p>
<p>In  my career, however, I have had the interesting experience of being  introduced to accessibility and ReactJS at around the same time. I found  tooling in React that helped me learn a lot about accessibility that I never would have encountered otherwise.</p>
<p>And while I don't disagree  that there are plenty of libraries, websites, apps, etc. written in  React that are inaccessible, I do disagree there is something inherent  in ReactJS that makes developers build inaccessible sites. In fact, I <strong>love</strong> the accessibility tooling available in the React ecosystem, so this post is really about how React can help you make <em>more accessible</em> websites than you've ever made before.</p>
<p>I'll  outline how you can combine React linting tools, DOM auditing, and  Storybook (a component library tool) to provide a really supportive  accessibility environment for developers -- whether they are  accessibility pros or just getting started. By the end of this post,  you'll have the following configured for your <a target="_blank" href="https://www.mediacurrent.com/videos/webinar-recording-rain-gatsbyjs-fast-tracking-drupal-8/">Gatsby project</a> (or other React project):</p>
<ul>
<li>in-editor reporting of accessibility errors</li>
<li>a pre-commit hook for preventing accessibility errors from getting into the repository</li>
<li>browser console reporting of accessibility errors during development, with links to info on how to resolve the errors</li>
<li>a component library with built-in accessibility testing so all  project stakeholders can hold the team accountable for accessibility  issues</li>
</ul>
<p><em>Want to get started right away? I created a Gatsby starter with all these accessibility tools built in. Checkout the <strong><a target="_blank" href="https://github.com/benjamingrobertson/gatsby-starter-accessibility">gatsby-starter-accessibility repo</a></strong> that has all these features available out of the box.</em></p>
<h2 id="heading-tools-and-setup">Tools and Setup</h2>
<h3 id="heading-eslint-plugin-jsx-a11yhttpsgithubcomevcoheneslint-plugin-jsx-a11y"><strong><a target="_blank" href="https://github.com/evcohen/eslint-plugin-jsx-a11y">eslint-plugin-jsx-a11y</a></strong></h3>
<p>If you've written JavaScript over the past few years, you've probably used or at least heard of <a target="_blank" href="https://eslint.org/">ESLint</a>. If not, now is a great time to get started with it!</p>
<p>ESLint  is a linting utility for JavaScript that helps you catch formatting and  syntax errors while you are writing code. Most editors have some sort  of linting configuration built in, which lets you see errors in your  editor while you code.</p>
<p>This is really helpful for keeping code consistent, especially when there's a lot of people working on a project.</p>
<p>ESLint  also has a really healthy plugin ecosystem. You can include rules  specific to the JavaScript framework you are working with (i.e., React,  Angular, Vue, etc), among others. For React, I typically use the <code>eslint-plugin-react</code> and the really helpful <a target="_blank" href="https://github.com/evcohen/eslint-plugin-jsx-a11y">eslint-plugin-jsx-a11y</a>. This plugin lints your code for known accessibility violations, using <a target="_blank" href="https://github.com/evcohen/eslint-plugin-jsx-a11y#supported-rules">these rules</a>.</p>
<p>Having these automated tests run while you are writing code can prevent <em>so many errors</em>. Even though automated accessibility testing catches only about <a target="_blank" href="https://www.mediacurrent.com/blog/manual-accessibility-testing-why-how/">20-30% of all accessibility errors</a>,  catching these errors before they make it into a codebase can save  time, budget, and energy for doing more manual testing once the code is  in the browser.</p>
<h4 id="heading-usage">Usage</h4>
<p>Here's how you can get started with accessibility linting in your React project.</p>
<p>First, we'll need to install the necessary eslint packages:</p>
<p><code>npm install eslint eslint-plugin-react eslint-plugin-jsx-a11y --save-dev</code></p>
<p>In your package.json, add the following configuration:</p>
<pre><code class="lang-json"><span class="hljs-string">"eslintConfig"</span>: {
    <span class="hljs-attr">"parserOptions"</span>: {
      <span class="hljs-attr">"sourceType"</span>: <span class="hljs-string">"module"</span>
    },
    <span class="hljs-attr">"env"</span>: {
      <span class="hljs-attr">"node"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"browser"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"es6"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-attr">"plugins"</span>: [
      <span class="hljs-string">"react"</span>,
      <span class="hljs-string">"jsx-a11y"</span>
    ],
    <span class="hljs-attr">"extends"</span>: [
      <span class="hljs-string">"eslint:recommended"</span>,
      <span class="hljs-string">"plugin:react/recommended"</span>,
      <span class="hljs-string">"plugin:jsx-a11y/recommended"</span>
    ]
}
</code></pre>
<p>With this added to your <code>package.json</code>, ESLint will use the rules recommended by ESLint, React, and the jsx-a11y plugin while you are working.</p>
<p>You'll want to make sure your editor is set up to display linting errors in the editor for this to be really useful.</p>
<h3 id="heading-add-a-pre-commit-hook-for-preventing-inaccessible-code-in-the-codebase-using-lintstaged">Add a pre-commit hook for preventing inaccessible code in the codebase using lint:staged</h3>
<p>Now  we've got some accessibility linting set up, and hopefully everyone  working on the project has linting turned on in their editor so they can  see any errors while they work.</p>
<p>But you can't be 100% sure that  everyone will be paying attention to the linter. And even if they are,  it's easy to make a quick change, switch files, and any errors will be  out of sight, out of mind.</p>
<p>What we can do as an extra check to prevent inaccessible code from entering the codebase is to add a <em>pre-commit hook</em> that runs the linting we set up above every time a developer tries to  commit code. If an accessibility error is found, an error message will  display with the relevant linting error and location of the error, and  the commit will be prevented until the developer resolves the issue.</p>
<p><img src="https://publish.mediacurrent.com/sites/default/files/media/lint-staged-example.png" alt="lint-staged will run a pre-commit hook that will catch any accessibility errors raised by eslint-plugin-jsx-a11y" width="600" height="400" loading="lazy">
<em>lint-staged will run a pre-commit hook that will catch any accessibility errors raised by eslint-plugin-jsx-a11y</em></p>
<h4 id="heading-usage-1">Usage</h4>
<p>The easiest way to set up pre-commit linting hooks is using the <code>[lint-staged](https://www.npmjs.com/package/lint-staged)</code> <a target="_blank" href="https://www.npmjs.com/package/lint-staged">package</a>.  After you've got all your eslint configuration set up (from our first  step), run the following command in your project directory:</p>
<p><code>npx mrm lint-staged</code></p>
<p>This command will install the <code>[husky](https://www.npmjs.com/package/husky)</code> <a target="_blank" href="https://www.npmjs.com/package/husky">package</a> for managing the pre-commit hooks and look in your package.json to  automatically setup a pre-commit hook based on your linting  configuration.</p>
<p>A simple configuration that lints all JS files based on the existing eslint configuration in the repo will look like this (from <code>package.json</code>):</p>
<pre><code class="lang-json"><span class="hljs-string">"husky"</span>: {
    <span class="hljs-attr">"hooks"</span>: {
      <span class="hljs-attr">"pre-commit"</span>: <span class="hljs-string">"lint-staged"</span>
    }
},
<span class="hljs-string">"lint-staged"</span>: {
    <span class="hljs-attr">"*.js"</span>: [
      <span class="hljs-string">"eslint"</span>
    ]
}
</code></pre>
<p>You can adjust this as you see fit. For example,  sometimes you want to limit linting to certain directories. To run the  pre-commit hook only on JS files in the src directory, you would update  the lint-staged configuration like this:</p>
<pre><code class="lang-json"><span class="hljs-string">"lint-staged"</span>: {
    <span class="hljs-attr">"src/*.js"</span>: [
      <span class="hljs-string">"eslint"</span>
    ]
}
</code></pre>
<p>The great thing about <code>lint-staged</code> is that it  only lints the files that are part of the current commit. If for some  reason there is some pre-existing errors in another part of the  codebase, the commit won't be prevented--it only prevents new errors  from being introduced.</p>
<h3 id="heading-react-axe">react-axe</h3>
<p>The great thing about the  linting setup we have now is that it will prevent a lot of errors from  being introduced into the codebase. It won't prevent all errors, however. Some errors only exist when several components are used  together, or from certain content, and can only be caught in the  browser.</p>
<p>Luckily, we have a solution for this, too. Axe is an open source engine for automated accessibility testing, supported by <a target="_blank" href="https://www.deque.com/">Deque</a>. I first became familiar with axe by using their really useful browser extension for <a target="_blank" href="https://www.mediacurrent.com/blog/5-website-accessibility-checkers/">testing individual pages in the browser</a>.</p>
<p>The problem with browser-extension accessibility testing is that they are typically only run <em>after</em> development is complete. Using the <code>react-axe library</code>,  you can have automated accessibility testing run on every page during  development, so developers can get real-time feedback on accessibility  issues. This helps make sure that accessibility issues never make it to  production, and it also educates developers who may not be accessibility  experts on potential pitfalls.</p>
<p>The <a target="_blank" href="https://github.com/dequelabs/react-axe">react-axe</a> library is an easy to use implementation of the axe engine, specifically for React.</p>
<h4 id="heading-usage-2">Usage</h4>
<p>Here's how to get started using react-axe with Gatsby (<a target="_blank" href="https://github.com/angeloashmore/gatsby-plugin-react-axe">someone made a Gatsby plugin for it!</a>):</p>
<p><code>npm install --save gatsby-plugin-react-axe</code></p>
<p>Add gatsby-plugin-react-axe to your plugins array in gatsby-config.js</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = {
 <span class="hljs-attr">siteMetadata</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">'Gatsby Default Starter'</span>,
    <span class="hljs-attr">description</span>:
      <span class="hljs-string">'Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.'</span>,
    <span class="hljs-attr">author</span>: <span class="hljs-string">'@gatsbyjs'</span>,
  },
  <span class="hljs-attr">plugins</span>: [
    <span class="hljs-string">'gatsby-plugin-react-axe'</span>,
    <span class="hljs-comment">// other plugins go here</span>
  ],
};
</code></pre>
<p>Now, when the page renders, the plugin will print any  accessibility errors to the browser console. Here's an example, where  I've put an <code>&lt;h5&gt;</code> directly underneath an <code>&lt;h1&gt;</code>:</p>
<p><img src="https://publish.mediacurrent.com/sites/default/files/media/react-axe-example.png" alt="React aXe will show accessibility errors in the console while you are developing." width="600" height="400" loading="lazy">
<em>React aXe will show accessibility errors in the console while you are developing.</em></p>
<p>​</p>
<p><em>React aXe will show accessibility errors in the console while you are developing.</em></p>
<p>You  can see that in the axe message in the console that it has identified  my heading issue: "Heading issues should only increase by one" as a  moderate issue. It also includes a link to learn more about <em>why</em> this is an issue and how to resolve it: <a target="_blank" href="https://dequeuniversity.com/rules/axe/3.2/heading-order">https://dequeuniversity.com/rules/axe/3.2/heading-order</a>. And lastly, it displays the specific element that is causing the issue for easy identification.</p>
<p>This kind of instant feedback is <em>so</em> important, whether you are an accessibility beginner or even a seasoned  pro. Catching the automated issues instantaneously can give you more  bandwidth to focus on other more involved tasks.</p>
<h3 id="heading-storybook-and-accessibility">Storybook and Accessibility</h3>
<p>The last piece of our accessibility workflow has to do with our <a target="_blank" href="https://www.mediacurrent.com/blog/building-components-breaking-it-down/">component-driven workflow</a>. For React projects, I have really enjoyed using <a target="_blank" href="https://storybook.js.org/">Storybook</a> to build and document our front end components.</p>
<p>Storybook  is an open source tool for developing UI components in isolation for  React, Vue, and Angular. It makes building stunning UIs organized and  efficient.</p>
<ul>
<li><a target="_blank" href="https://storybook.js.org/">storybook.js.org</a></li>
</ul>
<p>Besides having a nice workflow and UI, Storybook has an awesome <a target="_blank" href="https://github.com/storybooks/storybook/tree/master/addons/a11y">accessibility add-on</a> that adds a panel to each component in your component library highlighting accessibility issues.</p>
<p><em>Our  storybook configuration has built-in axe tests for each component and a  color blindness simulator, provided by the storybook accessibility  add-on.</em></p>
<p>Behind the scenes, the add-on actually also uses aXe  for testing. This is really nice, because it means that the testing we  are using in development is the same as what we are using in the  component library. Having the errors highlighted in the component  library also helps everyone on our project teams catch accessibility  issues as they are browsing the library, either for QA purposes or  design inspiration.</p>
<h4 id="heading-setup">Setup</h4>
<p>The setup for Storybook is a bit more involved, so if you haven't used Storybook before, you can checkout the <a target="_blank" href="https://storybook.js.org/docs/guides/guide-react/">Storybook for React</a> documentation for a generic React setup.</p>
<p>If you want to get Storybook running with Gatsby, see <a target="_blank" href="https://www.gatsbyjs.org/docs/visual-testing-with-storybook/">Visual Testing with Storybook</a> in the Gatsby docs.</p>
<p>Once you have Storybook setup, adding the accessibility add-on is pretty straightforward.</p>
<p>First, install the add-on:</p>
<p><code>npm install @storybook/addon-a11y --save-dev</code></p>
<p>Then add this line to your addons.js file in your storybook config directory:</p>
<p><code>import '@storybook/addon-a11y/register';</code></p>
<p>And lastly, add this line in your Storybook config.js file to automatically add the accessibility panel to all components:</p>
<p><code>addDecorator(withA11y);</code></p>
<p>When you run Storybook now, you should now see the accessibility panel (<a target="_blank" href="https://gatsby-starter-accessibility.netlify.com/storybook/?path=/story/header--default">see a live version here</a>):</p>
<p><img src="https://publish.mediacurrent.com/sites/default/files/media/storybook-preview.png" alt="Our storybook configuration has built-in axe tests for each component and a color blindness simulator, provided by the storybook accessibility add-on." width="600" height="400" loading="lazy">
<em>Our storybook configuration has built-in axe tests for each component and a color blindness simulator, provided by the storybook accessibility add-on.</em></p>
<p>As a side note - you can control the order of the tabs  in your add-ons panel based on the order that you import add-ons into  your addons.js file, if you want to have the accessibility panel display  by default, make sure it is the first line in your addons.js.</p>
<h2 id="heading-wrap-up">Wrap up</h2>
<p>If you didn't follow along with the setup or just want to get a new project setup quickly with this workflow, checkout the <a target="_blank" href="https://github.com/benjamingrobertson/gatsby-starter-accessibility">gatsby-starter-accessibility Gatsby starter!</a></p>
<p>You  can create a new Gatsby site with all the configuration I described  above out-of-the box with this single line in your terminal:</p>
<p><code>npx gatsby new my-accessible-project https://github.com/benjamingrobertson/gatsby-starter-accessibility</code></p>
<p>Or you can checkout the specific configuration in the <a target="_blank" href="https://github.com/benjamingrobertson/gatsby-starter-accessibility">repo</a>.</p>
<p>Whether you ran through all the steps above or use with the <a target="_blank" href="https://github.com/benjamingrobertson/gatsby-starter-accessibility">starter</a>, you'll have the following features set up in your Gatsby / React project:</p>
<ul>
<li>in-editor reporting of accessibility errors</li>
<li>a pre-commit hook for preventing accessibility errors from getting into the repository</li>
<li>browser console reporting of accessibility errors during development, with links to info on how to resolve the errors</li>
<li>a component library with built-in accessibility testing so all  project stakeholders can hold the team accountable for accessibility  issues  </li>
</ul>
<p>On a complex project with many team members and moving parts,  automating accessibility testing will help save time to make sure you  can pay more attention to the accessibility tasks that can't be caught  by automated tests.</p>
<p>Beyond that, tools like this can really help developers level up their accessibility knowledge.</p>
<p>I know it's helped me--I hope it helps your team too!</p>
<p><em>Want to dive deeper into building accessible websites? Join my free email course:</em> ? <em><a target="_blank" href="https://benrobertson.io/courses/common-accessibility-mistakes/">Common accessibility mistakes and how to avoid them</a>. 30 days, 10 lessons, 100% fun!</em> ? <a target="_blank" href="https://benrobertson.io/courses/common-accessibility-mistakes/"><em>Sign up here</em></a>!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Automatically Cross-post from Your GatsbyJS Blog with RSS ]]>
                </title>
                <description>
                    <![CDATA[ By Dane Stevens With the recent exodus from Medium many developers are now creating their own GatsbyJS Blogs and then cross-posting to Medium or publications like freecodecamp.org and dev.to. Cross-posting is time consuming, but necessary to drive tr... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-automatically-cross-post-from-your-gatsbyjs-blog-with-rss/</link>
                <guid isPermaLink="false">66d84e8829e30bc0ad477561</guid>
                
                    <category>
                        <![CDATA[ blog ]]>
                    </category>
                
                    <category>
                        <![CDATA[ canonical url ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ rss feed ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SEO ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 11 Jun 2019 18:45:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/06/gatsby-rss.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Dane Stevens</p>
<p>With the recent exodus from <a target="_blank" href="https://medium.com">Medium</a> many developers are now creating their own GatsbyJS Blogs and then cross-posting to <a target="_blank" href="https://medium.com">Medium</a> or publications like <a target="_blank" href="https://www.freecodecamp.org/news/">freecodecamp.org</a> and <a target="_blank" href="https://dev.to">dev.to</a>.</p>
<p>Cross-posting is time consuming, but necessary to drive traffic to your personal site. Let's look at how we can automate this by adding an RSS feed to your personal GatsbyJS blog.</p>
<h2 id="heading-add-canonical-urls-to-your-blog">Add Canonical URL's to Your Blog</h2>
<p>What is a canonical url?</p>
<p><img src="https://cdn.tueri.io/274877907146/carbon.png" alt="Canonical URL for Tueri.io" width="1108" height="416" loading="lazy"></p>
<p>A canonical url tells search engines which page is the primary or authorative page when duplicate content is found (ie. cross-posting).</p>
<p>Let's install <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-plugin-canonical-urls/">gatsby-plugin-canonical-urls</a></p>
<p><strong>Quick tip:</strong> <code>npm i</code> is an alias for <code>npm install --save</code></p>
<pre><code>npm i gatsby-plugin-canonical-urls
</code></pre><p><strong>Note:</strong> If you are using <code>gatsby-plugin-react-helmet</code> install this plugin instead: <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-plugin-react-helmet-canonical-urls/">gatsby-plugin-react-helmet-canonical-urls</a>*</p>
<pre><code>npm i gatsby-plugin-react-helmet-canonical-urls
</code></pre><p>Add plugin configuration to <code>/gatsby-config.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// In your gatsby-config.js</span>
<span class="hljs-attr">plugins</span>: [
  {
    <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-plugin-canonical-urls`</span>,
    <span class="hljs-comment">// or</span>
    <span class="hljs-comment">// resolve: `gatsby-plugin-react-helmet-canonical-urls`</span>
    <span class="hljs-attr">options</span>: {
      <span class="hljs-comment">// Change `siteUrl` to your domain </span>
      <span class="hljs-attr">siteUrl</span>: <span class="hljs-string">`https://tueri.io`</span>

      <span class="hljs-comment">// Query string parameters are inclued by default.</span>
      <span class="hljs-comment">// Set `stripQueryString: true` if you don't want `/blog` </span>
      <span class="hljs-comment">// and `/blog?tag=foobar` to be indexed separately</span>
      <span class="hljs-attr">stripQueryString</span>: <span class="hljs-literal">true</span>
    }
  }
]
</code></pre>
<p>With this configuration, the plugin will add a <code>&lt;link rel="canonical" ... /&gt;</code> to the head of every page e.g.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"canonical"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://tueri.io/2019-04-04-how-to-securely-deploy-to-kubernetes-from-bitbucket-pipelines/"</span> /&gt;</span>
</code></pre>
<h2 id="heading-install-an-rss-feed-generator">Install an RSS Feed Generator</h2>
<p>We'll use <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-plugin-feed/">gatsby-plugin-feed</a> to generate an rss feed from our blog posts.</p>
<pre><code>npm i gatsby-plugin-feed
</code></pre><p>Add plugin configuration to <code>/gatsby-config.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// In your gatsby-config.js</span>
<span class="hljs-attr">plugins</span>: [
  {
    <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-plugin-feed`</span>,
    <span class="hljs-attr">options</span>: {
      <span class="hljs-attr">query</span>: <span class="hljs-string">`
        {
          site {
            siteMetadata {
              title
              description
              siteUrl
              site_url: siteUrl
            }
          }
        }
      `</span>,
      <span class="hljs-attr">feeds</span>: [
        {
          <span class="hljs-attr">serialize</span>: <span class="hljs-function">(<span class="hljs-params">{ query: { site, allMarkdownRemark } }</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> allMarkdownRemark.edges.map(<span class="hljs-function"><span class="hljs-params">edge</span> =&gt;</span> {
              <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.assign({}, edge.node.frontmatter, {
                <span class="hljs-attr">description</span>: edge.node.excerpt,
                <span class="hljs-attr">date</span>: edge.node.frontmatter.date,
                <span class="hljs-attr">url</span>: site.siteMetadata.siteUrl + edge.node.fields.slug,
                <span class="hljs-attr">guid</span>: site.siteMetadata.siteUrl + edge.node.fields.slug,
                <span class="hljs-attr">custom_elements</span>: [{ <span class="hljs-string">"content:encoded"</span>: edge.node.html }],
              })
            })
          },
          <span class="hljs-attr">query</span>: <span class="hljs-string">`
            {
              allMarkdownRemark(
                sort: { order: DESC, fields: [frontmatter___date] },
              ) {
                edges {
                  node {
                    excerpt
                    html
                    fields { slug }
                    frontmatter {
                      title
                      date
                    }
                  }
                }
              }
            }
          `</span>,
          <span class="hljs-attr">output</span>: <span class="hljs-string">"/rss.xml"</span>,
          <span class="hljs-attr">title</span>: <span class="hljs-string">"Your Site's RSS Feed"</span>,
          <span class="hljs-comment">// optional configuration to insert feed reference in pages:</span>
          <span class="hljs-comment">// if `string` is used, it will be used to create RegExp and then test if pathname</span>
          <span class="hljs-comment">// of current page satisfied this regular expression;</span>
          <span class="hljs-comment">// if not provided or `undefined`, all pages will have feed reference inserted</span>
          <span class="hljs-attr">match</span>: <span class="hljs-string">"^/blog/"</span>,
        },
      ],
    }
  }
]
</code></pre>
<p><strong>NOTE:</strong> This plugin will only generates the <code>xml</code> file(s) when run in <code>production</code> mode! To test your feed, run: <code>gatsby build &amp;&amp; gatsby serve</code></p>
<p>Here's what our feed looks like: <a target="_blank" href="https://tueri.io/rss.xml">Tueri.io's RSS Feed</a></p>
<p>For more information on configuring your feed check out the <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-plugin-feed/">plugin docs</a>.</p>
<h2 id="heading-connect-devtohttpsdevto-to-your-rss-feed">Connect <a target="_blank" href="https://dev.to">dev.to</a> to Your RSS Feed</h2>
<ol>
<li>Log in to your <a target="_blank" href="https://dev.to">dev.to</a> account</li>
<li>Go to: Settings &gt; Publishing from RSS or <a target="_blank" href="https://dev.to/settings/publishing-from-rss">https://dev.to/settings/publishing-from-rss</a></li>
<li>Add your "RSS Feed URL" e.g. <a target="_blank" href="https://tueri.io/rss.xml">https://tueri.io/rss.xml</a></li>
<li>Check "Mark the RSS source as canonical URL by default</li>
<li>Click "Update"</li>
</ol>
<p><img src="https://cdn.tueri.io/274877907149/screencapture-dev-to-settings-publishing-from-rss-2019-06-06-06_48_32.png" alt="Screenshot of dev.to RSS settings" width="942" height="924" loading="lazy"></p>
<h2 id="heading-connect-mediumhttpsmediumcom-to-your-rss-feed">Connect <a target="_blank" href="https://medium.com">Medium</a> to Your RSS Feed</h2>
<p>The connection for <a target="_blank" href="https://medium.com">Medium</a> is not quite as straight-forward, but simple enough using <a target="_blank" href="https://zapier.com">Zapier</a>.</p>
<p>Head on over to <a target="_blank" href="https://zapier.com">Zapier</a> and create a free account.</p>
<h3 id="heading-make-a-zap">"Make a Zap"</h3>
<ol>
<li>Choose "RSS" as your "Trigger App"</li>
<li>Select "New Item in Feed"</li>
<li>Paste in your "Feed URL"</li>
<li>Select a sample from your feed.</li>
<li>Choose "Medium" as your "Action App"</li>
<li>Select "Create Story"</li>
<li>Authorize your Medium account</li>
<li>Select your fields: make sure you select your Canonical URL</li>
<li>Send a test to Medium</li>
<li>Finish and turn on your Zap</li>
</ol>
<p><img src="https://cdn.tueri.io/274877907150/screencapture-zapier-app-editor-59814153-nodes-59814313-fields-2019-06-06-06_53_55.png" alt="Screenshot of connecting RSS to Medium with Zapier" width="774" height="2065" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Make sure Google gives you credit for your content by using Canonical URL's.</p>
<p>I hope you found this helpful and that it saves you lots of time cross-posting your content!</p>
<hr>
<p>_Originally published at <a target="_blank" href="https://tueri.io/blog/2019-06-06-how-to-automatically-cross-post-from-your-gatsbyjs-blog-with-rss/?utm_source=Freecodecamp&amp;utm_medium=Post&amp;utm_campaign=">Tueri.io</a>_</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to create a searchable log with Gatsby ]]>
                </title>
                <description>
                    <![CDATA[ By Amber Wilkie For all your developer note-taking needs Taking notes is key to remembering most things in our lives. How many times have you worked on a project, then three months later needed to get back in the code, and it took you hours to come b... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-searchable-log-with-gatsby-d624bf3a05af/</link>
                <guid isPermaLink="false">66d45da03a8352b6c5a2aa05</guid>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 16 May 2019 17:23:45 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*jxvEoOYeWq64HVZAVNoI7g.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Amber Wilkie</p>
<h4 id="heading-for-all-your-developer-note-taking-needs">For all your developer note-taking needs</h4>
<p>Taking notes is key to remembering most things in our lives. How many times have you worked on a project, then three months later needed to get back in the code, and it took you hours to come back up to speed? If you had taken a few minutes to jot down some documentation, you could have cut to the chase.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/d-mmUTctxFHT7pg572F9k2oihMmBZ2Yvh74f" alt="Image" width="800" height="540" loading="lazy"></p>
<p>Personally, I keep my notes all over the place — in notebooks, mostly, but also right here on this blog. Many times when I finish a large, difficult feature, I like to blog key elements of it so I can come back later and figure out how I did what I did. Plus, it might help someone else along the way. However, there are tons of things I learn every day that just slip away. I keep learning and re-learning them and that’s inefficient.</p>
<p>I recently wanted a way to quickly jot down things I learn throughout the day, or lessons I want to keep in mind. But that’s not enough — I also need to be able to <em>search</em> these logs so I can find exactly what I’m looking for right away. That’s exactly what I’m going to show you how to build today. This project, front-to-back, took me maybe an hour and a half.</p>
<h3 id="heading-gatsby">Gatsby</h3>
<p>This project is built using <a target="_blank" href="https://www.gatsbyjs.org">Gatsby</a>, the wildly popular front-end framework for creating static websites. I’m going to skip all the sales pitch stuff and just jump into the code, but if you want to back up a step, I wrote a <a target="_blank" href="https://medium.freecodecamp.org/how-to-leverage-your-react-skills-with-static-site-generator-gatsby-js-81843e928606">long blog post about why I love Gatsby so much</a>. In short: it’s awesome if you know React, and probably worth learning anyway if you need a static site.</p>
<h3 id="heading-step-1-create-a-new-gatsby-site-using-the-beautiful-julia-template">Step 1: Create a new Gatsby site using the beautiful “Julia” template</h3>
<p>Assuming you’ve got the Gatsby CLI working, run this to pull the pared-down but beautifully laid-out <a target="_blank" href="https://github.com/niklasmtj/gatsby-starter-julia">Julia template</a>:</p>
<pre><code>gatsby <span class="hljs-keyword">new</span> &lt;site-name&gt; https:<span class="hljs-comment">//github.com/niklasmtj/gatsby-starter-julia</span>
</code></pre><p>Pop open the <code>gatsby-config.js</code> and swap out your details for “Julia Doe” under <code>siteMeta</code>. You’re halfway there.</p>
<h3 id="heading-step-2-add-logging">Step 2: Add logging</h3>
<p>Now we want to add some functionality to the site. In the <code>content</code> directory, add a markdown file or twenty. Nest them however you like. You’ll follow this format:</p>
<pre><code>---
title: <span class="hljs-string">"Whatever title you want"</span>
<span class="hljs-attr">date</span>: <span class="hljs-string">"2019-05-010"</span>
<span class="hljs-attr">draft</span>: <span class="hljs-literal">false</span>
<span class="hljs-attr">path</span>: <span class="hljs-string">"/logs/some-slug-for-the-file"</span>
<span class="hljs-attr">tags</span>: testing, documentation
---

# Monday, May <span class="hljs-number">6</span>, <span class="hljs-number">2019</span>
* Added documentation ....
</code></pre><p>Note that <code>path</code> needs to be unique for each file. I named mine by date (with each week getting one file) but obviously you can do anything you like.</p>
<h4 id="heading-step-2a-follow-the-gatsby-documentation-for-creating-pages-from-markdown">Step 2A: follow the Gatsby documentation for creating pages from Markdown</h4>
<p>I could reiterate, but the <a target="_blank" href="https://www.gatsbyjs.org/docs/adding-markdown-pages/">Gatsby documentation itself</a> is incredibly straightforward and easy to follow. You’ll install the required plugins, configure them in <code>gatsby-config.js</code>, create a template for how your posts should look, and set up <code>gatsby-node.js</code> to build pages from your markdown files.</p>
<p>To steal a tip from somewhere else on the internet: if you head to a localhost page you know doesn’t take you anywhere (I prefer <code>localhost:8000/garbage</code>), you can see all the available links for your page. It’s a quick way to check Gatsby has created all your markdown pages appropriately.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Eg7SMq2ZN2jaiO7ZMShIE9vT0UIlKYMcLXdl" alt="Image" width="800" height="228" loading="lazy"></p>
<h4 id="heading-keep-it-clean">Keep it clean</h4>
<p>I learned working on this project that you can assign multiple folders to get scanned by Gatsby’s file system plugin:</p>
<pre><code>{
  <span class="hljs-attr">resolve</span>: <span class="hljs-string">`gatsby-source-filesystem`</span>,
  <span class="hljs-attr">options</span>: {
    <span class="hljs-attr">name</span>: <span class="hljs-string">`images`</span>,
    <span class="hljs-attr">path</span>: <span class="hljs-string">`<span class="hljs-subst">${__dirname}</span>/src/images`</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">name</span>: <span class="hljs-string">`markdown-pages`</span>,
    <span class="hljs-attr">path</span>: <span class="hljs-string">`<span class="hljs-subst">${__dirname}</span>/src/content`</span>,
  },
},
</code></pre><p>So no problem if you are already using <code>gatsby-source-filesystem</code> to read, for instance, your image files. I also tested nesting, and Gatsby will grab anything in your <code>content</code> folder recursively — so you can go ahead and organize any way you like.</p>
<p>Good times! If you took that diversion to the Gatsby docs, you should now have a fully-functioning log system.</p>
<h3 id="heading-step-3-add-search">Step 3: Add search</h3>
<p>Now the fun part. We’ll add the ability to search our logs using the <a target="_blank" href="https://github.com/gatsby-contrib/gatsby-plugin-elasticlunr-search">Gatsby lunr elastic search plugin</a>.</p>
<h4 id="heading-configure">Configure</h4>
<p>First, <code>yarn add @gatsby-contrib/gatsby-plugin-elasticlunr-search</code>, then we’ll add to <code>gatsby-config.js</code>:</p>
<pre><code class="lang-js">{
  <span class="hljs-attr">resolve</span>: <span class="hljs-string">`@gatsby-contrib/gatsby-plugin-elasticlunr-search`</span>,
  <span class="hljs-attr">options</span>: {
    <span class="hljs-comment">// Fields to index</span>
    <span class="hljs-attr">fields</span>: [<span class="hljs-string">`title`</span>, <span class="hljs-string">`tags`</span>, <span class="hljs-string">`html`</span>],
    <span class="hljs-attr">resolvers</span>: {
      <span class="hljs-attr">MarkdownRemark</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-function"><span class="hljs-params">node</span> =&gt;</span> node.frontmatter.title,
        <span class="hljs-attr">tags</span>: <span class="hljs-function"><span class="hljs-params">node</span> =&gt;</span> node.frontmatter.tags,
        <span class="hljs-attr">path</span>: <span class="hljs-function"><span class="hljs-params">node</span> =&gt;</span> node.frontmatter.path,
        <span class="hljs-attr">html</span>: <span class="hljs-function"><span class="hljs-params">node</span> =&gt;</span> node.internal.content,
      },
    },
  },
},
</code></pre>
<p>Note that I’ve added a field not included on the lunr docs: <code>html</code>. We’ll need this for full text search of the logs, rather than just searching by tags.</p>
<h4 id="heading-add-a-search-bar">Add a search bar</h4>
<p>Obviously yours can go anywhere. I put mine right on the index under my name.</p>
<p><strong>The search bar component:</strong></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, StaticQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">"gatsby"</span>
<span class="hljs-keyword">import</span> Search <span class="hljs-keyword">from</span> <span class="hljs-string">"./search"</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;<span class="hljs-name">StaticQuery</span>
      <span class="hljs-attr">query</span>=<span class="hljs-string">{graphql</span>`
          <span class="hljs-attr">query</span> <span class="hljs-attr">SearchIndexQuery</span> {
            <span class="hljs-attr">siteSearchIndex</span> {
              <span class="hljs-attr">index</span>
            }
          }
        `}
      <span class="hljs-attr">render</span>=<span class="hljs-string">{data</span> =&gt;</span> (
        <span class="hljs-tag">&lt;<span class="hljs-name">Search</span> <span class="hljs-attr">searchIndex</span>=<span class="hljs-string">{data.siteSearchIndex.index}/</span>&gt;</span>
      )}
    /&gt;</span>
  )
}
</code></pre>
<p>Nothing much going on here — we’re just grabbing the search index from the elastic search data.</p>
<p>The search component, essentially copied directly from the lunr docs:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { Index } <span class="hljs-keyword">from</span> <span class="hljs-string">"elasticlunr"</span>
<span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">"gatsby"</span>
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">"@emotion/styled"</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Search</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
  state = {
    <span class="hljs-attr">query</span>: <span class="hljs-string">``</span>,
    <span class="hljs-attr">results</span>: []
 }

  render() {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.query}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.search}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
          {this.state.results.map(page =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{page.id}</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>"/" + <span class="hljs-attr">page.path</span>}&gt;</span>{page.title}<span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
              {': ' + page.tags}
            <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>
    )
  }

  getOrCreateIndex = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.index
      ? <span class="hljs-built_in">this</span>.index
      : <span class="hljs-comment">// Create an elastic lunr index and hydrate with graphql query results</span>
      Index.load(<span class="hljs-built_in">this</span>.props.searchIndex)
  }

  search = <span class="hljs-function"><span class="hljs-params">evt</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> query = evt.target.value
    <span class="hljs-built_in">this</span>.index = <span class="hljs-built_in">this</span>.getOrCreateIndex()
    <span class="hljs-built_in">this</span>.setState({
      query,
      <span class="hljs-comment">// Query the index with search string to get an [] of IDs</span>
      <span class="hljs-attr">results</span>: <span class="hljs-built_in">this</span>.index
        .search(query, { <span class="hljs-attr">expand</span>: <span class="hljs-literal">true</span> })
        <span class="hljs-comment">// Map over each ID and return the full document</span>
        .map(<span class="hljs-function">(<span class="hljs-params">{ ref }</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.index.documentStore.getDoc(ref)
        }),
    })
  }
}
</code></pre>
<p>You build a search index, fetch results based on a partial string, hydrate those results based on what the index returns, then map over them to display.</p>
<p>And that is seriously it. Your markdown pages will be built when Gatsby <code>build</code> runs and your search will index the first time you try to search.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/5RGRv6C7VR-i5sbunBvW3KwM5GjcjUagU7Hv" alt="Image" width="800" height="480" loading="lazy"></p>
<h3 id="heading-step-4-add-security">Step 4: Add security</h3>
<p>I’m not putting any state secrets or <code>env</code> variables in these logs, but I would rather not have a potential employer stumble upon them, mostly because I want to be free to talk about my struggles or be very clear about what I don’t know. If I have to censor myself, it will affect the quality of my logs.</p>
<p>At the same time, I can’t be bothered with a login or anything too fancy. So I opted for the silliest, loosest, easiest security I could come up with: a basic <code>localStorage</code> token. If you have it, you see the logs, and if not, too bad. Here’s how that works.</p>
<p>In <code>landing-bio.js</code> and anywhere else I want to protect:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> isBrowser = <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">"undefined"</span>
<span class="hljs-keyword">const</span> isAuthenticated = isBrowser() &amp;&amp; <span class="hljs-built_in">window</span>.localStorage.getItem(<span class="hljs-string">'authenticated'</span>);
[...]
{isAuthenticated ? <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SearchBar</span> /&gt;</span></span> : <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>You aren't Amber, so you don't get to read her logs.<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>}
</code></pre>
<p>I would never use this for actually-sensitive information, but it’s great for a tiny bit of peace of mind that my coworkers won’t be sneaking around my personal logs.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/6JvxsRvmiIdc76U5xZhkmhOhQMhepUSeJgUm" alt="Image" width="800" height="317" loading="lazy"></p>
<p>Note that the browser check (first line) is needed for this to pass tests on Netlify — it works fine without it otherwise.</p>
<h3 id="heading-bonus-deploy-with-netlify">Bonus: Deploy with Netlify</h3>
<p>I talked about how much I love <a target="_blank" href="https://www.netlify.com">Netlify</a> on my <a target="_blank" href="https://medium.freecodecamp.org/how-to-leverage-your-react-skills-with-static-site-generator-gatsby-js-81843e928606">previous Gatsby blog post</a>, and I still love them. It’s so dang easy to get your stuff right online.</p>
<p>All you’ll do is head over to Netlify, authorize them to access the Github where your logs are stored, and they will monitor Github and make new releases for you whenever you push to master. They will also create deploy previews when you make PRs! It’s really wonderful and I super-recommend them.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/ET0qPPLGcm14zLGqyImUUCGIq8i4SBsQ9wiw" alt="Image" width="800" height="486" loading="lazy"></p>
<p>If you are going to create logs in markdown, I highly recommend a deploy system as easy as this one, and I don’t know of another that is as seamless.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Great Gatsby.js Bootcamp ]]>
                </title>
                <description>
                    <![CDATA[ Gatsby is a framework for creating blazing fast websites and web applications. Powered by React and GraphQL, Gatsby gives you everything you need to build and launch your next project. In this course from Andrew Mead, you will learn everything you ne... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/great-gatsby-bootcamp/</link>
                <guid isPermaLink="false">66b202b8297cd6de0bd5463f</guid>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 15 May 2019 15:12:21 +0000</pubDate>
                <media:content url="https://s3.amazonaws.com/cdn-media-1.freecodecamp.org/ghost/2019/05/gatsby.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Gatsby is a framework for creating blazing fast websites and web applications. Powered by React and GraphQL, Gatsby gives you everything you need to build and launch your next project.</p>
<p>In this course from Andrew Mead, you will learn everything you need to build and launch your first site with Gatsby. You’ll build a website from scratch and learn how to get it deployed to production with tools like GraphQL. Some things you'll learn:</p>
<ol>
<li>How to create a Gatsby Site</li>
<li>Working with Gatsby Pages</li>
<li>Linking Between Pages with Gatsby</li>
<li>Creating Shared Page Components</li>
<li>Creating Gatsby Page Layouts</li>
<li>Styling Gatsby Projects</li>
<li>Styling Gatsby with CSS Modules</li>
<li>Gatsby Data with GraphQL</li>
<li>GraphQL Playground</li>
<li>Sourcing Content from the File System</li>
<li>Working with Markdown Posts</li>
<li>Generating Slugs for Posts</li>
<li>Dynamically Generating Pages</li>
<li>Rendering Post Data in Blog Template</li>
<li>Adding Images to Posts</li>
<li>Getting Started with Contentful</li>
<li>Rendering Contentful Posts</li>
<li>Dynamic Pages from Contentful</li>
<li>404 Pages and React Helmet</li>
<li>Deploying Your Gatsby Site</li>
</ol>
<p>You can watch the full video course on the <a target="_blank" href="https://www.youtube.com/watch?v=kzWIUX3CpuI">freeCodeCamp.org YouTube channel</a> (5 hour watch).</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
