Reputation: 567
I'm working on Gatsbyjs web with headless WordPress as data source. I don't want to generate all pages statically in /pages, but instead in im gatsby-node.js mapping through allPages / allPosts queries and sending data to page/post template using createPage
API.
But my pages are kinda complex, they seem to need very different queries ( acf.. )
What would be the best practice here? Should I create a template for each and every page and map data directly into those?
Upvotes: 2
Views: 439
Reputation: 29320
Yes, you hit the nail. You have to generate templates/pages for each type of page you want to generate.
You only need to create different createPage
actions and point them in different templates/pages. For example:
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
slug: node.fields.slug,
},
})
and
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/tags.js`),
context: {
slug: node.fields.slug,
},
})
Standard use-case
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
`)
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
slug: node.fields.slug,
},
})
})
}
component
will define where the data will be available to use and which template/page/component will use.
If you want to use a different template rather than /blog-post
you need to create another createPage
action. Something like this:
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions
return graphql(`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
id
fields {
slug
}
frontmatter {
tags
templateKey
}
}
}
}
}
`).then(result => {
if (result.errors) {
result.errors.forEach(e => console.error(e.toString()))
return Promise.reject(result.errors)
}
const posts = result.data.allMarkdownRemark.edges
posts.forEach(edge => {
const id = edge.node.id
createPage({
path: edge.node.fields.slug,
tags: edge.node.frontmatter.tags,
component: path.resolve(
`src/templates/blog-post.js`
),
// additional data can be passed via context
context: {
id,
},
})
})
// Tag pages:
let tags = []
// Iterate through each post, putting all found tags into `tags`
posts.forEach(edge => {
if (_.get(edge, `node.frontmatter.tags`)) {
tags = tags.concat(edge.node.frontmatter.tags)
}
})
// Eliminate duplicate tags
tags = _.uniq(tags)
// Make tag pages
tags.forEach(tag => {
const tagPath = `/tags/${_.kebabCase(tag)}/`
createPage({
path: tagPath,
component: path.resolve(`src/templates/tags.js`),
context: {
tag,
},
})
})
})
}
Without going into details of what it does or how (if you need I can detail the answer), the important thing is that you can use createPage
action to define how many pages, data, and components you need. In this case, blog-post.js
and tags.js
which will be found under /blog-post/postSlug
and in /tag/tagPath
.
Promise use-case
If you have a small website or project, the previous case may work, but if your project grows, it becomes hell to find information among so many lines. So I use to create promises to store that information. In my gatsby-node
:
const postsBuilder = require("./src/build/postsBuilder");
const tagsBuilder = require("./src/build/tagsBuilder");
exports.createPages = async ({graphql, actions}) => {
await Promise.all(
[
postBuilder(graphql, actions),
tagsBuilder(graphql, actions)
]
);
};
Then, in one of those builders:
const path = require('path')
async function postsBuilder(graphql, actions) {
const {createPage} = actions;
const postsQuery= await graphql(`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
id
fields {
slug
}
frontmatter {
tags
templateKey
}
}
}
}
}`);
const resultForms = postsQuery.data.allMarkdownRemark.edges;
resultForms.map(node => {
createPage({
path: node.node.url + '/',
component: whateverYouNeed,
context: {
name: node.node.name,
url: node.node.url
},
})
});
}
module.exports = postsBuilder;
Note that code could be refactored in many ways, is just to show another approach of what you are able to do.
I think the promise way is much more semantic and clean but it's up to you to use whatever you need in each case.
References:
Upvotes: 3