<?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[ mdx - 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[ mdx - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 25 Aug 2026 04:22:03 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/mdx/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build Your Own Blog with Next.js and MDX ]]>
                </title>
                <description>
                    <![CDATA[ By Caleb Olojo When I decided to build my blog, I found many tools out there that were readily available. I looked at Gastby along with content management systems like Ghost, Contentful, Sanity dot io, and HUGO.  But I needed something that I could h... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-your-own-blog-with-next-js-and-mdx/</link>
                <guid isPermaLink="false">66d45ddbc7632f8bfbf1e3ed</guid>
                
                    <category>
                        <![CDATA[ blog ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mdx ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 21 Apr 2022 02:28:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/04/thoughts.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Caleb Olojo</p>
<p>When I decided to build my blog, I found many tools out there that were readily available. I looked at <a target="_blank" href="https://www.gatsbyjs.com/">Gastby</a> along with content management systems like <a target="_blank" href="https://ghost.org/">Ghost</a>, <a target="_blank" href="https://www.contentful.com/">Contentful</a>, <a target="_blank" href="https://www.sanity.io/">Sanity dot io</a>, and <a target="_blank" href="https://gohugo.io/">HUGO</a>. </p>
<p>But I needed something that I could have total control over. I've also always been someone who loves the flexibility of writing own my custom code. When I do this, I can conveniently go back to where any issues might be when a problem arises.</p>
<p>Gatsby provides this flexibility, and it is something I could get familiar with pretty easily since it's built on a library I use every day (React.js). But, I found out that I can do the exact same thing with <a target="_blank" href="https://nextjs.org">Next.js</a> by integrating <a target="_blank" href="https://mdxjs.com/">MDX</a>. </p>
<p><em>"What is MDX?"</em> You might ask me.</p>
<p>Well... MDX is more or less like the markdown files we always see in GitHub repositories. MDX brings this flexibility into a markdown file by allowing you to literally write or import JavaScript (React) components into your articles. This in turn saves you from writing repetitive code.</p>
<p>In this article, I am going to show you how I built my blog with these tools, so you can also try building something similar. You'll like this simple stack if you are a person who loves the flexibility that this approach brings. </p>
<p>So, sit tight, and let's get started.</p>
<h2 id="heading-how-to-start-building-my-trial-and-error">How to Start Building – My Trial and Error</h2>
<p>To build a blog with Next.js and MDX, there are four popular options that you can choose from.</p>
<p>They are:</p>
<ul>
<li><a target="_blank" href="https://www.npmjs.com/package/@next/mdx">@next/mdx</a>, which is the official tool built by the Next.js team</li>
<li>Kent C. Dodds' <a target="_blank" href="https://github.com/kentcdodds/mdx-bundler">mdx-bundler</a></li>
<li><a target="_blank" href="https://github.com/hashicorp/next-mdx-remote">next-mdx-remote</a>, which is a tool built by the Hashicorp team</li>
<li><a target="_blank" href="https://github.com/hashicorp/next-mdx-enhanced">next-mdx-enhanced</a>, which is a tool also built by Hashicorp (I honestly don't know why they decided to build two)</li>
</ul>
<p>At first, I started by using Kent's mdx-bundler, but then I ran into a lot of problems with the tool. It is a library that is based on the new ECMAScript standards that allow us to create ESModules in the browser, and I was using a very old version of Next.js (V10.1.3, my bad honestly, I didn't know any better).</p>
<p>I did a lot of downgrading and upgrading of Next.js to fix this problem to no avail. There was a certain error that stuck with me, and refused to go away for days. <em>Yes, for days!</em> I felt like crying during that period. Take a look at the error below:</p>
<blockquote>
<p>module not found: can't resolve 'builtin-modules'</p>
</blockquote>
<p>Apparently, for mdx-bundler to work, it needs another npm package called esbuild to do the necessary compiling processes that work under the hood.</p>
<pre><code class="lang-bash">npm i mdx-bundler esbuild
</code></pre>
<p>Luckily for me — at least I thought I was lucky — <a target="_blank" href="https://github.com/kentcdodds/mdx-bundler/issues/18">Cody Brunner submitted an issue about this particular error</a>. Going through the discussions on the issue, a lot of possible fixes were suggested, some of them were related to Webpack, modifying your <code>next.config.js</code> file, and whatnot.</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">future</span>: {
    <span class="hljs-comment">// Opt-in to webpack@5</span>
    <span class="hljs-attr">webpack5</span>: <span class="hljs-literal">true</span>,
  },
  <span class="hljs-attr">reactStrictMode</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">webpack</span>: <span class="hljs-function">(<span class="hljs-params">config, { buildId, dev, isServer, defaultLoaders, webpack }</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (!isServer) {
      <span class="hljs-comment">// https://github.com/vercel/next.js/issues/7755</span>
      config.resolve = {
        ...config.resolve,
        <span class="hljs-attr">fallback</span>: {
          ...config.resolve.fallback,
          <span class="hljs-attr">child_process</span>: <span class="hljs-literal">false</span>,
          <span class="hljs-attr">fs</span>: <span class="hljs-literal">false</span>,
          <span class="hljs-string">'builtin-modules'</span>: <span class="hljs-literal">false</span>,
          <span class="hljs-attr">worker_threads</span>: <span class="hljs-literal">false</span>,
        },
      }
    }

    <span class="hljs-keyword">return</span> config
  },
}
</code></pre>
<p>In the snippet above, it shows that Webpack5 was still a feature that was in progress for Next.js – hence the snippet below in the config:</p>
<pre><code class="lang-js">future: {
  <span class="hljs-attr">webpack5</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<p>But, now the latest version of Next.js supports Webpack5 by default, so there's no need to add that object — if it works for you — in the config. </p>
<p>After going through the discussions, I found a comment (by Kent) that says running <code>npm update</code> would fix the issue, and it did work for Cody Brunner. But not for me apparently.</p>
<p>When I couldn't find a possible fix to this error, I decided to use next-mdx-remote, and the only issue I faced was the breaking change that was added to the tool. Before version 3 of next-mdx-remote you would normally render parsed markdown content by doing the following:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> renderToString <span class="hljs-keyword">from</span> <span class="hljs-string">'next-mdx-remote/render-to-string'</span>
<span class="hljs-keyword">import</span> hydrate <span class="hljs-keyword">from</span> <span class="hljs-string">'next-mdx-remote/hydrate'</span>
<span class="hljs-keyword">import</span> Test <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/test'</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">TestPage</span>(<span class="hljs-params">{ source }</span>) </span>{
  <span class="hljs-keyword">const</span> content = hydrate(source, { components })

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"content"</span>&gt;</span>{content}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStaticProps</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// MDX text - can be from a local file, database, anywhere</span>
  <span class="hljs-keyword">const</span> source = <span class="hljs-string">'Some **mdx** text, with a component &lt;Test /&gt;'</span>
  <span class="hljs-keyword">const</span> mdxSource = <span class="hljs-keyword">await</span> renderToString(source, { components })

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">props</span>: {
      <span class="hljs-attr">source</span>: mdxSource,
    },
  }
}
</code></pre>
<p>The breaking change that was added in version 3 of the package stripped off a lot of internal code that was perceived to cause poor experiences for people who were using it at that time.</p>
<p>The team went on to announce the reason behind this change and the major changes. Take a look at them below.</p>
<blockquote>
<p>This release includes a full rewrite of the internals of next-mdx-remote to make it faster, lighter-weight, and behave more predictably! The migration should be fairly quick for most use-cases, but it will require some manual changes. Thanks to our community for testing out this release and providing early feedback. heart.</p>
</blockquote>
<h4 id="heading-major-changes-to-next-mdx-remote">Major changes to next-mdx-remote:</h4>
<ul>
<li><code>renderToString</code> has been replaced with <code>serialize</code></li>
<li><code>hydrate</code> has been replaced with <code>&lt;MDXRemote /&gt;</code></li>
<li>Removed provider configuration. React context usage should now work<br>without additional effort.</li>
<li>Content will now hydrate immediately by default</li>
<li>Dropped support for IE11 by default</li>
</ul>
<p>With this new change, the previous implementation will now become:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { serialize } <span class="hljs-keyword">from</span> <span class="hljs-string">'next-mdx-remote/serialize'</span>
<span class="hljs-keyword">import</span> { MDXRemote } <span class="hljs-keyword">from</span> <span class="hljs-string">'next-mdx-remote'</span>

<span class="hljs-keyword">import</span> { Test, Image, CodeBlock } <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/'</span>

<span class="hljs-keyword">const</span> components = { Test }

<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">TestPage</span>(<span class="hljs-params">{ source }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"content"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">MDXRemote</span> {<span class="hljs-attr">...source</span>} <span class="hljs-attr">components</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">Test</span>, <span class="hljs-attr">Image</span>, <span class="hljs-attr">CodeBlock</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">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStaticProps</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// MDX text - can be from a local file, database, anywhere</span>
  <span class="hljs-keyword">const</span> source = <span class="hljs-string">'Some **mdx** text, with a component &lt;Test /&gt;'</span>
  <span class="hljs-keyword">const</span> mdxSource = <span class="hljs-keyword">await</span> serialize(source)

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">props</span>: {
      <span class="hljs-attr">source</span>: mdxSource,
    },
  }
}
</code></pre>
<h2 id="heading-how-to-build-the-blog">How to Build the Blog</h2>
<p>In the previous section, I walked you through some of the issues I encountered while I was choosing a suitable tool to use. </p>
<p>In this section, we're going to cover how you can build a similar blog like mine.</p>
<p>We'll start by creating a Next.js app with the command below:</p>
<pre><code class="lang-bash">npx create-next-app blog
</code></pre>
<p>The command above will give you a boilerplate of a typical Next.js app. For the sake of brevity, I'll be focusing more on the <code>pages</code> and <code>src/utils</code> folders of this app.</p>
<pre><code class="lang-bash">|--pages
|   |-- blog
|   |   |-- index.js
|   |   |-- [slug].js
|   |-- _app.js
|   |-- index.js
|--src
|   |-- utils
|        |-- mdx.js
|--data
|   |-- articles
|        |-- example-post.mdx
|        |-- example-post2.mdx
</code></pre>
<p>In a typical blog, we'd need to write blog posts or articles. In this blog, we're using markdown (MDX) to write our articles, which is why you can see that we have two <code>.mdx</code> files inside the <code>data/articles</code> directory. You can have more than that, as far as the number of articles you want to write goes.</p>
<h2 id="heading-how-to-read-the-markdown-mdx-files">How to Read the Markdown (MDX) Files</h2>
<p>In this section, we're going to start by writing some reusable functions inside <code>src/utils/mdx.js</code>. </p>
<p>The functions we're writing here will be using Node.js' FileSystem API. We'll be calling the functions at the server-side in the pages folder because Next.js has some <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching/overview">data-fetching methods</a> that runs on the server.</p>
<p>Let's start by installing the dependencies that we need for now. As we progress, we'll be adding other dependencies:</p>
<pre><code class="lang-bash">npm install gray-matter reading-time next-mdx-remote glob dayjs
</code></pre>
<p>The command above will get all packages listed above as dependencies in our blog project.</p>
<p><code>gray-matter</code> will parse the content in the <code>.mdx</code> files to readable HTML content.</p>
<p><code>reading-time</code> assigns an approximate time to read a blog post or article based on the word count.</p>
<p><code>next-mdx-remote</code> does the background compilation of the MDX files by allowing them to be loaded within Next.js' <code>getStaticProps</code> or <code>getServerSideProps</code> data-fetching method, and hydrated properly on the client.</p>
<p><code>glob</code> gives us access to match the file patterns in <code>data/articles</code>, which we'll be using as the slug of the article.</p>
<p><code>dayjs</code> is a JavaScript library that helps to parse, manipulate, validate, and display dates that we would be adding to the metadata of each article.</p>
<p>We've seen the basic functions of the packages we installed. Now let's start writing the functions that'll read the files in the articles directory.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> path <span class="hljs-keyword">from</span> <span class="hljs-string">'path'</span>
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">'fs'</span>
<span class="hljs-keyword">import</span> matter <span class="hljs-keyword">from</span> <span class="hljs-string">'gray-matter'</span>
<span class="hljs-keyword">import</span> readingTime <span class="hljs-keyword">from</span> <span class="hljs-string">'reading-time'</span>
<span class="hljs-keyword">import</span> { sync } <span class="hljs-keyword">from</span> <span class="hljs-string">'glob'</span>

<span class="hljs-keyword">const</span> articlesPath = path.join(process.cwd(), <span class="hljs-string">'data/articles'</span>)

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSlug</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> paths = sync(<span class="hljs-string">`<span class="hljs-subst">${articlesPath}</span>/*.mdx`</span>)

  <span class="hljs-keyword">return</span> paths.map(<span class="hljs-function">(<span class="hljs-params">path</span>) =&gt;</span> {
    <span class="hljs-comment">// holds the paths to the directory of the article</span>
    <span class="hljs-keyword">const</span> pathContent = path.split(<span class="hljs-string">'/'</span>)
    <span class="hljs-keyword">const</span> fileName = pathContent[pathContent.length - <span class="hljs-number">1</span>]
    <span class="hljs-keyword">const</span> [slug, _extension] = fileName.split(<span class="hljs-string">'.'</span>)

    <span class="hljs-keyword">return</span> slug
  })
}
</code></pre>
<p>In the snippet above, we've imported the Node.js FileSystem from its module and the other packages. The first variable declaration, <code>articlesPath</code>, holds the path to where all the articles can be found.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> articlesPath = path.join(process.cwd(), <span class="hljs-string">'data/articles'</span>)
</code></pre>
<p>We're using the <code>path</code> module to get access to where the articles are by tapping into the <code>process</code> API of Node.js which gives us direct access to the <code>cwd()</code> (Current Working Directory) object.</p>
<p>The <code>getSlug</code> function will get a unique article when it's clicked by the user<br>on the blog page. You'll see that we're referencing the <code>articlesPath</code> variable that was declared before, and we're passing it to the <code>sync</code> function of the <code>glob</code> package. This will in turn match any file that has the <code>.mdx</code> extension, and give us an array with a list of these files.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> paths = sync(<span class="hljs-string">`<span class="hljs-subst">${articlesPath}</span>/*.mdx`</span>)
</code></pre>
<p>With that being said, we'll return an array of modified file names. The <code>pathContent</code> variable holds the path to all the articles in the articles directory, so we're using JavaScript to remove all the "forward-slashes" with the <code>split()</code> method of JavaScript.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fileName = pathContent[pathContent.length - <span class="hljs-number">1</span>]
<span class="hljs-keyword">const</span> [slug, _extension] = fileName.split(<span class="hljs-string">'.'</span>)
</code></pre>
<p>The <code>fileName</code> variable declaration gets the last part of path, say for example <code>"/data/articles/example-post.mdx"</code>, since it is an array, and returns the last part which is <code>/example-post.mdx</code>. The next variable goes on to remove the period <code>(.)</code> from the filename itself, so we'll be left with <code>example-post</code> as the slug.</p>
<h2 id="heading-how-to-parse-article-content-from-the-slug">How to Parse Article Content from the Slug</h2>
<p>The next function gets and parses the content in our MDX files from the slugs. It returns an object of metadata that we'll be using as we progress.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getArticleFromSlug</span>(<span class="hljs-params">slug</span>) </span>{
  <span class="hljs-keyword">const</span> articleDir = path.join(articlesPath, <span class="hljs-string">`<span class="hljs-subst">${slug}</span>.mdx`</span>)
  <span class="hljs-keyword">const</span> source = fs.readFileSync(articleDir)
  <span class="hljs-keyword">const</span> { content, data } = matter(source)

  <span class="hljs-keyword">return</span> {
    content,
    <span class="hljs-attr">frontmatter</span>: {
      slug,
      <span class="hljs-attr">excerpt</span>: data.excerpt,
      <span class="hljs-attr">title</span>: data.title,
      <span class="hljs-attr">publishedAt</span>: data.publishedAt,
      <span class="hljs-attr">readingTime</span>: readingTime(source).text,
      ...data,
    },
  }
}
</code></pre>
<p>In the snippet above, we're using Node.js' <code>readFileSync</code> function from the FileSystem API to read the files in the <code>articleDir</code> in a synchronous manner. </p>
<p>What we're doing with this function — <code>readFileSync</code> — is telling Node to stop other processes that are currently going on, and perform this operation for us.</p>
<p>You can learn more about it <a target="_blank" href="https://www.geeksforgeeks.org/node-js-fs-readfilesync-method/">here</a> if you want to.</p>
<p>If you go ahead and <code>console.log(source)</code> in your terminal, you'll get a <code>&lt;Buffer&gt;</code> — which isn't readable — data type in your console. </p>
<p>This is where the <code>gray-matter</code> package comes to save the day. It helps in parsing the markdown content in the source to something — readable HTML — that you and I can understand.</p>
<p>Here, we're destructuring <code>content</code> and <code>data</code> variables, assigning it to the <code>matter</code> package (which parses the source) and returns an object that holds our <code>content</code> and <code>frontmatter: data</code> variables:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { content, data } = matter(source)

<span class="hljs-keyword">return</span> {
  content,
  <span class="hljs-attr">frontmatter</span>: {
    slug,
    <span class="hljs-attr">excerpt</span>: data.excerpt,
    <span class="hljs-attr">title</span>: data.title,
    <span class="hljs-attr">publishedAt</span>: data.publishedAt,
    <span class="hljs-attr">readingTime</span>: readingTime(source).text,
    ...data,
  },
}
</code></pre>
<p>We need a way to display all the articles on the blog page. The function below does that for us, by utilizing the <code>reduce()</code> method of JavaScript to return an array of all the articles in the articles directory.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAllArticles</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> articles = fs.readdirSync(path.join(process.cwd(), <span class="hljs-string">'data/articles'</span>))

  <span class="hljs-keyword">return</span> articles.reduce(<span class="hljs-function">(<span class="hljs-params">allArticles, articleSlug</span>) =&gt;</span> {
    <span class="hljs-comment">// get parsed data from mdx files in the "articles" dir</span>
    <span class="hljs-keyword">const</span> source = fs.readFileSync(
      path.join(process.cwd(), <span class="hljs-string">'data/articles'</span>, articleSlug),
      <span class="hljs-string">'utf-8'</span>
    )
    <span class="hljs-keyword">const</span> { data } = matter(source)

    <span class="hljs-keyword">return</span> [
      {
        ...data,
        <span class="hljs-attr">slug</span>: articleSlug.replace(<span class="hljs-string">'.mdx'</span>, <span class="hljs-string">''</span>),
        <span class="hljs-attr">readingTime</span>: readingTime(source).text,
      },
      ...allArticles,
    ]
  }, [])
}
</code></pre>
<p>You can see how we're using <code>readdirSync()</code> to synchronously read all the files inside <code>data/articles</code>. The <code>source</code> variable can be accessed by reading all the files with their respective slugs and getting their content parsed with the <code>gray-matter</code> package.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> source = fs.readFileSync(
  path.join(process.cwd(), <span class="hljs-string">'data/articles'</span>, articleSlug),
  <span class="hljs-string">'utf-8'</span>
)
<span class="hljs-keyword">const</span> { data } = matter(source)
</code></pre>
<p>If you take a look at the snippet below, you'll see how we're using the <code>reading-time</code> package to get the approximate time it will take to read this article. We get the slug that will be attached to this article by stripping the last part of the article — <code>blog/example-post.mdx</code> — and replacing it with an empty string. This makes it accessible via "blog/example-post".</p>
<pre><code class="lang-js">{
  <span class="hljs-attr">slug</span>: articleSlug.replace(<span class="hljs-string">'.mdx'</span>, <span class="hljs-string">''</span>),
  <span class="hljs-attr">readingTime</span>: readingTime(source).text,
}
</code></pre>
<p>The <code>readingTime</code> has some methods that you can assign to it, one of them is the <code>text</code> method. You can try removing this value, saving your code, and allowing Next.js to throw an error, so you can get a glimpse of the values that you can use.</p>
<h2 id="heading-how-to-display-a-list-of-articles">How to Display a List of Articles</h2>
<p>In the previous sections, we have seen how we can use the Node.js FileSystem API and a couple of other tools to get access to where all our articles are. </p>
<p>In this section, we'll be displaying the articles on a webpage.</p>
<p>We'll start with the <code>index</code> file in the blog folder. In this file, we'll be using the data-fetching method — <code>getStaticProps</code> — to render the articles on the page.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { getAllArticles } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../src/utils/mdx'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStaticProps</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> articles = <span class="hljs-keyword">await</span> getAllArticles()

  articles
    .map(<span class="hljs-function">(<span class="hljs-params">article</span>) =&gt;</span> article.data)
    .sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (a.data.publishedAt &gt; b.data.publishedAt) <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
      <span class="hljs-keyword">if</span> (a.data.publishedAt &lt; b.data.publishedAt) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>

      <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
    })

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">props</span>: {
      <span class="hljs-attr">posts</span>: articles.reverse(),
    },
  }
}
</code></pre>
<p>In the snippet above, we imported the <code>getAllArticles</code> function and used it in the data-fetching method of Next.js. </p>
<p>You'll notice how we're sorting the articles based on the date they were published. We'll eventually map the list of articles that will be returned as props to the index (blog) page.</p>
<pre><code class="lang-js">articles
  .map(<span class="hljs-function">(<span class="hljs-params">article</span>) =&gt;</span> article.data)
  .sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (a.data.publishedAt &gt; b.data.publishedAt) <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
    <span class="hljs-keyword">if</span> (a.data.publishedAt &lt; b.data.publishedAt) <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
  })
</code></pre>
<p>Lest I forget, this is how the content of your typical article file will look in markdown syntax below:</p>
<pre><code class="lang-md">---
title: 'Next.js Image optimization error on Netlify'
publishedAt: '2022-04-16'
excerpt: 'Next.js has a built-in Image component that comes with a lot of performance optimization features when you are using it.'
<span class="hljs-section">cover<span class="hljs-emphasis">_image: 'path/to/where/image/is/stored'
---

rest of the content falls here</span></span>
</code></pre>
<p>You may ask me, <em>"why do we need to sort the articles by date if we can just use the <code>reverse()</code> method to re-order the array of articles?"</em>. </p>
<p>I think it is appropriate for us to sort the list of articles by comparing them with the date they were published and still apply the <code>reverse</code> method to the array.</p>
<p>Say, for example, we forget to add the published dates to the articles. Then the <code>reverse()</code> method will just perform the operation on the array without comparing the dates in a LIFO — Last-In-First-Out — pattern, if the sort function is missing. So it is better to sort the articles and still reverse the content of the array.</p>
<p>Now that we've returned the list of articles as props we can go ahead to map them onto the page.</p>
<pre><code class="lang-jsx"><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> Head <span class="hljs-keyword">from</span> <span class="hljs-string">'next/head'</span>
<span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</span>
<span class="hljs-keyword">import</span> { getAllArticles } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../src/utils/mdx'</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">BlogPage</span>(<span class="hljs-params">{ posts }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.Fragment</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Blog<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Head</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        {posts.map((frontMatter) =&gt; {
          return (
            <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">{</span>`/<span class="hljs-attr">blog</span>/${<span class="hljs-attr">frontMatter.slug</span>}`} <span class="hljs-attr">passHref</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">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title"</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> <span class="hljs-attr">className</span>=<span class="hljs-string">"summary"</span>&gt;</span>{frontMatter.excerpt}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"date"</span>&gt;</span>
                  {dayjs(frontMatter.publishedAt).format('MMMM D, YYYY')} <span class="hljs-symbol">&amp;mdash;</span>{' '}
                  {frontMatter.readingTime}
                <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">Link</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">React.Fragment</span>&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStaticProps</span>(<span class="hljs-params"></span>) </span>{
  ...
}
</code></pre>
<p>In the snippet above, we're using the <code>Link</code> component to route the user to a dynamic page with the unique article's slug. This is the reason we created a file called <code>[slug].js</code>, if you can recall. It is a dynamic route, and you can read more about it <a target="_blank" href="https://nextjs.org/docs/routing/dynamic-routes">here</a>.</p>
<h2 id="heading-how-to-display-a-unique-article">How to Display a Unique Article</h2>
<p>In the last section, we were able to render the list of articles onto the webpage. In this section, we'll be rendering a unique article that gets clicked on by the user in a new route.</p>
<p>We're also going to be using a tool called rehype to customize what our blog post will look like. Rehype is an HTML pre-processor that is powered by plugins. We'll be using some of these plugins in this section, so let's install them now.</p>
<pre><code class="lang-bash">npm i rehype-highlight rehype-autolink-headings rehype-code-titles rehype-slug
</code></pre>
<p><code>rehype-highlight</code> allows us to add syntax highlighting to our code blocks.</p>
<p><code>rehype-autolink-headings</code> is a plugin that adds links to headings from h1 to h6.</p>
<p><code>rehype-code-titles</code> adds language/file titles to your code.</p>
<p><code>rehype-slug</code> is a plugin that adds an <code>id</code> attributes to headings.</p>
<p>Now that we've seen the roles that each plugin carries out, let's start working on the <code>[slug].js</code> file. In this file, we'll be using two data-fetching methods of Next.js — <code>getStaticProps</code> and <code>getStaticPaths</code>.</p>
<p>We're using these two methods because we'll be fetching data (articles) that are unique to the path (slugs) that the user is redirected to.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// dynamically generate the slugs for each article(s)</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStaticPaths</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// getting all paths of each article as an array of</span>
  <span class="hljs-comment">// objects with their unique slugs</span>
  <span class="hljs-keyword">const</span> paths = (<span class="hljs-keyword">await</span> getSlug()).map(<span class="hljs-function">(<span class="hljs-params">slug</span>) =&gt;</span> ({ <span class="hljs-attr">params</span>: { slug } }))

  <span class="hljs-keyword">return</span> {
    paths,
    <span class="hljs-comment">// in situations where you try to access a path</span>
    <span class="hljs-comment">// that does not exist. it'll return a 404 page</span>
    <span class="hljs-attr">fallback</span>: <span class="hljs-literal">false</span>,
  }
}
</code></pre>
<p>When you take a look at the snippet above, you'll see that we're obtaining the list of <code>paths</code> from the articles, and mapping that list of items (paths) to an array. This can be accessed with the <code>params</code> variable in the <code>getStaticProps</code> data-fetching method.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { getArticleFromSlug } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../src/utils/mdx"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStaticProps</span>(<span class="hljs-params">{ params }</span>) </span>{
  <span class="hljs-comment">//fetch the particular file based on the slug</span>
  <span class="hljs-keyword">const</span> { slug } = params
  <span class="hljs-keyword">const</span> { content, frontmatter } = <span class="hljs-keyword">await</span> getArticleFromSlug(slug)

  <span class="hljs-keyword">const</span> mdxSource = <span class="hljs-keyword">await</span> serialize(content, {
    <span class="hljs-attr">mdxOptions</span>: {
      <span class="hljs-attr">rehypePlugins</span>: [
        rehypeSlug,
        [
          rehypeAutolinkHeadings,
          {
            <span class="hljs-attr">properties</span>: { <span class="hljs-attr">className</span>: [<span class="hljs-string">'anchor'</span>] },
          },
          { <span class="hljs-attr">behaviour</span>: <span class="hljs-string">'wrap'</span> },
        ],
        rehypeHighlight,
        rehypeCodeTitles,
      ],
    },
  })

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">props</span>: {
      <span class="hljs-attr">post</span>: {
        <span class="hljs-attr">source</span>: mdxSource,
        frontmatter,
      },
    },
  }
}<span class="hljs-string">`</span>
</code></pre>
<p>In the snippet above, we're destructuring <code>content</code> and <code>frontmatter</code> — which is the metadata of the article — and assigning it to the <code>getArticleFromSlug</code> function which receives the slug of the article as an argument.</p>
<p>We continued by serializing the content of the article with next-mdx-remote's <code>serialize()</code> function, and pass the necessary rehype plugins in the <code>mdxOptions</code> object:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> mdxSource = <span class="hljs-keyword">await</span> serialize(content, {
  <span class="hljs-attr">mdxOptions</span>: {
    <span class="hljs-attr">rehypePlugins</span>: [
      rehypeSlug,
      [
        rehypeAutolinkHeadings,
        {
          <span class="hljs-attr">properties</span>: { <span class="hljs-attr">className</span>: [<span class="hljs-string">'anchor'</span>] },
        },
        { <span class="hljs-attr">behaviour</span>: <span class="hljs-string">'wrap'</span> },
      ],
      rehypeHighlight,
      rehypeCodeTitles,
    ],
  },
})
</code></pre>
<p>To wrap it up, we return the <code>content</code> of the article and the <code>frontmatter</code> as props that'll be accessed by the slug component.</p>
<pre><code class="lang-js"><span class="hljs-keyword">return</span> {
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">post</span>: {
      <span class="hljs-attr">source</span>: mdxSource,
      frontmatter,
    },
  },
}
</code></pre>
<p>The props that we returned in the previous snippets can be accessed via the component below. </p>
<p>You'll notice that the <code>&lt;MDXRemote /&gt;</code> component receives the <code>{...source}</code> and custom React component props that we can use in our MDX files. This eradicates the process of having to write repetitive code over and over.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> dayjs <span class="hljs-keyword">from</span> <span class="hljs-string">'dayjs'</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> Head <span class="hljs-keyword">from</span> <span class="hljs-string">'next/head'</span>
<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">'next/image'</span>
<span class="hljs-keyword">import</span> rehypeSlug <span class="hljs-keyword">from</span> <span class="hljs-string">'rehype-slug'</span>
<span class="hljs-keyword">import</span> { MDXRemote } <span class="hljs-keyword">from</span> <span class="hljs-string">'next-mdx-remote'</span>
<span class="hljs-keyword">import</span> rehypeHighlight <span class="hljs-keyword">from</span> <span class="hljs-string">'rehype-highlight'</span>
<span class="hljs-keyword">import</span> rehypeCodeTitles <span class="hljs-keyword">from</span> <span class="hljs-string">'rehype-code-titles'</span>
<span class="hljs-keyword">import</span> { serialize } <span class="hljs-keyword">from</span> <span class="hljs-string">'next-mdx-remote/serialize'</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'highlight.js/styles/atom-one-dark-reasonable.css'</span>
<span class="hljs-keyword">import</span> rehypeAutolinkHeadings <span class="hljs-keyword">from</span> <span class="hljs-string">'rehype-autolink-headings'</span>
<span class="hljs-keyword">import</span> { getSlug, getArticleFromSlug } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../src/utils/mdx'</span>
<span class="hljs-keyword">import</span> { SectionTitle, Text } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../data/components/mdx-components'</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">Blog</span>(<span class="hljs-params">{ post: { source, frontmatter } }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.Fragment</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>{frontmatter.title} | My blog<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Head</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"article-container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"article-title"</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> <span class="hljs-attr">className</span>=<span class="hljs-string">"publish-date"</span>&gt;</span>
          {dayjs(frontmatter.publishedAt).format('MMMM D, YYYY')} <span class="hljs-symbol">&amp;mdash;</span>{' '}
          {frontmatter.readingTime}
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"content"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">MDXRemote</span> {<span class="hljs-attr">...source</span>} <span class="hljs-attr">components</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">Image</span>, <span class="hljs-attr">SectionTitle</span>, <span class="hljs-attr">Text</span> }} /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">React.Fragment</span>&gt;</span></span>
  )
}
</code></pre>
<p>In the snippet above, you'll notice how we destructured the post props into <code>{ source, frontmatter }</code>. So instead of doing this, in the <code>&lt;MDXRemote&gt;</code> component below we can just spread the source variable directly as a prop.</p>
<pre><code class="lang-jsx">&lt;MDXRemote {...post.source} /&gt;
</code></pre>
<p>Notice how we're also dynamically rendering the title of the page with the title of the article instead of the normal title? This is gotten from the frontmatter.</p>
<pre><code class="lang-jsx">&lt;Head&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>{frontmatter.title} | My blog<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span></span>
&lt;/Head&gt;
</code></pre>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Every developer loves having their fancy themes applied to their editors. So we're not going to leave this out in this blog. </p>
<p>I'm currently using the <code>"atom-one-dark-reasonable"</code> theme for my syntax highlighting. You can import it from the <code>"highlight.js"</code> library — since the <code>rehype-highlight</code> plugin uses it under the hood — like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">'highlight.js/styles/atom-one-dark-reasonable.css'</span>
</code></pre>
<p>There are a lot of other themes <a target="_blank" href="https://highlightjs.org/static/demo/">here</a>, so you can choose any that you're comfortable with.</p>
<p>You might have noticed while reading this article that there are some components like the one in the image below – and you may have been wondering how it was created.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/tooltip-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can decide to have a lot of custom MDX components that you can use in your articles. But, I decided to target any element that I want to style in this article by assigning a generic className to it. So whenever I want to use it, I just reference that style in the element.</p>
<p>SEO is one of the important things when it comes to building a blog, and luckily for us, Next.js already has that covered for us. You can take a look at this article that walks you through <a target="_blank" href="https://seven.hashnode.dev/seo-in-nextjs-apps">How to add SEO Meta tags in your Next.js apps</a> and <a target="_blank" href="https://meje.dev/blog/meta-tags-error-in-nextjs">How I fixed a meta tag pre-rendering error in Next.js</a></p>
<p>There is an important thing that you must not forget, and that is the <code>next.config.js</code> file. You need to make sure that it is properly set up so you can avoid one of the version compatibility errors of the latest version of React — v18.0.0 — with next-mdx-remote.</p>
<p>Although the Hashicorp team said they've fixed <a target="_blank" href="https://github.com/hashicorp/next-mdx-remote/pull/250">this</a> in their latest release, it didn't work for me. A way to bypass this error is to install next-mdx-remote as a legacy peer dependency, like so:</p>
<pre><code class="lang-bash">npm i next-mdx-remote --legacy-peer-deps
</code></pre>
<p>And make sure to have a <code>next.config</code> file that looks like what you're seeing below.</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">reactStrictMode</span>: <span class="hljs-literal">true</span>,

  <span class="hljs-attr">images</span>: {
    <span class="hljs-attr">loader</span>: <span class="hljs-string">'akamai'</span>,
    <span class="hljs-attr">path</span>: <span class="hljs-string">''</span>,
  },

  <span class="hljs-attr">webpack</span>: <span class="hljs-function">(<span class="hljs-params">config</span>) =&gt;</span> {
    config.resolve.alias = {
      ...config.resolve.alias,
      <span class="hljs-string">'react/jsx-runtime.js'</span>: <span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'react/jsx-runtime'</span>),
    }

    config.resolve = {
      ...config.resolve,

      <span class="hljs-attr">fallback</span>: {
        ...config.resolve.fallback,
        <span class="hljs-attr">child_process</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">fs</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-comment">// 'builtin-modules': false,</span>
        <span class="hljs-comment">// worker_threads: false,</span>
      },
    }

    <span class="hljs-keyword">return</span> config
  },
}
</code></pre>
<p>The <code>resolve.alias</code> object in the config above helps as a workaround in fixing the error below</p>
<h4 id="heading-what-to-do-if-you-get-a-server-error">What to do if you get a server error</h4>
<p>Error: Package subpath "./jsx-runtime.js" is not defined by "exports" in "path-to-node_modules/react/package.json"</p>
<p>Sometimes you may also encounter an error that has to do with the "builtin" modules of Node.js while you're deploying your project. The <code>config.resolve</code> object with the <code>fallback</code> key helps in removing that error.</p>
<p>You'll notice that there's an <code>image</code> object in the config.</p>
<pre><code class="lang-js">  images: {
    <span class="hljs-attr">loader</span>: <span class="hljs-string">'akamai'</span>,
    <span class="hljs-attr">path</span>: <span class="hljs-string">''</span>,
  },
</code></pre>
<p>Its role is to ensure that the proper image optimization process is used during the build process. You can take a look at an article I wrote about how you can fix the <a target="_blank" href="https://meje.dev/blog/image-optimization-error-in-nextjs">Next.js image optimization error on Netlify</a></p>
<p>Thank you so much for reading this article. I hope you found it helpful.</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[ Convert the Gatsby default starter blog to use MDX ]]>
                </title>
                <description>
                    <![CDATA[ By Scott Spence In this guide we're going to cover converting the Gatsby default blog starter to use MDX. All the cool kids are using Gatsby and MDX in their blogs these days. If you already have a blog that uses Gatsby but want to move onto the new ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/convert-the-gatsby-default-starter-blog-to-use-mdx/</link>
                <guid isPermaLink="false">66d8521dc1d58e16fbe2b419</guid>
                
                    <category>
                        <![CDATA[ beginners guide ]]>
                    </category>
                
                    <category>
                        <![CDATA[ blog ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mdx ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 09 Apr 2019 17:18:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/06/cover-6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Scott Spence</p>
<p>In this guide we're going to cover converting the Gatsby default blog starter to use MDX.</p>
<p>All the cool kids are using Gatsby and MDX in their blogs these days. If you already have a blog that uses Gatsby but want to move onto the new hotness then this is the guide for you.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/gck4RjaX5D4" 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-versions">Versions:</h2>
<p><strong>This guide is being used with the following dependency versions.</strong></p>
<ul>
<li>gatsby: 2.3.5</li>
<li>react: 16.8.6</li>
<li>react-dom: 16.8.6</li>
<li>gatsby-mdx: 0.4.5</li>
<li>@mdx-js/mdx: 0.20.3</li>
<li>@mdx-js/tag: 0.20.3</li>
</ul>
<p>You can also check out the <a target="_blank" href="https://codesandbox.io/s/lqp6p647q">example code</a>.</p>
<hr>
<p>We're going to need some links, which are:</p>
<p><a target="_blank" href="https://codesandbox.io/docs/importing">CodeSandbox docs for importing projects</a></p>
<p><a target="_blank" href="https://codesandbox.io/s/github">CodeSandbox import wizard</a></p>
<p><a target="_blank" href="https://github.com/gatsbyjs/gatsby-starter-blog">Gatsby starter blog</a></p>
<h2 id="heading-import-to-codesandbox">Import to CodeSandbox</h2>
<p>For this example I'm going to be using the <a target="_blank" href="https://github.com/gatsbyjs/gatsby-starter-blog">Gatsby starter blog</a> and importing it into CodeSandbox, looking at the docs it says you can do this with the <a target="_blank" href="https://codesandbox.io/s/github">CodeSandbox import wizard</a> linked, paste the link in there and CodeSandbox will open the representation of the code on GitHub.</p>
<p>Now we can go about moving from using Gatsby transformer remark over to MDX.</p>
<p>Let's take a look at what we'll be changing for this example. But first we're going to need to import some dependencies to get MDX running in out Gatsby project.</p>
<p>With the add dependency button in CodeSandbox add the following dependencies:</p>
<ul>
<li><code>gatsby-mdx</code></li>
<li><code>@mdx-js/mdx</code></li>
<li><code>@mdx-js/tag</code></li>
</ul>
<p>We will also need to add dependencies for styled-components so may as well add them now as well:</p>
<ul>
<li><code>gatsby-plugin-styled-components</code></li>
<li><code>styled-components</code></li>
<li><code>babel-plugin-styled-components</code></li>
</ul>
<p>Files to change:</p>
<ul>
<li><code>gatsby-node.js</code></li>
<li><code>gatsby-config.js</code></li>
<li><code>index.js</code></li>
<li><code>blog-post.js</code></li>
</ul>
<h2 id="heading-gatsby-nodejs"><code>gatsby-node.js</code></h2>
<p>First up we're going to need to change <code>gatsby-node.js</code> this is where all the pages and data nodes are generated.</p>
<p>Change all markdown remark occurrences with MDX, that's the initial GraphQL query in create pages, then again in the result.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/initialGatsbyNode.png" alt="initial gatsby node changes" width="600" height="400" loading="lazy"></p>
<p>Then change the <code>node.internal.type</code> in <code>onCreateNode</code> from <code>MarkdownRemark</code> to <code>Mdx</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/lastGatsbyNode.png" alt="last gatsby node changes" width="600" height="400" loading="lazy"></p>
<h2 id="heading-gatsby-configjs"><code>gatsby-config.js</code></h2>
<p>Here we're going to replace <code>gatsby-transformer-remark</code> with <code>gatsby-mdx</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/gatsbyConfig.png" alt="replace transformer remark with gatsby-mdx" width="600" height="400" loading="lazy"></p>
<h2 id="heading-indexjs"><code>index.js</code></h2>
<p>Here we're going to alter the <code>posts</code> variable to take the <code>Mdx</code> edges.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/indexPageEdges.png" alt="replace all markdown edges" width="600" height="400" loading="lazy"></p>
<p>The <code>Mdx</code> edges are taken from the page query, which is also altered to use <code>allMdx</code> in place of <code>allMarkdownRemark</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/indexPageQuery.png" alt="index page query" width="600" height="400" loading="lazy"></p>
<h2 id="heading-blog-postjs"><code>blog-post.js</code></h2>
<p>Now last on the list to get MDX working is the blog post template, we're going to need to import <code>MDXRenderer</code> from <code>gatsby-mdx</code> we're going to replace <code>dangerouslySetInnerHTML</code> with this shortly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/importMdxRenderer.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here's where we use it, we'll come onto <code>post.code.body</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/replaceDangerHtml.png" alt="replace dangerously set html" width="600" height="400" loading="lazy"></p>
<p>Again in the query we're replacing <code>markdownRemark</code> with <code>mdx</code> and this time also doing away with <code>html</code> from the query and adding in <code>code</code> for <code>body</code> which we're using in our render method.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/blogPostQuery-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-now-were-using-mdx">Now we're using MDX!</h2>
<p>So now we can create a <code>.mdx</code> post, let's do that.</p>
<p>Import the styled-components dependencies:</p>
<pre><code class="lang-bash">gatsby-plugin-styled-components
styled-components
babel-plugin-styled-components
</code></pre>
<p>Then configure them in <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">`Gatsby Starter Blog`</span>,
    ...
    },
  },
  <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-source-filesystem`</span>,
      <span class="hljs-attr">options</span>: {
  ...
</code></pre>
<p>Now we can use styled-components, in <code>src/components</code> make a new component, I have called my one <code>butt.js</code> call yours what you like.</p>
<p>We're going to use this component in a <code>.mdx</code> document, first the component:</p>
<pre><code class="lang-js"><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> Butt = styled.button<span class="hljs-string">`
  background-color: red;
  height: 40px;
  width: 80px;
`</span>;
</code></pre>
<p>Spicy, right! ?</p>
<p>Now we can include this component in a <code>.mdx</code> document so let's go ahead and create that, in <code>content/blog</code> make a new directory, I'm giving mine the imaginative name <code>first-mdx-post</code>, in there create a <code>index.mdx</code> file and use the frontmatter from one of the other posts as an example of what to use:</p>
<pre><code class="lang-mdx">---
title: My First MDX Post!
date: '2019-04-07T23:46:37.121Z'
---

# make a site they said, it'll be fun they said

more content yo!
</code></pre>
<p>This will render a <code>h1</code> and a <code>p</code> and we should see it render in our CodeSandbox preview.</p>
<p>Now we can go ahead and import our beautifully crafted button:</p>
<pre><code class="lang-mdx">---
title: My First MDX Post!
date: '2019-04-07T23:46:37.121Z'
---

import { Butt } from '../../../src/components/button';

# make a site they said, it'll be fun they said

more content yo!

&lt;Butt&gt;yoyoyo&lt;/Butt&gt;
</code></pre>
<h2 id="heading-wrap-up">Wrap up!</h2>
<p>So, that's it, we've gone and converted the Gatsby starter blog from using Markdown Remark over to using MDX.</p>
<p>I hope you have found it helpful.</p>
<p><strong>Thanks for reading.</strong></p>
<p>Please take a look at my other content if you enjoyed this.</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://thelocalhost.blog/">my blog</a>.</strong></p>
</blockquote>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
