2019-08-05
|~4 min read
|728 words
When I set up my gatsby-filesystem
previously, I noted that if there were multiple file systems that I wanted access to, all I need to do was to duplicate that post.
As a website grows, it’s possible that its scope will expand. What might start as a simple text blog can evolve to have many different types of content. Imagine a site that has
To accommodate the different types of content, I want a site that looks different based on the type of content. I want to apply a different layout for each type.
gatsby-config
to have multiple resolvers based on the content type.createPage
action to differentiate based on the type of contentNext, I’ll demo stepping through these three steps for two types of content: blogs and videos.
For example, in a project with the following structure:
.
├── content
│ ├── blog
│ └── video
├── public
├── src
└── static
I could configure my resolvers to have access the video and the blogs respectively:
module.exports = {
plugins: [
...
{
resolve: ‘gatsby-source-filesystem',
options: {
name: ‘blog’,
path: ‘content/blog’
}
},
{
resolve: ‘gatsby-source-filesystem’,
options: {
name: ‘video’,
path: ‘content/video’
}
},
],
}
Whereas previously, I asked for all of my MDX files (since that’s all I had), now I want to be a little more verbose:
query {
blog: allFile(filter: { sourceInstanceName: { eq: "blog" } }) {
nodes {
childMdx {
frontmatter {
title
}
}
}
}
video: allFile(filter: { sourceInstanceName: { eq: "video" } }) {
nodes {
childMdx {
frontmatter {
title
}
}
}
}
}
The key here is that since the queries are identical, we need to alias them so that GraphQL knows which is which.1
Also notice that I’m now going through the instance name of — this is what is configured as the name
attribute in the filesystem resolver.
exports.createPages = async ({ actions, graphql, reporter }) => {
// In node (which this is), graphql is a function that takes a string.
const result = await graphql(`
query {
blog: allFile(filter: { sourceInstanceName: { eq: "blog" } }) {
nodes {
childMdx {
frontmatter {
slug
}
}
}
}
video: allFile(filter: { sourceInstanceName: { eq: "video" } }) {
nodes {
childMdx {
frontmatter {
slug
}
}
}
}
}
`);
if (result.errors) {
reporter.panic(‘failed to create posts’, result.errors);
}
const blogNodes = result.data.blog.nodes;
const videoNodes = result.data.video.nodes;
blogNode.forEach(post => {
const path = post.childMdx.frontmatter.slug;
actions.createPage({
path,
component: require.resolve(‘./src/templates/post.js’),
context: {
slug: post.childMdx.frontmatter.slug,
},
});
})
videoNodes.forEach(post => {
const path = post.childMdx.frontmatter.slug;
actions.createPage({
path,
component: require.resolve(‘./src/templates/video.js’),
context: {
slug: post.childMdx.frontmatter.slug,
},
});
})
;
};
Now that my query has bene updated to differentiate between posts and videos, I can create pages programmatically for each based on different layouts!
Refactoring a site to accommodate multiple types of content is three steps:
There were two stumbling blocks for me in exploring this space. Though they weren’t major, it’s worth calling out in case it can help someone else in the future (aka me when I come back to look how this is done):
allFile
key in the results. So, for blogs, I go to blog
and videos to video
. After doing this once, it makes sense intuitively — after all, that’s exactly how I’d expect an alias to work.nodes
is an array itself. So, I had to start iterating at that level rather than lower down as I had when it was just a single file type and I was querying based on allMdx
(rather than allFiles
).Overall, however the conversion was straightforward and I’m now positioned to have multiple types of content on my site!
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!