Skip to content

Creating Pages From CMS Data — Contentful, WordPress and More

DodaTech Updated 2026-06-28 4 min read

Learn how to create pages from headless CMS data in Gatsby using Contentful, WordPress, Strapi, and other CMS source plugins for dynamic content.

In this lesson, you'll understand how to connect a CMS, query its data, and generate pages programmatically from CMS content types.

What You'll Learn

How to configure CMS source plugins, query CMS data in gatsby-node.js, create pages for each content entry, and handle rich text and media.

Why It Matters

CMS-backed Gatsby sites let non-developers manage content while delivering the performance of a static site. This is the most common production Gatsby setup.

flowchart LR
    A[CMS Content] --> B[Source Plugin]
    B --> C[GraphQL Nodes]
    C --> D[gatsby-node.js]
    D --> E[Static Pages]
    E --> F[CDN]
    style B fill:#639,color:#fff
    style E fill:#4a148c,color:#fff

Contentful Setup

// gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: 'gatsby-source-contentful',
      options: {
        spaceId: process.env.CONTENTFUL_SPACE_ID,
        accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
        environment: 'master'
      }
    }
  ]
};

Query Contentful entries:

query {
  allContentfulBlogPost {
    nodes {
      title
      slug
      publishedDate
      body {
        raw
      }
      author {
        name
      }
    }
  }
}

Creating Pages from Contentful

// gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  const result = await graphql(`
    query {
      allContentfulBlogPost {
        nodes {
          slug
        }
      }
      allContentfulCategory {
        nodes {
          slug
        }
      }
    }
  `);

  // Create blog post pages
  result.data.allContentfulBlogPost.nodes.forEach(post => {
    createPage({
      path: `/blog/${post.slug}/`,
      component: path.resolve('./src/templates/cms-blog-post.js'),
      context: { slug: post.slug }
    });
  });

  // Create category pages
  result.data.allContentfulCategory.nodes.forEach(cat => {
    createPage({
      path: `/category/${cat.slug}/`,
      component: path.resolve('./src/templates/cms-category.js'),
      context: { slug: cat.slug }
    });
  });
};

Output: Each Contentful entry becomes a page. Blog posts are at /blog/slug/ and categories at /category/slug/.

WordPress Setup

// gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: 'gatsby-source-wordpress',
      options: {
        url: 'https://example.com/graphql'
      }
    }
  ]
};
// gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  const result = await graphql(`
    query {
      allWpPost {
        nodes {
          uri
          categories { nodes { slug } }
        }
      }
      allWpCategory {
        nodes { slug }
      }
    }
  `);

  result.data.allWpPost.nodes.forEach(post => {
    createPage({
      path: post.uri,
      component: path.resolve('./src/templates/wp-post.js'),
      context: { uri: post.uri }
    });
  });
};

Output: WordPress posts become Gatsby pages at their WordPress URI paths.

Strapi Setup

// gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: 'gatsby-source-strapi',
      options: {
        apiURL: process.env.STRAPI_API_URL,
        collectionTypes: ['article', 'category'],
        singleTypes: ['homepage', 'about']
      }
    }
  ]
};
// gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  const result = await graphql(`
    query {
      allStrapiArticle {
        nodes {
          slug
        }
      }
    }
  `);

  result.data.allStrapiArticle.nodes.forEach(article => {
    createPage({
      path: `/articles/${article.slug}/`,
      component: path.resolve('./src/templates/strapi-article.js'),
      context: { slug: article.slug }
    });
  });
};

Output: Strapi articles become Gatsby pages. The apiURL points to your Strapi instance.

Handling Rich Text

CMS rich text requires special handling:

// src/templates/cms-blog-post.js
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { BLOCKS, MARKS } from '@contentful/rich-text-types';
import React from 'react';

const options = {
  renderMark: {
    [MARKS.BOLD]: text => <strong>{text}</strong>
  },
  renderNode: {
    [BLOCKS.EMBEDDED_ASSET]: node => {
      const { gatsbyImageData, description } = node.data.target;
      return <GatsbyImage image={getImage(gatsbyImageData)} alt={description} />;
    }
  }
};

export default function BlogPost({ data }) {
  const post = data.contentfulBlogPost;
  return (
    <article>
      <h1>{post.title}</h1>
      {documentToReactComponents(JSON.parse(post.body.raw), options)}
    </article>
  );
}

Output: Contentful rich text is converted to React components with embedded assets rendered as optimized images.

Common Mistakes

  1. Hardcoding API keys in config: Use environment variables. Never commit CMS API keys to version control.
  2. Not handling CMS previews: For Contentful preview mode, use a separate API key and host: 'preview.contentful.com' option.
  3. Missing content type IDs: Contentful content type IDs must match what you query. Check the GraphiQL explorer for exact type names.
  4. Forgetting to rebuild after CMS changes: Gatsby doesn't auto-rebuild when CMS data changes. Use webhooks or Gatsby Cloud for automatic rebuilds.
  5. Not optimizing CMS images: Configure gatsby-plugin-image with your CMS plugin. Contentful and WordPress support automatic image optimization.

Practice Questions

  1. How do you configure a Contentful source plugin? Answer: Add gatsby-source-contentful with spaceId and accessToken options. Use environment variables for security.

  2. What does allWpPost return? Answer: All WordPress posts from the connected WordPress GraphQL endpoint, with fields like title, uri, content, and featuredImage.

  3. How do you render Contentful rich text? Answer: Use @contentful/rich-text-react-renderer to convert the rich text JSON to React components.

  4. What happens when CMS data changes? Answer: Gatsby doesn't auto-rebuild. You need a Webhook trigger (Gatsby Cloud, Netlify, Vercel) or manual rebuild.

Challenge

Set up a Contentful space with three content types (Blog, Author, Category). Create pages for each, with blog posts linking to authors and categories. Use Gatsby Cloud for automatic rebuilds on content changes.

Mini Project

Build a multi-CMS site that sources from both Contentful and WordPress. Create a unified blog listing that combines posts from both sources, sorted by date. Use consistent styling across both.

FAQ

Can I use multiple CMS sources together?

: Yes. You can source from Contentful, WordPress, and Strapi simultaneously. Each CMS content appears as separate GraphQL types.

How do I handle CMS image optimization?

: Most CMS plugins integrate with gatsby-plugin-image. Contentful and WordPress plugins automatically Process images.

Does incremental builds work with CMS sources?

: Yes. Gatsby Cloud and services like Netlify support incremental builds triggered by CMS webhooks.

Can I preview CMS content before publishing?

: Yes. For Contentful, use the Preview API key. For WordPress, use draft status filtering.

What's Next

Learn about Gatsby SEO and Meta Tags to optimize your Gatsby site for search engines with meta tags and structured data.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro