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`,
},
},
]
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`,
]
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.
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/"}}
) {
...
}
}
`
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
}
}
}
}
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
}
}
}
}
`
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 }) => (
...
)
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 >
)
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>)
}
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
Run gatsby develop
and check out the page. It should be at localhost:8000.
Top comments (1)
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