Component Shadowing in Gatsby Child Themes

Today
on stream I
worked through the final steps for implementing Child
Theming for Gatsby. The interesting outcomes of this are
that

  1. Themes can now depend on other themes as “parents”
  2. Component Shadowing works across all ancestors and has
    been moved to the root of the src directory.

In this post I’ll be referencing work from the
gatsby-theme-examples
repo

Multiple Themes

The way I chose to split up the original gatsby-theme-blog
was into two basic pieces. gatsby-theme-blog-core would
hold the data structures while gatsby-theme-blog would
handle the UI layer. Since this isn’t a post about child
theming itself but rather Component Shadowing, that’s really
all you have to know for now.

Component Shadowing

The idea behind Component Shadowing is fairly small. If we
have some file in our theme theme/a/b.js, then we can
replace this component at build time by creating a different
file in our own site at our-site/a/theme/b.js. This means
that themes can take advantage of Component Shadowing for
replacing brand colors, the way a blog post renders, or
anything else that can be represented as a file.

Until now, Component Shadowing was only enabled for files
placed in src/components. This choice was made to limit
the blast radius while we evaluated the feature itself. As
it turns out, people start putting everything in
src/components to take advantage of Shadowing. As a result
we’ve moved the supported directory up one level to src.

The impact of this change is that Shadowing is enabled by
default for the entire src/ directory. Since
gastby-theme-blog-core is a parent of gatsby-theme-blog
we have the following set of files.

  • gatsby-theme-blog-core
    • src/components/blog-post.js
  • gatsby-theme-blog
    • src/gatsby-theme-blog-core/components/blog-post.js
  • blog-b-core (a user’s site)
    • src/gatsby-theme-blog-core/components/blog-post.js

Remember that the order for how this file gets resolved is
the same order that we use for composition. In our case, it
is [gatsby-theme-blog-core, gatsby-theme-blog,
user-site]. The last entry in the array wins.

gatsby-theme-blog-core offers a shadowable component that
handles rendering for a blog post page template at
src/components/blog-post.js. The only thing this component
does is dump the props onto the page inside of a <pre>
tag.

JS

import

React

from

"react"

;

export

default

props

=>

(

<

pre style

=

{

{

whiteSpace

:

"pre-wrap"

}

}

>

{

JSON

.

stringify

(

props

,

null

,

2

)

}

<

/

pre

>

)

;

gatsby-theme-blog, being responsible for the UI layer,
shadows this component by using a file at
src/gatsby-theme-blog-core/components/blog-post.js. This
file includes a React component, set of styles, and a much
nicer rendering of the props we have. Because
gatsby-theme-blog is included “later”, it’s version of the
blog-post rendering wins.

Then, when we used gatsby-theme-blog in our own site, we
decided we wanted the blog post to render differently than
the child theme, so we created a new file in our site at
src/gatsby-theme-blog-core/components/blog-post.js. This
is a very similar location to when we shadowed the blog post
component in the child theme. Because the user’s site is
always included last, the new blog post file wins over both
themes.

Fin

Component Shadowing is a powerful way for people who are
unfamiliar with GraphQL to get started building out their
own sites. A child theme could be a collection of these
shadowed components that use any set of technologies without
needing to worry about writing new GraphQL queries to get
data: The page structure and content is already
well-defined.