• Home
  • Portfolio
  • Articles
  • Pricing
  • About
  • Contact

Generating Contentful Pages Dynamically with Gatsby Node.js

Jan 13th, 2020

Alex Quasar



This overview assumes you have some familiarity with Gatsby and Contentful. In this article I will be using a Menu Items content model from Contentful for a hypothetical cafe store Aquasar Cafe. The Menu Items will have the following content fields such as title, subtitle, description, pictures, slug, price. We will be creating pages out of all the menu items.

Creating a Content Model and Some Content in Gatsby

I have the following Content Model `Menu Items` set up where

  • title - A short text field, required (required and unique)

  • slug - A short text field, with appearance set to slug (required and unique)

  • subtitle - A long text field (regular not rich text format)

  • description - A long rich text field

  • pictures - Media, many files

  • category - Short text List

  • subcategory - Short text List

  • price - Decimal number

Your Content model might be quite different of course, but I have laid out the required items because this effects how you write your graph query.

Once that is done, go ahead and some content for your new content model.

Gatsby Node JS

Inside of the gatsby-node.js file in your text editor, we can use createPages method to render a page for each content you created with your content model above.

const path = require('path');

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

  const { data } = await graphql(`
      menuItems: allContentfulMenuItems {
        nodes {

  // creates pages Menu
  data.menuItems.nodes.forEach(item => {
      path: `menu/${item.slug}`,
      component: path.resolve('./src/templates/MenuItemTemplate.js'),
      context: {
        slug: item.slug,
Each page is using a template called MenuItemTemplate from the unique slugs in Contentful. Lets go ahead and create this.

Template File

I have created a very simple template file which will be able to render your content including pictures and rich text which hopefully is enough for you to get started and customized it in your own project.

// Use as reference starting point for different page queries
import React from 'react';
import { graphql } from 'gatsby';
import Image from 'gatsby-image';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { BLOCKS, MARKS } from '@contentful/rich-text-types';
import Layout from '../components/layouts/Layout';
import { Bold, P } from '../components/reusableStyles/typography/Typography';

// run the template query
export const query = graphql`
  query menuSimpleTemplateQuery($slug: String!) {
    menu: allContentfulMenuItems(filter: { slug: { eq: $slug } }) {
      nodes {
        subtitle {
        description {
        pictures {
          fluid(maxWidth: 500) {

const RTFBold = ({ children }) => <Bold>{children}</Bold>;
const Text = ({ children }) => <P>{children}</P>;

const MenuItemTemplate = ({ data: { menu } }) => {
  const {
    subtitle: { subtitle },
    description: { json },
  } = menu.nodes[0];

  const options = {
    renderMark: {
      [MARKS.BOLD]: text => <RTFBold>{text}</RTFBold>,

    renderNode: {
      [BLOCKS.PARAGRAPH]: (node, children) => <Text>{children}</Text>,
  return (
      <Image fluid={pictures[0].fluid} />
      <main>{documentToReactComponents(json, options)}</main>
export default MenuItemTemplate;

At the top I am using a graphql query to render and filter for each menu item based on the current slug passed in. It knows which page to render because we build and created this in gatsby-node.js file earlier. Make sure you have installed the necessary packages imported at the top, namely the Contentful Packages which you help to customize your content from the rich text editor. There currently is not as many formatting options in the rich text editor as I would like. There have been many requests to expand on it, so I hope the team at Contentful is aware and looks to address this need in the future.

One thing that is not super clear is that when using the rich text editor you will need to query the description as json similar to above

To ensure images are optimized Contentful has its own image processor so inside fluid make sure to pass in  ...GatsbyContentfulFluid_withWebp or some other similar option.

For more info or questions please reach out to me directly or view this repo on If it helpful please consider to star the repo