<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 🌈 Josh</title>
    <description>The latest articles on DEV Community by 🌈 Josh (@joshwcomeau).</description>
    <link>https://dev.to/joshwcomeau</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F316601%2F68a19b5f-b09f-4c00-8427-d9e33d140c8d.jpeg</url>
      <title>DEV Community: 🌈 Josh</title>
      <link>https://dev.to/joshwcomeau</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joshwcomeau"/>
    <language>en</language>
    <item>
      <title>Generate an SEO-Friendly Sitemap for your Gatsby Site</title>
      <dc:creator>🌈 Josh</dc:creator>
      <pubDate>Tue, 10 Mar 2020 22:09:51 +0000</pubDate>
      <link>https://dev.to/joshwcomeau/generate-an-seo-friendly-sitemap-for-your-gatsby-site-1dp9</link>
      <guid>https://dev.to/joshwcomeau/generate-an-seo-friendly-sitemap-for-your-gatsby-site-1dp9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a cross-post from &lt;a href="https://joshwcomeau.com/gatsby/seo-friendly-sitemap/"&gt;my personal blog&lt;/a&gt;, where you'll find many other posts on React and Gatsby!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So here's the thing. I really don't want to have to care about SEO. It's all very nebulous, and it attracts so many snake-oil salespeople. SEO websites are &lt;a href="https://twitter.com/JoshWComeau/status/1236438207593660416"&gt;the worst&lt;/a&gt;. And yet, if you want people to see the stuff that you build, SEO remains super important.&lt;/p&gt;

&lt;p&gt;Happily, we don't need to become SEO experts. A few key optimizations can play a big role in our search engine results!&lt;/p&gt;

&lt;p&gt;I was intrigued by a &lt;a href="https://www.gatsbyjs.org/blog/2019-05-07-advanced-sitemap-plugin-for-seo/"&gt;recent blog post&lt;/a&gt; about how the Ghost team moved their blog to Gatsby. The move had a profound impact on their SEO:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2vRc5XZT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y5rotyybmek6sj2ewegi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2vRc5XZT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y5rotyybmek6sj2ewegi.png" alt="A graph showing the SEO impacts of moving a site to Gatsby - after the switch, SEO climbed dramatically, about 2x as much organic traffic over 2-3 months!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In that article, the author explains how adding an XML sitemap (among other factors) helped them achieve remarkable organic traffic gains. So today, this tutorial will walk you through how to generate a sitemap for your Gatsby blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an XML Sitemap?
&lt;/h2&gt;

&lt;p&gt;An XML sitemap is a raw document designed to help machines learn about the structure of a website. They look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J-sPR8YI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pi72blrwxuvj4t0oezhz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J-sPR8YI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pi72blrwxuvj4t0oezhz.png" alt="An HTML-like document shows a bunch of unintelligible markup. Upon close scrutiny, URLs from my blog can be seen."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is different from the "sitemap" sometimes linked to in the footers of websites. No human is meant to look at this, and they shouldn't be linked to. This is a document exclusively for Googlebot and its cousins.&lt;/p&gt;

&lt;h1&gt;
  
  
  Leveraging the ecosystem
&lt;/h1&gt;

&lt;p&gt;Whenever I run into a new problem when working on a Gatsby project, my first instinct is always to check and see if a solution has been created by the community. A quick search reveals &lt;a href="https://www.gatsbyjs.org/packages/gatsby-plugin-sitemap/"&gt;gatsby-plugin-sitemap&lt;/a&gt;, an officially-maintained plugin that solves this exact problem! 🎉&lt;/p&gt;

&lt;p&gt;Let's install it, either using &lt;a href="https://yarnpkg.com/"&gt;yarn&lt;/a&gt; or &lt;a href="//www.npmjs.com"&gt;npm&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add gatsby-plugin-sitemap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, we can add it to our &lt;code&gt;gatsby-config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;siteMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ✂️&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gatsby-plugin-sitemap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ✂️&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Whenever we build our site, this plugin will generate a &lt;code&gt;sitemap.xml&lt;/code&gt; file, alongside all the other files that Gatsby builds.&lt;/p&gt;

&lt;p&gt;Critically, this plugin &lt;strong&gt;only runs when building for production.&lt;/strong&gt; This means that you won't be able to test it when running in development mode. Let's build, and spin up a static server with &lt;code&gt;serve&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; serve public
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.npmjs.com/package/serve"&gt;&lt;code&gt;serve&lt;/code&gt;&lt;/a&gt; is an NPM package that will serve the files on your local filesystem. If you've never used it before, you'll first need to install it with &lt;code&gt;yarn add -g serve&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We pass &lt;code&gt;public&lt;/code&gt; as an argument, since Gatsby builds into the &lt;code&gt;/public&lt;/code&gt; directory; that's where all our static files will be served from.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should now be able to open &lt;code&gt;localhost:5000/sitemap.xml&lt;/code&gt;, and see a beautiful ugly XML document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Excluding certain paths
&lt;/h2&gt;

&lt;p&gt;Unless you're &lt;em&gt;extremely&lt;/em&gt; lucky, it's likely that this sitemap isn't quite right.&lt;/p&gt;

&lt;p&gt;One of the biggest reasons to add a sitemap is to tell Google which pages &lt;em&gt;not&lt;/em&gt; to worry about. For example, my blog had the following sites specified in the original version of my sitemap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://www.joshwcomeau.com/admin&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;daily&lt;span class="nt"&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;priority&amp;gt;&lt;/span&gt;0.5&lt;span class="nt"&gt;&amp;lt;/priority&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://www.joshwcomeau.com/confirmed&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;daily&lt;span class="nt"&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;priority&amp;gt;&lt;/span&gt;0.5&lt;span class="nt"&gt;&amp;lt;/priority&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;admin&lt;/code&gt; is an authenticated route I use for viewing stats about the website, and &lt;code&gt;confirmed&lt;/code&gt; is shown when users join my newsletter. Neither of these pages makes sense to include in search results.&lt;/p&gt;

&lt;p&gt;Happily, we can customize the plugin to pass an array of paths to exclude:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// gatsby-config.js&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;siteMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ✂️&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gatsby-plugin-sitemap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ✂️&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Advanced customizations
&lt;/h1&gt;

&lt;p&gt;When reading the &lt;a href="https://support.google.com/webmasters/answer/183668?hl=en"&gt;Google sitemap recommendations&lt;/a&gt;, I found this bit of information:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;List only canonical URLs in your sitemaps.&lt;/strong&gt; If you have two versions of a page, list only the (Google-selected) canonical in the sitemap.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;A "canonical" URL is the "true home" for a specific entity. If you have multiple URLs that contain the same content, you need to mark one as "canonical" for search engines to use.&lt;/p&gt;

&lt;p&gt;If you &lt;em&gt;don't&lt;/em&gt; do this, Google will penalize you, and it can hurt your search result rankings 😬&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In addition to this sitemap stuff, it is also a good idea to add a &lt;code&gt;&amp;lt;link rel="canonical"&amp;gt;&lt;/code&gt; tag to the head of each page with React Helmet. I'll be writing another post about this soon – &lt;a href="https://joshwcomeau.com/subscribe/"&gt;subscribe to my newsletter&lt;/a&gt; so you don't miss it!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On my blog, post URLs are in the following format: &lt;code&gt;/:category/:slug&lt;/code&gt;. This presents a problem, since posts can belong to multiple categories. For example, the post that you're reading right now can be reached through both of these URLs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/gatsby/seo-friendly-sitemap/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/seo/seo-friendly-sitemap/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The posts on my blog are all written using &lt;a href="https://mdxjs.com/"&gt;MDX&lt;/a&gt;. In the frontmatter for the posts, I have data that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
title: "Generate an SEO-Friendly Sitemap for your Gatsby Site"
type: tutorial
publishedOn: 2020-03-09T09:30:00-0400
categories: ['gatsby', 'seo']
---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Categories are listed in priority order, so the first category should always form the canonical URL.&lt;/p&gt;

&lt;p&gt;The challenge is clear: I need to fetch the categories from my MDX frontmatter and use it to filter the sites generated in the sitemap. Delightfully, this is an option with the plugin!&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying data with GraphQL
&lt;/h2&gt;

&lt;p&gt;Inside our &lt;code&gt;gatsby-config.js&lt;/code&gt;, we can write a GraphQL query to pull whatever data we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;siteMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ✂️&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gatsby-plugin-sitemap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
          {
            site {
              siteMetadata {
                siteUrl
              }
            }

            allSitePage {
              edges {
                node {
                  path
                }
              }
            }
          }
        `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By default, the plugin uses a query like this, but we can overwrite it. Here it fetches the &lt;code&gt;siteUrl&lt;/code&gt;, which in my case is &lt;code&gt;http://www.joshwcomeau.com&lt;/code&gt;, and then it fetches the path for every page node (eg. &lt;code&gt;/gatsby/seo-friendly-sitemap&lt;/code&gt;). It stitches those two strings together for every page it finds, and produces a sitemap.&lt;/p&gt;

&lt;p&gt;In order to filter out non-canonical results, we first need to expose the right data to GraphQL!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;allSitePage&lt;/code&gt; is an index of every page created, either by putting a React component in &lt;code&gt;src/pages&lt;/code&gt;, or using the &lt;code&gt;createPage&lt;/code&gt; API. In my case, I'm generating all articles/tutorials programmatically with &lt;code&gt;createPage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's what a typical &lt;code&gt;createPage&lt;/code&gt; call looks like, inside &lt;code&gt;gatsby-node.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;createPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt;
  &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* component props */&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you're building a blog with Markdown or MDX, you're probably already using this to generate your pages. You provide it a &lt;code&gt;path&lt;/code&gt; to live, a &lt;code&gt;component&lt;/code&gt; to mount, and some contextual data that the component might need. Anything passed to &lt;code&gt;context&lt;/code&gt; becomes available to the &lt;code&gt;component&lt;/code&gt; via props.&lt;/p&gt;

&lt;p&gt;Happily, it turns out that &lt;code&gt;context&lt;/code&gt; also gets exposed to GraphQL!&lt;/p&gt;

&lt;p&gt;I added a new piece of data to &lt;code&gt;context&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;createPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt;
  &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isCanonical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentCategory&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;canonicalCategory&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;currentCategory&lt;/code&gt; and &lt;code&gt;canonicalCategory&lt;/code&gt; variables were already available to me, since I was iterating through all my data and using it to create these pages.&lt;/p&gt;

&lt;p&gt;With this data added, I could update the GraphQL query passed to &lt;code&gt;query&lt;/code&gt;, in my &lt;code&gt;gatsby-config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
  {
    site {
      siteMetadata {
        siteUrl
      }
    }

    allSitePage {
      edges {
        node {
          path
          context {
            isCanonical
          }
        }
      }
    }
  }
`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;One of the most common stumbling blocks for Gatsby developers is GraphQL. It's a powerful tool, but it has a pretty steep learning curve. The Gatsby &lt;a href="https://www.gatsbyjs.org/docs/graphql-concepts/"&gt;GraphQL Concepts&lt;/a&gt; doc should help clarify some of what we're doing here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Filtering pages
&lt;/h2&gt;

&lt;p&gt;We've now exposed each page's "canonical status" to GraphQL, and written it into the query that &lt;code&gt;gatsby-plugin-sitemap&lt;/code&gt; will use. The final piece of this puzzle: overwriting the default "serializer" to specify what should be done with this queried data.&lt;/p&gt;

&lt;p&gt;Here's what that looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`gatsby-plugin-sitemap`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="cm"&gt;/* ✂️ */&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;allSitePage&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;allSitePage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isCanonical&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;siteMetadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;siteUrl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;changefreq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;daily&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;serialize&lt;/code&gt; is a function that transforms the data from the &lt;code&gt;query&lt;/code&gt; into an array of "sitemappy" objects. The items we return will be used as the raw data to generate the sitemap.&lt;/p&gt;

&lt;p&gt;Now that we've specified it in GraphQL, we can access &lt;code&gt;node.context.isCanonical&lt;/code&gt; to filter out duplicate pages.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You'll notice we're explicitly checking to see if &lt;code&gt;isCanonical&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;. This is important, since &lt;code&gt;isCanonical&lt;/code&gt; will be &lt;code&gt;undefined&lt;/code&gt; for all the non-blog-post pages, and these pages are totally worth including in the sitemap. We only want to remove pages that are &lt;code&gt;false&lt;/code&gt;, not ones that are falsy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By using the &lt;code&gt;query&lt;/code&gt; and &lt;code&gt;serialize&lt;/code&gt; escape hatches built into &lt;code&gt;gatsby-plugin-sitemap&lt;/code&gt;, we are given far greater control over the generated sitemap. It also allows us to fine-tune some page-specific options!&lt;/p&gt;

&lt;h1&gt;
  
  
  Page-specific options
&lt;/h1&gt;

&lt;p&gt;When generating the XML sitemap, you may have noticed a couple additional fields being shown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://www.your-website.com/page-1&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;daily&lt;span class="nt"&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;priority&amp;gt;&lt;/span&gt;0.5&lt;span class="nt"&gt;&amp;lt;/priority&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://www.your-website.com/page-2&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;daily&lt;span class="nt"&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;priority&amp;gt;&lt;/span&gt;0.5&lt;span class="nt"&gt;&amp;lt;/priority&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In fact, there are a handful of options that can be used to tweak each page, for optimal effects.&lt;/p&gt;

&lt;h3&gt;
  
  
  changefreq
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;changefreq&lt;/code&gt; is a measure of how often your page changes. From the &lt;a href="https://www.sitemaps.org/protocol.html"&gt;Sitemaps protocol&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This value provides general information to search engines and may not correlate exactly to how often they crawl the page. Valid values are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;always&lt;/li&gt;
&lt;li&gt;hourly&lt;/li&gt;
&lt;li&gt;daily&lt;/li&gt;
&lt;li&gt;weekly&lt;/li&gt;
&lt;li&gt;monthly&lt;/li&gt;
&lt;li&gt;yearly&lt;/li&gt;
&lt;li&gt;never&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The value "always" should be used to describe documents that change each time they are accessed. The value "never" should be used to describe archived URLs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a blog, I feel like &lt;code&gt;daily&lt;/code&gt; fits most usecases pretty well.&lt;/p&gt;

&lt;h3&gt;
  
  
  priority
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;priority&lt;/code&gt; is a &lt;em&gt;relative&lt;/em&gt; measure of a page's importance. You can use this to signal to the crawler which pages it should care about, and which aren't so important. There are 11 values available to you: &lt;code&gt;0.0&lt;/code&gt; through &lt;code&gt;1.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On this blog, I'm using it to rank article pages like this one above "index" pages like the &lt;a href="https://joshwcomeau.com/latest/"&gt;latest content&lt;/a&gt; page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're a clever trickster, you might be concocting a devious plan: set every page to a &lt;code&gt;1.0&lt;/code&gt; priority, and watch as your site rockets to the top of the search results!&lt;/p&gt;

&lt;p&gt;Unfortunately, this scheme doesn't work—&lt;code&gt;priority&lt;/code&gt; is a &lt;em&gt;relative&lt;/em&gt; measure of importance. It won't affect how your site compares to other sites.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  lastmod
&lt;/h3&gt;

&lt;p&gt;Finally, we can add a date-time stamp to indicate when the page was last modified.&lt;/p&gt;

&lt;p&gt;I'm honestly not sure how valuable this is, since presumably Googlebot is smart enough to detect when a page's content has changed, but correctly following a specification can't hurt!&lt;/p&gt;

&lt;h1&gt;
  
  
  Even more customizations
&lt;/h1&gt;

&lt;p&gt;If you feel like you're limited by the options presented by this plugin, the folks at Ghost created their own &lt;a href="https://www.gatsbyjs.org/packages/gatsby-plugin-advanced-sitemap/?=sitemap"&gt;advanced sitemap plugin&lt;/a&gt;. It uses XSL templating for a much prettier output! Because it's a newer and less-battle-tested plugin, I opted to stick with the standard one for my blog, but this could be a powerful option for folks with advanced usecases!&lt;/p&gt;

&lt;h1&gt;
  
  
  Submitting your sitemap
&lt;/h1&gt;

&lt;p&gt;Once your sitemap has been generated, and your site's been deployed, you'll need to let Google know that it exists!&lt;/p&gt;

&lt;p&gt;For this, there are a number of options. I opted to submit it via the &lt;a href="https://support.google.com/webmasters/answer/7451001"&gt;Google Search Console tool&lt;/a&gt;, though there are &lt;a href="https://support.google.com/webmasters/answer/183668?hl=en"&gt;other options&lt;/a&gt; outlined in their documentation.&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>seo</category>
    </item>
    <item>
      <title>The Perils of Rehydration: An Eye-Opening Realization about Gatsby and React</title>
      <dc:creator>🌈 Josh</dc:creator>
      <pubDate>Tue, 03 Mar 2020 22:18:53 +0000</pubDate>
      <link>https://dev.to/joshwcomeau/the-perils-of-rehydration-an-eye-opening-realization-about-gatsby-and-react-17f0</link>
      <guid>https://dev.to/joshwcomeau/the-perils-of-rehydration-an-eye-opening-realization-about-gatsby-and-react-17f0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a cross-post from &lt;a href="https://joshwcomeau.com/react/the-perils-of-rehydration/"&gt;my personal blog&lt;/a&gt;. View it there for at least 35% more whimsy!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I ran into the strangest issue recently. Everything was groovy in development, but in production, the bottom of my blog was doing something… unintended:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HUoWxZBe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://joshwcomeau.com/static/jumbled-light-aa748b654e9ec651e022a0dd3e3cef04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HUoWxZBe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://joshwcomeau.com/static/jumbled-light-aa748b654e9ec651e022a0dd3e3cef04.png" alt="jumbled screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A bit of digging into the Elements tab in the devtools revealed the culprit… My React component was rendering in the wrong spot!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- In development, things are correct --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ContentFooter"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Last updated: &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Sometime&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"NewsletterSignup"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- Newsletter signup form stuff --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;


&lt;span class="c"&gt;&amp;lt;!-- In production, things had teleported! --&amp;gt;&lt;/span&gt;  ​
&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ContentFooter"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Last updated: &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Sometime&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"NewsletterSignup"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Newsletter signup form stuff --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;How could this be? Had I discovered a bug in React? I checked the React Devtools "⚛️ Components" tab, and it told a different story, one in which everything was fine, and the pieces were all where they were supposed to be. What a liar!&lt;/p&gt;

&lt;p&gt;It turns out, I had a fundamental misunderstanding about how React works in a server-side-rendering context. And I think &lt;em&gt;many&lt;/em&gt; React devs share this misunderstanding! And it can have some pretty serious ramifications.&lt;/p&gt;

&lt;h1&gt;
  
  
  Some problematic code
&lt;/h1&gt;

&lt;p&gt;Here's an example of code that can cause the kind of rendering issue shown above. Can you spot the problem?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Pretend that this function exists,&lt;/span&gt;
  &lt;span class="c1"&gt;// and returns either a user object or `null`.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthenticatedNav&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For a long time, I would have believed that this code was A-OK. Right up until my blog started impersonating a Picasso painting.&lt;/p&gt;

&lt;p&gt;This tutorial will peek behind the curtain to help us understand how server-side rendering works. We'll see why the logic shown here can be problematic, and how a different approach can accomplish the same goal.&lt;/p&gt;

&lt;h1&gt;
  
  
  Server-side rendering 101
&lt;/h1&gt;

&lt;p&gt;To understand the problem, we need to first dig a little into how frameworks like Gatsby and Next.js differ from traditional client-side apps built with React.&lt;/p&gt;

&lt;p&gt;When you use React with something like create-react-app, all of the rendering happens in the browser. It doesn't matter how large your application is, the browser still receives an initial HTML document that looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Maybe some stuff here --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script
      &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/static/bundle.js"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script
      &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/static/0.chunk.js"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script
      &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/static/main.chunk.js"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The page is fundamentally empty, but it includes a couple JS scripts. Once the browser downloads and parses those scripts, React will build up a picture of what the page should look like, and inject a bunch of DOM nodes to make it so. This is known as &lt;em&gt;client-side rendering&lt;/em&gt;, since all the rendering happens on the client (the user's browser).&lt;/p&gt;

&lt;p&gt;All of that stuff takes time, and while the browser and React are working their magic, the user is staring at a blank white screen. Not the best experience.&lt;/p&gt;

&lt;p&gt;Smart people realized that if we could do that rendering on the &lt;strong&gt;server&lt;/strong&gt;, we could send the user a fully-formed HTML document. That way, they'd have something to look at while the browser downloads, parses, and executes the JS. This is known as &lt;em&gt;server-side rendering&lt;/em&gt; (SSR).&lt;/p&gt;

&lt;p&gt;Server-side rendering can be a performance win, but the thing is, that work still needs to be done on-demand. When you request your-website.com, React has to transform your React components into HTML, and you'll still be staring at a blank screen while you wait for it. It's just that the work is being done on the server, not on the user's computer.&lt;/p&gt;

&lt;p&gt;The galaxy-brain realization is that huge chunks of many websites and apps are static, and they can be built at &lt;em&gt;compile-time&lt;/em&gt;. We can generate the initial HTML &lt;strong&gt;way ahead of time&lt;/strong&gt;, on our development machines, and distribute it immediately when a user requests it. Our React apps can load as quickly as a vanilla HTML site!&lt;/p&gt;

&lt;p&gt;This is exactly what Gatsby does (along with Next.js, in certain configurations). When you run &lt;code&gt;yarn build&lt;/code&gt;, it generates 1 HTML document for every route on your site. Every side page, every blog post, every store item — an HTML file is created for each of them, ready to be served up immediately.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is this all just server-side rendering?&lt;/strong&gt; Unfortunately, a lot of this language is used interchangeably, and it can be kinda hard to follow. Technically, what Gatsby does &lt;em&gt;is&lt;/em&gt; server-side rendering, since it renders the React app using Node.js using the same ReactDOMServer APIs as a more traditional server-side render. In my mind, though, it's conceptually different; "server-side rendering" happens on your live production server in real-time, in response to a request, whereas this compile-time render happens much earlier, as part of the build process.&lt;/p&gt;

&lt;p&gt;Some folks have started calling it &lt;strong&gt;SSG&lt;/strong&gt;, which either stands for "Static Site Generation" or "Server-Side Generated", depending on who you ask.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Code on the client
&lt;/h3&gt;

&lt;p&gt;The apps we build nowadays are interactive and dynamic—users are accustomed to experiences that can't be accomplished with HTML and CSS alone! So we still need to run client-side JS.&lt;/p&gt;

&lt;p&gt;The client-side JS includes the same React code used to generate it at compile-time. It runs on the user's device, and builds up a picture of what the world should look like. It then compares it to the HTML built into the document. This is a process known as &lt;em&gt;rehydration&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Critically, &lt;em&gt;rehydration&lt;/em&gt; is not the same thing as a &lt;em&gt;render&lt;/em&gt;. In a typical render, when props or state change, React is prepared to reconcile any differences and update the DOM. In a &lt;em&gt;rehydration&lt;/em&gt;, React assumes that the DOM won't change. It's just trying to adopt the existing DOM.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dynamic sections
&lt;/h1&gt;

&lt;p&gt;This takes us back to our code snippet. As a reminder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Navigation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Pretend that this function exists,&lt;/span&gt;
  &lt;span class="c1"&gt;// and returns either a user object or `null`.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthenticatedNav&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This component is designed to have three possible outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user is logged in, render the &lt;code&gt;&amp;lt;AuthenticatedNav&amp;gt;&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;If the user is NOT logged in, render the &lt;code&gt;&amp;lt;UnauthenticatedNav&amp;gt;&lt;/code&gt; component.&lt;/li&gt;
&lt;li&gt;If we don't know if the user is logged in or not, render nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Schrodinger's user
&lt;/h2&gt;

&lt;p&gt;In a &lt;a href="https://en.wikipedia.org/wiki/Schr%C3%B6dinger%27s_cat"&gt;macabre thought experiment&lt;/a&gt;, Austrian physicist Erwin Schrödinger describes a situation: a cat is placed in a box with a toxin that has a 50% chance of being released within an hour. After an hour, there is an equal probability that the cat is alive or dead. But until you open the box and find out, the cat can be thought of as both alive &lt;em&gt;and&lt;/em&gt; dead.&lt;/p&gt;

&lt;p&gt;In our webapp, we face a similar predicament; for the first few moments that a user is on our site, we don't know whether they are logged in or not.&lt;/p&gt;

&lt;p&gt;This is because the HTML file is built at &lt;strong&gt;compile-time&lt;/strong&gt;. Every single user gets an identical copy of that HTML, regardless of whether they're logged in or not. Once the JS bundle is parsed and executed, we can update the UI to reflect the user's state, but there is a significant gap of time before that happens. Remember, the whole point of SSG is to give the user something to look at while we download, parse, and rehydrate the app, which can be a lengthy process on slow networks/devices.&lt;/p&gt;

&lt;p&gt;Many webapps choose to show the "logged out" state by default, and this leads to a flicker you've probably run into before.&lt;/p&gt;

&lt;p&gt;I took the liberty of building a mini Gatsby app that reproduces this issue. &lt;a href=""&gt;Visit this demo app&lt;/a&gt;, and click "Login" to simulate a login. Notice when refreshing the page, you get a flicker!&lt;/p&gt;

&lt;h2&gt;
  
  
  A noble but flawed attempt
&lt;/h2&gt;

&lt;p&gt;In the shared code snippet, we attempt to solve for this problem in the first few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Navigation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The idea here is sound: Our initial compile-time build happens in Node.js, a server runtime. We can detect whether or not we're rendering on the server by checking to see if &lt;code&gt;window&lt;/code&gt; exists. If it doesn't, we can abort the render early.&lt;/p&gt;

&lt;p&gt;The problem is that in doing so, we're breaking the rules. 😬&lt;/p&gt;

&lt;h1&gt;
  
  
  Rehydration ≠ render
&lt;/h1&gt;

&lt;p&gt;When a React app &lt;em&gt;rehydrates&lt;/em&gt;, it assumes that the DOM structure will match.&lt;/p&gt;

&lt;p&gt;When the React app runs on the client for the first time, it builds up a mental picture of what the DOM should look like, by mounting all of your components. Then it squints at the DOM nodes already on the page, and tries to fit the two together. It's not playing the “spot-the-differences” game it does during a typical update, it's just trying to snap the two together, so that &lt;em&gt;future&lt;/em&gt; updates will be handled correctly.&lt;/p&gt;

&lt;p&gt;By rendering something different depending on whether we're within the server-side render or not, we're hacking the system. We're rendering one thing on the server, but then telling React to expect something else on the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- The initial HTML
     generated at compile-time --&amp;gt;&lt;/span&gt;
    ​
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Your Site&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;


&lt;span class="c"&gt;&amp;lt;!-- What React expects
     after rehydration --&amp;gt;&lt;/span&gt;
    ​
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Your Site&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Somewhat remarkably, React can still handle this situation sometimes. You may have done this yourself, and gotten away with it. But you're playing with fire. The rehydration process is optimized to be ⚡️ fast ⚡️, not to catch and fix mismatches.&lt;/p&gt;

&lt;h3&gt;
  
  
  About Gatsby in particular
&lt;/h3&gt;

&lt;p&gt;The React team knows that rehydration mismatches can lead to funky issues, and they've made sure to highlight mismatches with a console message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NAiQzqGL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://joshwcomeau.com/static/rehydration-warning-9648f71314903cc16031a7d03b98901a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NAiQzqGL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://joshwcomeau.com/static/rehydration-warning-9648f71314903cc16031a7d03b98901a.png" alt="A dev-tools console error message: “Warning: Expected server HTML to contain a matching &amp;lt;div&amp;gt; in &amp;lt;nav&amp;gt;.”"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, &lt;strong&gt;Gatsby only uses the server-side rendering APIs when building for production&lt;/strong&gt;. And because React warnings in general only fire in development, it means that these warnings are &lt;em&gt;never shown&lt;/em&gt; when building with Gatsby 😱&lt;/p&gt;

&lt;p&gt;This is a trade-off. By opting out of server-side-rendering in dev, Gatsby is optimizing for a short feedback loop. Being able to quickly see the changes you make is &lt;a href="https://vimeo.com/36579366"&gt;so, so important&lt;/a&gt;. Gatsby prioritizes speed over accuracy.&lt;/p&gt;

&lt;p&gt;This is kind of a significant problem, though; folks in &lt;a href="https://github.com/gatsbyjs/gatsby/issues/17914"&gt;an open issue&lt;/a&gt; are advocating for a change, and we may start seeing hydration warnings.&lt;/p&gt;

&lt;p&gt;Until then, though, it is especially important to be mindful of this when developing with Gatsby!&lt;/p&gt;

&lt;h1&gt;
  
  
  The solution
&lt;/h1&gt;

&lt;p&gt;To avoid issues, we need to ensure that the rehydrated app matches the original HTML. How do we manage "dynamic" data then?&lt;/p&gt;

&lt;p&gt;Here's what the solution looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hasMounted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHasMounted&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setHasMounted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasMounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AuthenticatedNav&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Login&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/nav&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We initialize a piece of state, &lt;code&gt;hasMounted&lt;/code&gt;, to &lt;code&gt;false&lt;/code&gt;. While it's false, we don't bother rendering the "real" content.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;useEffect&lt;/code&gt; call, we immediately trigger a re-render, setting &lt;code&gt;hasMounted&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;. When this value is &lt;code&gt;true&lt;/code&gt;, the "real" content gets rendered.&lt;/p&gt;

&lt;p&gt;The difference from our earlier solution: &lt;strong&gt;&lt;code&gt;useEffect&lt;/code&gt; only fires after the component has mounted.&lt;/strong&gt; When the React app adopts the DOM during rehydration, &lt;code&gt;useEffect&lt;/code&gt; hasn't been called yet, and so we're meeting React's expectation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- The initial HTML
     generated at compile-time --&amp;gt;&lt;/span&gt;
    ​
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Your Site&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- What React expects
     after rehydration --&amp;gt;&lt;/span&gt;
    ​
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Your Site&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Immediately after this comparison, we trigger a re-render, and this allows React to do a proper reconciliation. It'll notice that there's some new content to render here—either an authenticated menu, or a login link—and update the DOM accordingly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Two-pass rendering
&lt;/h1&gt;

&lt;p&gt;Have you ever noticed that the expiration date on cereal clearly wasn't printed at the same time as the rest of the box? It's stamped on, after the fact:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UkXWro8R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://joshwcomeau.com/static/cereal-109bb1437a6637e90c97537784f233bf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UkXWro8R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://joshwcomeau.com/static/cereal-109bb1437a6637e90c97537784f233bf.png" alt="Two cereal boxes—Cheerios and Lucky Charms—showing how the expiration date is stamped imprecisely onto a large blue rectangle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a logic to this: cereal-box printing is a two-step process. First, all of the "universal" stuff is printed: the logo, the cartoon leprechaun, the enlarged-to-show-texture photograph, the random pics of smart-watches. Because these things are static, they can be mass-produced, printed millions at a time, months in advance.&lt;/p&gt;

&lt;p&gt;They can't do that with expiration dates, though. At that moment in time, the manufacturers have no idea what the expiration date should be; the cereal that will fill those boxes probably doesn't even exist yet! So they print an empty blue rectangle instead. Much later, after cereal has been produced and injected into the box, they can stamp on a white expiration date and pack it up for shipment.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Two-pass rendering&lt;/em&gt; is the same idea. The first pass, at compile-time, produces all of the static non-personal content, and leaves holes where the dynamic content will go. Then, after the React app has mounted on the user's device, a second pass stamps in all the dynamic bits that depend on client state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance implications
&lt;/h2&gt;

&lt;p&gt;The downside to two-pass rendering is that it can delay time-to-interactive. Forcing a render right after mount is generally frowned upon.&lt;/p&gt;

&lt;p&gt;That said, for most applications, this shouldn't make a big difference. Usually the amount of dynamic content is relatively small, and can be quickly reconciled. If huge chunks of your app are dynamic, you'll miss out on many of the benefits of pre-rendering, but this is unavoidable; dynamic sections can't be produced ahead of time by definition.&lt;/p&gt;

&lt;p&gt;As always, it's best to do some experimentation of your own if you have concerns around performance.&lt;/p&gt;

&lt;h1&gt;
  
  
  Abstractions
&lt;/h1&gt;

&lt;p&gt;On this blog, I wound up needing to defer a handful of rendering decisions to the second pass, and I was sick of writing the same logic over and over again. I created a &lt;code&gt;&amp;lt;ClientOnly&amp;gt;&lt;/code&gt; component to abstract it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ClientOnly&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;delegated&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hasMounted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHasMounted&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setHasMounted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasMounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;delegated&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then you can wrap it around whichever elements you want to defer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ClientOnly&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Navigation&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ClientOnly&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We could also use a custom hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useHasMounted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hasMounted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHasMounted&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setHasMounted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hasMounted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasMounted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useHasMounted&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasMounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthenticatedNav&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this trick up my sleeve, I was able to solve my rendering issue. The day was saved!&lt;/p&gt;

&lt;h1&gt;
  
  
  Mental models
&lt;/h1&gt;

&lt;p&gt;While neat, the abstractions aren't the most important part of this tutorial. The critical bit is the mental model.&lt;/p&gt;

&lt;p&gt;When working in Gatsby apps, I've found it really helpful to think in terms of a two-pass render. The first pass happens at compile-time, &lt;em&gt;wayyy&lt;/em&gt; ahead of time, and sets the foundation for the page, filling in everything that is universal for all users. Then, much later, a second-pass render will fill in the stateful bits that vary from person to person.&lt;/p&gt;

&lt;p&gt;Gatsby is sometimes called a "static site generator", but that name feels limiting—you can build rich, dynamic web applications with Gatsby! It does require a bit of special consideration, but with the right mental model, just about anything is possible 💫&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>react</category>
      <category>javascript</category>
      <category>ssr</category>
    </item>
    <item>
      <title>Persisting React State in localStorage</title>
      <dc:creator>🌈 Josh</dc:creator>
      <pubDate>Mon, 24 Feb 2020 22:37:16 +0000</pubDate>
      <link>https://dev.to/joshwcomeau/persisting-react-state-in-localstorage-32p5</link>
      <guid>https://dev.to/joshwcomeau/persisting-react-state-in-localstorage-32p5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a cross-post from &lt;a href="https://www.joshwcomeau.com/react/persisting-react-state-in-localstorage"&gt;my personal blog&lt;/a&gt;. View it there for at least 35% more whimsy!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's say we're building a calendar app, like Google Calendar. The app lets you toggle between three different displays: month, week, and day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VLxirGsY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/x3s1j5pammt8itoqqxb7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VLxirGsY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/x3s1j5pammt8itoqqxb7.gif" alt="Quick screen-grab of Google Calendar, toggling between day and week views"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Personally, I always want to see the "Week" view. It gives me everything I need to know about the current day, while also giving me a peek at what's coming up in the next couple of days.&lt;/p&gt;

&lt;p&gt;Thankfully, calendar apps know that users have strong preferences around this kind of thing, and the toggle is &lt;strong&gt;sticky&lt;/strong&gt;. If I switch from “week” to “month” and refresh the page, the “month” view is the new default; it sticks.&lt;/p&gt;

&lt;p&gt;Conversely, it's &lt;em&gt;super annoying&lt;/em&gt; when form controls aren't sticky. For example: every month, I create 4-5 expenses through Expensify. Every single time, I have to swap the default currency from USD to CAD. Why can't it remember that I'm Canadian??&lt;/p&gt;

&lt;p&gt;In this tutorial we'll see how we can create a &lt;strong&gt;custom React hook&lt;/strong&gt; to abstract away the "stickiness", so we get it for free whenever we need it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Show me the code
&lt;/h1&gt;

&lt;p&gt;Here's what our custom hook looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useStickyState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stickyValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stickyValue&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stickyValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What about SSR?&lt;/strong&gt; If your app is server-rendered (with a framework like Next.js or Gatsby), you'll get an error if you try using this hook as-is.&lt;/p&gt;

&lt;p&gt;This is actually a pretty tricky problem, because that first render on the server doesn't have access to your computer's localStorage; it can't possibly know what the initial value should be!&lt;/p&gt;

&lt;p&gt;Dynamic content in a server-rendered app is a complex subject, but fortunately, my very next blog post will shed some light on this! &lt;a href="https://www.joshwcomeau.com/subscribe/"&gt;Join my newsletter&lt;/a&gt; to make sure you don't miss it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If this code isn't clear to you, fear not! The rest of this tutorial explains it in greater detail 💫&lt;/p&gt;

&lt;h2&gt;
  
  
  In practice
&lt;/h2&gt;

&lt;p&gt;This hook makes a single assumption, which is reasonably safe in React apps: the value powering a form input is held in React state.&lt;/p&gt;

&lt;p&gt;Here's a non-sticky implementation of a form control to switch between values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CalendarView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Day&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"week"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Week&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"month"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Month&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Calendar stuff here */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can use our new "sticky" variant by swapping out the hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CalendarView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useStickyState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calendar-view&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Everything else unchanged&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;While the &lt;code&gt;useState&lt;/code&gt; hook only takes 1 argument—the initial value—our &lt;code&gt;useStickyState&lt;/code&gt; hook takes two arguments. The second argument is the key that will be used to get and set the value persisted in localStorage. The label you give it has to be unique, but it otherwise doesn't matter what it is.&lt;/p&gt;

&lt;h1&gt;
  
  
  How it works
&lt;/h1&gt;

&lt;p&gt;Fundamentally, this hook is a wrapper around &lt;code&gt;useState&lt;/code&gt;. It just does some other stuff too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lazy initialization
&lt;/h2&gt;

&lt;p&gt;First, it takes advantage of &lt;a href="https://reactjs.org/docs/hooks-reference.html#lazy-initial-state"&gt;lazy initialization&lt;/a&gt;. This lets us pass a function to &lt;code&gt;useState&lt;/code&gt; instead of a value, and that function will only be executed the first time the component renders, when the state is created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stickyValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stickyValue&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stickyValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In our case, we're using it to check for the value in localStorage. If the value exists, we'll use that as our initial value. Otherwise, we'll use the default value passed to the hook ("day", in our earlier example).&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping localStorage in sync
&lt;/h2&gt;

&lt;p&gt;The final step to this is to make sure that we update localStorage whenever the state value changes. For that, our trusty friend &lt;code&gt;useEffect&lt;/code&gt; comes in handy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If the state value changes rapidly (like, many times a second), you may wish to throttle or debounce the updates to localStorage. Because localStorage is a synchronous API, it can cause performance problems if it's done too rapidly.&lt;/p&gt;

&lt;p&gt;Don't take this an excuse to prematurely optimize, though! The profiler will show you whether or not your updates need to be throttled.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Wrapping up
&lt;/h1&gt;

&lt;p&gt;This hook is a small but powerful example of how custom hooks let us invent our own APIs for things. While &lt;a href="https://www.npmjs.com/package/use-persisted-state"&gt;packages&lt;/a&gt; &lt;a href="https://github.com/kripod/react-hooks"&gt;exist&lt;/a&gt; that solve this problem for us, I think there's a lot of value in seeing how to solve these problems ourselves 🧙🏻‍♂️&lt;/p&gt;

&lt;p&gt;Special thanks to Satyajit Sahoo for a couple refactor suggestions 🌠&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
