DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

alex
alex

Posted on

How to use markdown in pages in Gatsby

Most tutorials show how to create full pages with Gatsby's createPages, like for blogs. However, what if we don't want to create pages? What if we want to take markdown from markdown files and insert them into pages instead? That is, how do we insert markdown into JSX? I haven't found a lot of details on this. So, this is a fairly quick piece on how to do it.

In this article, we will display markdown from a markdown file in the default index.js landing page.

I kind of wrote this from memory. Please let me know if I made a mistake, missed anything, or if you have a faster method.

Why put markdown in pages?

I work with non-tech people. Sometimes, we want an SSG instead of a CMS. Markdown is easy to learn and write with.

This lets content folks publish with ease. They can upload markdown files and edit them whenever they like.

Basic setup

Create a new project

Set up the default starter project with gatsby new myproj. You can read more about starting a project here.

Install plugins

We need to install gatsby-source-filesystem to help Gatsby access the markdown files that we'll soon create. So, do this in your project root directory: npm install gatsby-source-filesystem.

Tell Gatsby about this new plugin by adding this to the gatsby-config.js file:

plugins: [
  {
    resolve: `gatsby-source-filesystem`,
    options: {
      name: `markdown-pages`,
      path: `${__dirname}/src/markdown-pages`,
    },
  },
]
Enter fullscreen mode Exit fullscreen mode

Next, install gatsby-transformer-remark, which lets Gatsby use markdown properly: npm install gatsby-transformer-remark

Add it to gatsby-config.js:

plugins: [
  {
    resolve: `gatsby-source-filesystem`,
    options: {
      path: `${__dirname}/src/markdown-pages`,
      name: `markdown-pages`,
    },
  },
  `gatsby-transformer-remark`,
]
Enter fullscreen mode Exit fullscreen mode

Note the path. It's ${__dirname}/src/markdown-pages. This means Gatsby should look into /src/markdown-pages for markdown files. Create the markdown-pages directory in src.

Make a markdown file

Create a markdown file in /src/markdown-pages. For our current demo purpose, let's create one called home.md.

Examples online usually tell us to create something like this:

---
path: "/"
date: "2019-10-06"
title: "About"
---

# About us

Here is the body of our content. It will be magically turned into HTML.
Enter fullscreen mode Exit fullscreen mode

The part in --- is the frontmatter. We place metadata in there.

Graphql code

We'll make this really simple. We'll place our code in index.js so everything loads on the landing page. Find it in /src/pages/index.js.

We use graphql to look for the markdown file /src/markdown-pages/home.md. After checking out a handful of solutions, such as here and here, I decided that the allMarkdownRemark method is probably easiest. Feel free to let me know if you have an even easier option.

I haven't figured out how to fully properly use regex in graphql yet but we'll use it as shown below. It should work for files named in simple ways.

Here's the query to get the file:

graphql`
query {
  allMarkdownRemark(
    filter: {fileAbsolutePath: {regex: "/home/"}}
  ) {
      ...
    }
}
`
Enter fullscreen mode Exit fullscreen mode

In the code above, the regex bit looks for file names with "home" in it. You might want to tailor the regex to your needs. It's currently fine for me because I'm using it for a simple site with just Home, Contact, and About pages.

Here's how to get the markdown content in home.md:

graphql`
query {
  allMarkdownRemark(
    filter: {fileAbsolutePath: {regex: "/home/"}}
  ) {
      edges {
        node {
          frontmatter {
            title
            date(formatString: "DD MMMM YYYY")
          }
          html
        }
      }
    }
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we've extracted a couple of frontmatter metadata. We've left out path because it's not needed today. We've also taken the html content.

It's not done yet though. We have to export it. So, the whole code looks like this:

export const query = graphql`
query {
  allMarkdownRemark(
    filter: {fileAbsolutePath: {regex: "/home/"}}
  ) {
      edges {
        node {
          frontmatter {
            title
            date(formatString: "DD MMMM YYYY")
          }
          html
        }
      }
    }
}
`
Enter fullscreen mode Exit fullscreen mode

Place it just above export default IndexPage.

Query response is in props

The data from the graphql query will be in props. So, let's put it into IndexPage with { data }:

const IndexPage = ({ data }) => (
  ...
)
Enter fullscreen mode Exit fullscreen mode

Let's also pass into IndexPage a method we'll use to read and display the markdown properly. We'll call it md().

To make things simpler, let's get rid of almost all the default JSX in IndexPage and just have {md({ data })} in it:

const IndexPage = ({ data }) => (
  <Layout>
    {md({ data })}
  </Layout >
)

Enter fullscreen mode Exit fullscreen mode

Feel free to add other JSX to it if you like.

Method to read markdown

Before we continue, let's note that the structure of the data follows the graphql query we made above. After some exploration, I found out that allMarkdownRemark returns an array. I then used both the graphql playground and console in developer tools to navigate the structure properly. Despite that, it still takes some experimentation to get right.

In the interest of time, I'll just tell you the markdown content we want is in data.data.allMarkdownRemark.edges[0].node.

Now, let's build the function md(). It has two important parts:

First, we destructure frontmatter and html from the props.

Second, we return the JSX we want with the markdown properly rendered.

Here's how it looks:

const md = (data) => {
  const { frontmatter, html } = data.data.allMarkdownRemark.edges[0].node

  return (<div>
    <h1>{frontmatter.title}</h1>
    <p>{frontmatter.date}</p>
    <div
      dangerouslySetInnerHTML={{ __html: html }}
    />
  </div>)
}
Enter fullscreen mode Exit fullscreen mode

The whole index.js

By now, our code should look like this:

import React from "react"
import { graphql } from "gatsby"

import Layout from "../components/layout"

const md = (data) => {
  const { frontmatter, html } = data.data.allMarkdownRemark.edges[0].node
  console.log(frontmatter.title)
  return (<div>
    <h1>{frontmatter.title}</h1>
    <p>{frontmatter.date}</p>
    <div
      dangerouslySetInnerHTML={{ __html: html }}
    />
  </div>)
}

const IndexPage = ({ data }) => (
  <Layout>
    {md({ data })}
  </Layout >
)

export const query = graphql`
query {
  allMarkdownRemark(
    filter: {fileAbsolutePath: {regex: "/home/"}}
  ) {
      edges {
        node {
          frontmatter {
            title
            date(formatString: "DD MMMM YYYY")
          }
          html
        }
      }
    }
}
`

export default IndexPage
Enter fullscreen mode Exit fullscreen mode

Run gatsby develop and check out the page. It should be at localhost:8000.

Top comments (1)

Collapse
kuldeepkumar profile image
Kuldeep Kumar

Any way we can query only markdownRemark instead of all? And I am trying to query inside layout and child components it doesn't work there any idea , how to simply achieve that

🌚 Browsing with dark mode makes you a better developer by a factor of exactly 40.

It's a scientific fact.