Building large Gatsby themes can result in a large array of configuration options to manage as a theme author. We can also find ourselves wanting the ability to let other themes or users' sites to add additional configuration instances (ex: get more blog posts from this folder and process them as usual).
Let's take a look at an example of this pattern. First, set
up our configuration node type in the Gatsby system using
createSchemaCustomization
.
exports.createSchemaCustomization = ({ actions },) => {const { createTypes } = actionscreateTypes(`type NotesConfig implements Node {basePath: String!homeText: StringbreadcrumbSeparator: String}`)
Then in sourceNodes
we'll talk the options configured with
defaults and inject them into the node system as a node.
var crypto = require("crypto");exports.sourceNodes = ({ actions: { createNode }, schema },{basePath = `/`,homeText = `~`,breadcrumbSeparator = `/`}) => {// create garden data from plugin configconst notesConfig = {breadcrumbSeparator,basePath,homeText};createNode({...notesConfig,id: `gatsby-theme-notes-config`,parent: null,children: [],internal: {type: `NotesConfig`,contentDigest: crypto.createHash(`md5`).update(JSON.stringify(notesConfig)).digest(`hex`),content: JSON.stringify(notesConfig),description: `Notes Config`}});};
Note that in this case we've specified a specific known ID we can query. It is best practice to use
createNodeId
for ids like this and we're doing it this way in this post for some clarity.
Because we've specified a specific ID, and because of
Gatsby's implementation of node ownership, our theme
effectively owns this object and we can be confident it
hasn't been modified. Using useStaticQuery
we can now
access this data inside our components anywhere in our
application.
import React from "react";import { useStaticQuery, graphql } from "gatsby";export default () => {const data = useStaticQuery(graphql`query NotesConfigObjectQuery {nodesConfig(id: { eq: "gatsby-theme-notes-config" }) {homeTextbreadcrumbSeparator}}`);return (<header><h1>{data.notesConfig.homeText}</h1></header>);};
If we had some field that semantically could be included
multiple times, we could skip the specific ID and query
allNotesConfig
with our own merge algorithm. Let's take a
look at a hypothetical someOption
field that is typed as
such:
type someOption = [String!]!
Querying this gives us a set of nested arrays.
import React from "react";import { useStaticQuery, graphql } from "gatsby";export default () => {const data = useStaticQuery(graphql`query NotesConfigObjectQuery {allNotesConfig {nodes {someOption}}}`);return (<header><ul>{data.allNotesConfig.nodes.map(({ someOption }) => someOption).flat().map(item => (<li>{item}</li>))}</ul></header>);};