Migrate to Netlify Today

Netlify announces the next evolution of Gatsby Cloud. Learn more

ContactSign Up
Community Plugin
View plugin on GitHub

@nickymeuleman/Gatsby Theme Blog

A Gatsby theme for creating a blog. Check out the live demo.

The default styling for the list of blogposts looks like this:

And the default styling for a single blogpost:

The lighthouse score for most pages looks like this:

Straight 100s

What you get from this theme

  • A plug and play feature rich blog platform


To use this theme in your Gatsby sites:

  1. Install the theme

    npm install --save @nickymeuleman/gatsby-theme-blog
  2. Add the theme to your gatsby-config.js:

    module.exports = {
      plugins: ["@nickymeuleman/gatsby-theme-blog"],
  3. Start your site

    gatsby develop
  4. Add an authors file and create a post! Instructions/details in the Usage section


Theme options

Key Default value Description
assetPath "data/assets" Folder location to house extra assets (like the author file.)
instances See instance options Array of instance options objects
gatsbyRemarkPlugins [] Additional plugins array to be used by gatsby-plugin-mdx
remarkPlugins [] Additional plugins array to be used by gatsby-plugin-mdx
rehypePlugins [] Additional plugins array to be used by gatsby-plugin-mdx

This theme uses gatsby-plugin-mdx and allows you to customize some configuration that it uses via the gatsbyRemarkPlugins, remarkPlugins, and rehypePlugins options. Those options take an array of the relevant plugins to be used. For more information on how to use those options, see the documentation in gatsby-plugin-mdx.

instance options

Key Default value Description
basePath "" Root url for this instance. eg: blog
contentPath "data/posts" Folder location to house individual post-folders for this instance
pagination undefined Optional object, enables pagination if provided
pagination options
Key Default value Description
postsPerPage 10 Amount of posts per paginated page
prefixPath "" Optional string. Prefixes numbers in the URL paths from paginated pages. eg: page

Example usage


// gatsby-config.js
module.exports = {
  plugins: ["@nickymeuleman/gatsby-theme-blog"],


// gatsby-config.js

module.exports = {
  plugins: [
      resolve: "@nickymeuleman/gatsby-theme-blog",
      options: {
        assetPath: "assets",
        instances: [
            contentPath: "posts",
            basePath: "blog",
            pagination: {
              postsPerPage: 10,
              prefixPath: "page",
            contentPath: "notes",
            basePath: "notes",
        gatsbyRemarkPlugins: [
            resolve: `gatsby-remark-katex`,
            options: {
              strict: `ignore`,

This will result in a seperate /blog and /notes path.

The posts in /blog will be sourced from the posts folder. The posts in /notes will be sourced from the notes folder.

Only posts in /blog will be paginated. With each paginated page holding a maximum of 10 posts. Paginated pages after the one that lists the first 10 posts, will be prefixed be /page. eg. /blog, /blog/page/2, /blog/page/3, etc.

A plugin to add support for math equations via KaTeX is added. Sidenote: For KaTeX to work correctly, The plugin on its own is not enough. The CSS also has to be included on the pages equations are used, in the demo this is done in gatsby-browser.js.

Authors are shared between instances (be it /blog or /notes). A single author can write posts in both instances. Refer to the Adding authors to see how to add authors.

Additional configuration

In addition to the theme options, there are a handful of items you can customize via the siteMetadata object in your site’s gatsby-config.js

// gatsby-config.js
module.exports = {
  siteMetadata: {
    // Used for the site title, SEO, and header component title.
    title: `My Blog Title`,
    // Used for SEO
    description: `My site description...`,
    // Used for SEO and as default if an author has no twitter defined
    social: {
      twitter: `@NMeuleman`,

Adding blog posts

In the folder that was created for the contentPath (data/posts by default). Create a folder to hold a blog post. Unless a slug is provided, the title of this folder will serve as the slug for the blogpost. Inside that folder, an index.mdx or index.md file will be the blog post itself. Along this file can be several different files specific to that blogpost (e.g. images) If no date is specified, the date the .md(x) file was created will serve as the date for the blogpost.

NOTE: If you dislike having a folder per blogpost, loose .md(x) files are also supported. Place them inside the folder created for contentPath. The title of the file will then serve as the slug of the blogpost if a slug not specified in the post’s slug field.

Extra mdx features

This theme adds a couple of things you can do inside an .mdx file. Codeblocks, obtained by surrounding a piece of code with three backticks ``` are powered by prism-react-renderer. As a result, these codeblocks support syntax highlighting for a range of languages.

Highlighting specific lines is possible by passing a string to the hl (short for highlight-lines) attribute when writing a codeblock. A title may also be added to the codeblock by passing a string to the title attribute when writing a codeblock. Numbering lines is possible by passing a numberLines attribute. Don’t like the copy button being there? Disable it by passing the noCopy attribute.

The value you pass to hl should be understood by node-parse-numeric-range. If passing a value to numberLines, it should be either true (to start the numbering at 1), or a decimal integer (to start the numbering at that number).

The code below will highlight the code inside as the jsx language, highlight lines 1,2,3, and 5, and put a small block above it displaying src/components/CodeBlock.jsx The lines will be numbered starting at 1. (Omit the # in front of these lines, I only used them here to make the three backticks show up).

# ```jsx numberLines hl=1-3,5 title=src/components/CodeBlock.jsx
# import React from 'react'
# import Highlight, { defaultProps } from 'prism-react-renderer'
# export default ({ children, className }) => {
#   const language = className.replace(/language-/, '') || ""
#   return (
#     <Highlight {...defaultProps}
#       code={children}
#       language={language}
#     >
#       {({ className, style, tokens, getLineProps, getTokenProps }) => (
#         <pre className={className} style={{ ...style }}>
#           {tokens.map((line, index) => {
#             const lineProps = getLineProps({ line, key: index })
#             return (
#               <div key={index} {...lineProps}>
#                 {line.map((token, key) => (
#                   <span key={key}{...getTokenProps({ token, key })} />
#                 ))}
#               </div>
#             )
#           }
#           )}
#         </pre>
#       )}
#     </Highlight>
#   )
# }
# ```

Example folder tree

An example folder tree for this theme with the default options:

└── data
    ├── assets
    │   ├── authors.json
    │   └── image-used-often.jpg
    └── posts
        ├── my-first-post
        │   ├── index.mdx
        │   ├── coverPhoto.jpg
        │   ├── boop.png
        │   └── infinite-boop.gif
        └── my-second-post
            ├── index.md
            ├── f1-car.jpg
            └── speed-data.svg

Adding authors

In the folder that was created for the assetPath (data/assets by default). Create a file called authors.json or authors.yaml. This file (or files, both formats can work together) holds an array of author objects.

Anatomy of an authors file

An authors file contains a top level array filled with object describing individual authors. An author can have several different field with information specific to them.

Key Value Required Description
shortName string yes Unique, url-safe identifier for the author, used in author field for blog posts
name string yes Full name eg. “Nicky Meuleman”
twitter string no Twitter handle without @
image string no Relative path from the authors file.

Anatomy of a blogpost

The blogpost itself (.md or .mdx file for now, others coming soon) can have several different fields with extra information. In .md or .mdx files these fields are set via the frontmatter.

Key Value Required Description
title string no Title of the post.
date date string no Date the post was written.
updatedAt date string no Date the post was last updated.
canonicalUrl full url string no Canonical url.
authors array of shortName strings no Authors of the post.
Should not be used in combination with the author key.
author shortName string no Author of the post.
Should not be used in combination with the authors key.
tags array of tag strings no Tags for the post.
keywords array of keyword strings no Keywords for SEO.
cover relative path to cover image no Displayed as cover image, in social cards.
published boolean, defaults to true no Include the post in production.
slug string no The last part of the URL for the post.
series string no Name of the group/series this post is a part of.

Components used in this theme.

Overwriting these with your own is highly encouraged. This can be done via component shadowing.

Query components

These components house the Gatsby template-query. They lightly transform that data and pass it on to the corresponsing Page component. Changes to these might require changes to the corresponding Page components because of that. Location to shadow: @nickymeuleman/gatsby-theme-blog/templates/<component-name>

  • BlogPostQuery
  • BlogPostListQuery
  • TagQuery
  • TagListQuery
  • AuthorQuery
  • AuthorListQuery

Page components

These components render an entire page. Each component is wrapped in the Layout component that centers the content and adds the Header. Location to shadow: @nickymeuleman/gatsby-theme-blog/components/<component-name>

  • BlogPostPage
  • BlogPostListPage
  • TagPage
  • TagListPage
  • AuthorPage
  • AuthorListPage

Regular components

These components are used by the Page components Location to shadow: @nickymeuleman/gatsby-theme-blog/components/<component-name>

  • PostCard
  • PostExtra
  • Pagination
  • CodeBlock
  • SeriesSelect

Mdx components

These components are usable in .mdx files without importing them first.

To add to this list, shadow @nickymeuleman/gatsby-theme-blog/components/mdx-components. Every component that is exported as a named export will be available for use in .mdx under that name, without importing it first.

example declaration of a <Shia /> component

// in src/@nickymeuleman/gatsby-theme-blog/components/mdx-components/index.js
import React from "react";
export * from "@nickymeuleman/gatsby-theme-blog/src/components/mdx-components/index";
const Shia = ({ children }) => (
  <p>Don't let your dreams be dreams! {children}</p>
export { Shia };

example usage in an .mdx file

# Shia LaBeouf says:

  Just do it!
  • Header
  • layout
  • Main

How to shadow components

If you want to use component shadowing, create a file at the following path in your site:


Example usage in MDX

In any MDX blogpost:

import { ComponentName } from "@nickymeuleman/gatsby-theme-blog"

# Lorem Ipsum
<ComponentName />

Components from Gatby-mdx-embed can be used without first importing them

Example usage in React components

In any React component:

import React from "react";
import { ComponentName } from "@nickymeuleman/gatsby-theme-blog";

export default () => (
    <ComponentName />


This theme uses theme-ui extensively for styling. That means a lot of the “look” (CSS-styling) can be overridden by the user, through their own theme-file. To use one, export an object that adheres to the theme-ui spec from src/gatsby-plugin-theme-ui/index.js.

It is recommended that you extend this theme’s theme-ui file and add your own overrides on top. The code below does this by merging the config used by this theme and overwriting a few color values that are used by this theme to make a beautiful, beautiful, website.

import { themeConfig } from "@nickymeuleman/gatsby-theme-blog";
import merge from "deepmerge";

const theme = merge(themeConfig, {
  colors: {
    text: `tomato`, // the named CSS color
    mutedText: `coral`, // the named CSS color
    background: `#000`,
    mutedBackground: `rgb(50,50,50)`,
    primary: `red`, // the last entry from the array in the theme's config for "red", not raw CSS red
    mutedPrimary: `red[2]`, // the red at index 2 from the array in the theme's config for "red"

export default theme;

Styling code blocks / syntax themes

To apply a different syntax highlighting theme to code blocks. Overwrite the styles under styles.CodeBlock in the theme-ui file.

  • To adjust the styles used for line highlighting, add new rules under styles.CodeBlock.lineHighlight.
  • To adjust the styles used for the title section of a codeblock, add new rules under styles.CodeBlock.title.
  • To adjust the styles used for the copy button, add new rules under styles.CodeBlock.copyButton.

Various pregenerated themes are available in the /presets folder of @theme-ui/prism. To use them, import your chosen theme’s .json file, and apply it to the styles.CodeBlock of your theme-ui file.

The code below imports a syntax highlighting theme based on the popular night-owl by Sarah Drasner. It also adds some styles so highlighting a line, or adding a title to the codeblock, will match the installed syntax theme.

import nightOwl from "@theme-ui/prism/presets/night-owl.json";
import { themeConfig } from "@nickymeuleman/gatsby-theme-blog";
import merge from "deepmerge";

const theme = merge(themeConfig, {
  styles: {
    CodeBlock: {
      highlightLine: {
        backgroundColor: `#01121f`,
        borderLeftColor: `#9ccc65`,
      title: {
        backgroundColor: nightOwl.backgroundColor,
        borderBottomColor: `#262a39`,
        color: nightOwl.color,

export default theme;

dev notes

notes: can’t see the change mentioned at https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v3-to-v4/#gatsby-transformer-json no jsonId or yamlId to be found. maybe this change happened in v5 of that plugin and this theme is on v4?

© 2023 Gatsby, Inc.