"Sub themes" are themes that are used by a controlling theme to execute logic in various lifecycles. sub-themes operate the same as sub-plugins, which means if you've used gatsby-transformer-remark or gatsby-plugin-mdx and gatsby-remark-* plugins like gatsby-remark-images, then you're familiar with passing plugins to other plugins using options.
Here's an example of sub-plugins in a gatsby config that
applies gatsby-remark-prismjs
using
gatsby-transformer-remark
.
// In your gatsby-config.jsplugins: [{resolve: `gatsby-transformer-remark`,options: {plugins: [{resolve: `gatsby-remark-prismjs`,options: {}}]}}];
It is important to note that as of the writing of this, the only plugins in the
plugins
option of the controlling plugin are the only ones that have gatsby-node, etc executed
Themes are the same as plugins, so we can take advantage of the same pattern. Note that this is mostly an advanced pattern and you probably want to either use "sibling" themes or "parent/child" themes instead.
Imagine a hypothetical gatsby-theme-blog that allows other
plugins to modify the core BlogPost
interface logic to add
an additional field called "categories" to the interface.
The hypothetical BlogPost
interface would be (in this case
it was actually taken from the current
gatsby-theme-blog-core)
interface BlogPost @nodeInterface {id: ID!title: String!body: String!slug: String!date: Date! @dateformattags: [String]!keywords: [String]!excerpt: String!}
An extremely naiive solution that didn't use the graphql AST or graphql-compose to construct the type could look like (note that this logic is pseudo-code and not copy/pastable into an implementation)
const blogPostInterface = `interface BlogPost @nodeInterface {id: ID!title: String!body: String!slug: String!date: Date! @dateformattags: [String]!keywords: [String]!excerpt: String!${options.plugins.map(plugin =>plugin.extend("BlogPost"))}}`;
An implementation in the sub-plugin could be in a special
gatsby-theme-blog.js
file, just like the other gatsby-*
files. Doing this as a sub-plugin also gives us the built-in
option to export this function from the index.js
file in
our theme as well.
exports.extend = gqlType => {if (gqlType === "BlogPost") {return `categories: [String!]!`;} else {return ``;}};
in this case our theme is allowing sub-themes to modify the
core BlogPost
interface by injecting additional strings.
Note that there are numerous problems with this specific
approach and it's not clear that it's a good idea anyway
(but illustrates the point).
Our theme could then accept the sub-theme as an option.
// In your gatsby-config.jsplugins: [{resolve: `gatsby-theme-blog`,options: {plugins: [{resolve: `gatsby-theme-blog-categories`,options: {}}]}}];
post-note: If sub-themes were allowed to have a
gatsby-config, we could do things like extend categories to
be a type from WordPress, Contentful, or any other headless
CMS by adding the relevant plugin to gatsby-config.js
.