Creating Pages From CMS Data — Contentful, WordPress and More
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
- Hardcoding API keys in config: Use environment variables. Never commit CMS API keys to version control.
- Not handling CMS previews: For Contentful preview mode, use a separate API key and
host: 'preview.contentful.com'option. - Missing content type IDs: Contentful content type IDs must match what you query. Check the GraphiQL explorer for exact type names.
- Forgetting to rebuild after CMS changes: Gatsby doesn't auto-rebuild when CMS data changes. Use webhooks or Gatsby Cloud for automatic rebuilds.
- Not optimizing CMS images: Configure
gatsby-plugin-imagewith your CMS plugin. Contentful and WordPress support automatic image optimization.
Practice Questions
How do you configure a Contentful source plugin? Answer: Add
gatsby-source-contentfulwithspaceIdandaccessTokenoptions. Use environment variables for security.What does
allWpPostreturn? Answer: All WordPress posts from the connected WordPress GraphQL endpoint, with fields liketitle,uri,content, andfeaturedImage.How do you render Contentful rich text? Answer: Use
@contentful/rich-text-react-rendererto convert the rich text JSON to React components.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
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