{node.frontmatter.title}
{node.frontmatter.date}
{node.slug}
---
title: Setting Up a new Dev Blog With Gatsby in 2022
date: 2022-07-20
tags: ['Gatsby', 'Guides', 'React']
---
Today we will set up a new dev blog. We will build it with Gatsby.
The initial goals are just the basic functionality of an index page with links to individual posts, and some niceties for presenting a lot of code, like syntax highlighting and a copy-to-clipboard button. We will be storing our content in markdown files.
{node.frontmatter.date} {node.slug}
## 1. Bootstrapping a new Gatsby Project
---
We could install `gatbsy-cli` globally or we can run it with NPX, but either way, _use the CLI to bootstrap the project_:
```sh image=init.png
npx gatsby new
```

Run the CLI and follow the prompts to choose your styling library, plugins, typescript or not, that sort of thing. This time we selected MDX support, responsive images, and React Helmet. For styling, we're going to use plain CSS modules.
The CLI will generate some files, and the first one to know about it is `gatsby-config.js/ts`:
```ts file=gatsby-config.ts
import type { GatsbyConfig } from 'gatsby'
const config: GatsbyConfig = {
siteMetadata: {
title: `My New Dev Blog`,
siteUrl: `http://my.blog.local`
},
graphqlTypegen: true,
plugins: [
'gatsby-plugin-image',
'gatsby-plugin-react-helmet',
'gatsby-plugin-mdx',
'gatsby-plugin-sharp',
'gatsby-transformer-sharp',
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'images',
path: './src/images/'
},
__key: 'images'
},
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'pages',
path: './src/pages/'
},
__key: 'pages'
},
]
}
export default config
```
First, a few things for our initial setup:
### Using ES6 syntax in `gatsby-node.js`
```sh image=init.png
npm i esm
```
```js file=gatsby-node.js
require = require('esm')(module)
module.exports = require('./gatsby-node.esm.js')
```
### Add aliases for absolute imports:
```js file=gatsby-node.esm.js
const path = require('path')
export onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({
resolve: {
alias: {
'@components': path.resolve(__dirname, 'src', 'components')
}
}
})
}
```
```json file=ts-config.json add=3-5 exclude=1,2,6
{
"compilerOptions": {
"paths": {
"@components/*": ["./src/components/*"]
},
...
```
If your setup is like mine and you're using Typescript + CSS modules, you'll probably also want a definitions file along these lines to prevent the compiler from finding fault with our CSS module imports:
```ts file=src/global.d.ts.ts
declare module '*.css' {
const content: { [className: string]: string }
export = content
}
```
## 2. Configuring a data source
---
Our posts (as in the mdx files) will be organized by date in subfolders. The mdx behind this post is at `./blog/2022/07/20/setting-up-with-gatsby/index.mdx` in the local filesystem, for example.
Our file system will also determine the URL of our post, so the way we organize our files is extremely important. This folder structure is what we want:
```none copy=0
π¦blog
β π2022
β β π07
β β β π20
β β β β πsetting-up-with-gatsby
β β β β β πindex.mdx
β β β β β πmdx-graphql.png
β β β π22
β β β β πsetting-up-with-gatsby-ii
β β β β β πgithub-pages-settings.png
β β β β β πindex.mdx
```
Make a folder called `blog` in the project root. This is where we will keep the markdown files that comprise our collection of blog posts.
We tell Gatsby about this `blog` folder by adding one more item at the bottom of the `plugins` array in our `gatsby-config`, just below the other `gatsby-source-filesystem` definitions.
In effect, this makes the files in the `blog` folder visible to our GraphQL queries:
```ts file=gatsby-config.ts exclude=1-3,12 add=4-11
plugins: [
...
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'blog',
path: './blog/'
},
__key: 'blog'
}
]
```
## 3. Create the blog index page
---
Start the development server...
```sh command=1 output=3,4
npm start
> dev-blog@1.0.0 start
> gatsby develop -o -p 8000
```
At [localhost:8000/___graphql](http://localhost:8000/___graphql), you get the GraphQL query interface. Basically this is just a nice UI to query Gatsby's data layer, meaning the things you have configured Gatsby to be able to get data from (like our `blog` folder). We use the UI to develop the queries we need to power our site.
For example, this query is basically what we will run to get the data to generate the index page. Try running this query while the `blog` folder is still empty:
```graphql
query BlogPosts {
allMdx(
sort: {order: DESC, fields: [frontmatter___date]},
limit: 10
) {
edges {
node {
slug
frontmatter {
date(formatString: "DD MMMM YYYY")
title
}
wordCount {
words
}
}
}
}
}
```
What happens is that GraphQL will throw an error: `Value "frontmatter___date" does not exist in "MdxFieldsEnum" enum`, because our query basically says `ORDER BY frontmatter___date`, which doesn't exist in the GQL schema.
If we had any files with `frontmatter___date` set, it would have been added to the schema dynamically. If not, GraphQL has no way of knowing that it exists.
However, if we really wanted our query to work in this situation and return an empty result set instead of an error, we could do something like this:
```js file=gatsby-node.esm.js
export createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
type Mdx implements Node {
frontmatter: MdxFrontmatter
}
type MdxFrontmatter {
date: Date @dateformat
}
`)
}
```
This isn't necessary because we don't plan on having an empty blog or inconsistent metadata in our MDX files. It is mentioned just to clarify how GraphQL queries are working with frontmatter in Gatsby.
In any case, we need at least [one MDX file with a date](https://raw.githubusercontent.com/jonkers3/dev-blog/master/blog/2022/07/20/setting-up-with-gatsby/index.mdx) to exist in our blog folder before we can continue. Once we have that, our query should work:

Now, to create our index page, replace the contents of `src/pages/index.jsx` file with the following:
```jsx file=src/pages/index.jsx start=1
import * as React from 'react'
import { graphql, Link } from 'gatsby'
const Blog = ({ data }) => {
return (
<>
{data.allMdx.nodes.map((node) => (
{node.frontmatter.title}
## 4. Create a Template for Blog Posts
---
Create a blog post template:
```jsx file=src/pages/{mdx.slug}.jsx
import * as React from 'react'
import { graphql } from 'gatsby'
import { MDXRenderer } from 'gatsby-plugin-mdx'
const BlogPost = ({ data }) => {
return (
<>
{data.mdx.frontmatter.date}
{data.mdx.frontmatter.date}
Posted: {node.frontmatter.date}
{node.slug}
{tokens.map((line, i) => (
{line.map((token, key) => (
))}
))}
)}
{data.mdx.frontmatter.date}
)
}
}
export default BlogPost
```
That's basically it. This looks pretty nice once it is working, but since presenting code is a fundamental part of a dev blog, we're going to want to improve some things. Line numbers, for example, are something we will want to add.
Given the endless possibilities we will have to have a post entirely dedicated to prism.js and advanced options for it. For now, we'll just be happy about how nice and colorful things already are.
{
navigator.clipboard.writeText(codeString)
setIsCopied(true)
setTimeout(() => setIsCopied(false), 4000)
}}
>
{isCopied ? Copied! : 'Copy'}
{tokens.map((line, i) => (
{line.map((token, key) => (
))}
))}
)}