Photo of journal, pencil, and writing
Photo by Aaron Burden on Unsplash

This post is a simple introduction to Gatsby's most common feature (probably): blogging with Remark JS.

Hopefully, by the end of this article, you'll have a basic understanding of some of the usual plugins used for configuring the blogging functionality and what benefits they bring to the table!

The core plugin needed to achieve our goal is the official gatsby-transformer-remark plugin.

This post aims to explain a few of the usual plugins you can use for setting up your very own blog with GatsbyJS. Since the configuration is generic, you can target any audience, but if you don't plan to include code, you can safely skip the PrismJS plugin.

Let's begin!

Install gatsby-transformer-remark

Assuming you already have a GatsbyJS site, start by installing the plugin with:

npm install gatsby-transformer-remark

Install additional plugins

The gatsby-transformer-remark plugin supports multiple input sources (e.g., various content management systems), but the easiest option is to host markdown files locally. To do that, you will need the following plugin:

npm install gatsby-source-filesystem

Image optimization

You will most likely want to include images in your markdown content. The gatsby-remark-images plugin has several nifty features such as:

  • resizing the image to the target's container size
  • generating a srcset of multiple image sizes (support multiple device widths)
  • "blur-up" while the image is loading
npm install gatsby-remark-images gatsby-plugin-sharp gatsby-transformer-sharp

The gatsby-remark-autolink-headers plugin automatically generates HTML IDs for each heading, allowing your readers to link to a specific section in your posts.

npm install gatsby-remark-autolink-headers

Since links in markdown content are not making use of React's reach router, you'll also want to convert them for faster navigation:

npm install gatsby-plugin-catch-links

Static content

If you link any files in your markdown (such as PDFs), you'll want them to be copied along with any other static resources.

npm install gatsby-remark-copy-linked-files

Syntax highlighting with PrismJS

And assuming you will include some code in your content, you will want to integrate with PrismJS:

npm install gatsby-transformer-remark gatsby-remark-prismjs prismjs

Configure the plugins

Before proceeding, you need to decide where to host your files. One common choice is /content. You can create .md files directly in that directory, or you can create any number of subdirectories. The path you use will determine the slugs at which your content will be hosted. E.g., creating a page under /content/my/ will render to the following page: https://SITE_URL/my/article.

A slug is a common term used to denote the part of a URL identifying a page.


First, add the following configuration to your gatsby-config.js.

module.exports = {
  plugins: [
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content`,
        name: `blog`,
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 730,
      resolve: `gatsby-plugin-sharp`,
      options: {
        defaults: {
          formats: [`auto`, `webp`],
          placeholder: `blurred`,
          quality: 95,
          breakpoints: [750, 1080, 1366, 1920],
          backgroundColor: `transparent`,
          tracedSVGOptions: {},
          blurredOptions: {},
          jpgOptions: {},
          pngOptions: {},
          webpOptions: {},
          avifOptions: {},

Creating blog pages

With all of that behind us, the final step is to read all the markdown from GraphQL and generate the pages.

Edit your gatsby-node.js file and add the following (if one does not exist, create it):

const path = require(`path`);
const { createFilePath } = require(`gatsby-source-filesystem`);

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions;

  if (node.internal.type === `MarkdownRemark`) {
    // generate post slug
    const value = createFilePath({ node, getNode });

      name: `slug`,

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions;

  // Get all markdown blog posts sorted by date
  const result = await graphql(
        allMarkdownRemark(sort: { fields: [frontmatter___date], order: ASC }) {
          nodes {
            fields {

  if (result.errors) {
      `There was an error loading your blog posts`,

  // Create blog posts pages only if there's at least one markdown file
  const posts =;
  if (posts.length == 0) {
    reporter.warn(`No blog posts found`);

  // Define a template for blog post
  const component = path.resolve(`./src/components/blog-post.js`);

  posts.forEach((post, index) => {
    const previousPostId = index === 0 ? null : posts[index - 1].id;
    const nextPostId = index === posts.length - 1 ? null : posts[index + 1].id;

    console.log(`Creating blog page: ${post.fields.slug}`);
      path: post.fields.slug,
      context: {

Each page is based on a template, ./src/templates/blog-post.js. Go ahead and create this file and populate it with a React component that will display the rendered markdown and other elements. Here's an example.


Congratulations! At this point, you should have a working blogging feature that enables you to write simple markdown and transform it into pages such as the one you're reading right now.

If you have any follow-up questions or feedback, please add them on this Twitter thread!

If you enjoyed this post, please share it with your friends!