|
| 1 | +--- |
| 2 | +title: Shadowing in Gatsby Themes |
| 3 | +--- |
| 4 | + |
| 5 | +Gatsby themes introduce a concept called "shadowing". This feature allows users to replace a file in the `src` directory that is included in the webpack bundle with their own implementation. This works for React components, pages in `src/pages`, JSON files, TypeScript files, as well as any other imported file (such as `.css`) in your site. |
| 6 | + |
| 7 | +A practical use case is when you've installed `gatsby-theme-blog` and want to customize the author `Bio` component to add your own biographical content. Shadowing lets you replace the theme’s original file, `gatsby-theme-blog/src/components/bio.js`, with your own file to make any changes you need. |
| 8 | + |
| 9 | +## Shadowing Example |
| 10 | + |
| 11 | +If you’ve installed `gatsby-theme-blog` you’ll notice that it renders a `Bio` component which is used in the `BlogPost` template. If you’d like to change the `Bio` component you can do so with the shadowing API. |
| 12 | + |
| 13 | +### Theme file structure |
| 14 | + |
| 15 | +You can inspect `gatsby-theme-blog`'s file structure to determine the file path for the file you want to shadow. |
| 16 | + |
| 17 | +```txt:title="tree gatsby-theme-blog" |
| 18 | +├── gatsby-config.js |
| 19 | +├── gatsby-node.js |
| 20 | +└── src |
| 21 | + ├── components |
| 22 | + │ ├── bio-content.js |
| 23 | + │ ├── bio.js // highlight-line |
| 24 | + │ ├── header.js |
| 25 | + │ ├── headings.js |
| 26 | + │ ├── home-footer.js |
| 27 | + │ ├── layout.js |
| 28 | + │ ├── post-footer.js |
| 29 | + │ ├── post.js |
| 30 | + │ ├── posts.js |
| 31 | + │ ├── seo.js |
| 32 | + │ └── switch.js |
| 33 | + ├── gatsby-plugin-theme-ui |
| 34 | + │ ├── colors.js |
| 35 | + │ ├── components.js |
| 36 | + │ ├── index.js |
| 37 | + │ ├── prism.js |
| 38 | + │ ├── styles.js |
| 39 | + │ └── typography.js |
| 40 | + └── templates |
| 41 | + ├── post.js |
| 42 | + └── posts.js |
| 43 | +``` |
| 44 | + |
| 45 | +### Customizing the `Bio` component |
| 46 | + |
| 47 | +In this case, the file to shadow is `gatsby-theme-blog/src/components/bio.js`. |
| 48 | + |
| 49 | +The shadowing API uses a deterministic file structure to determine which component will be rendered. In order to override the `Bio` component in `gatsby-theme-blog`, create a file named `user-site/src/gatsby-theme-blog/components/bio.js`. |
| 50 | + |
| 51 | +Any file that lives in the `src/gatsby-theme-blog` directory of the user’s site will be used instead of a file with the same name located in the theme’s src directory: `gatsby-theme-blog/src`. This replaces the entire file: to re-use parts of the original file from the theme such as functionality or styling, check out the sections of this doc on [extending](#extending-shadowed-files) and [importing](#importing-the-shadowed-component) shadowed files. |
| 52 | + |
| 53 | +This means that `user-site/src/gatsby-theme-blog/components/bio.js` will be rendered in place of `gatsby-theme-blog/src/components/bio.js`: |
| 54 | + |
| 55 | +```js:title=src/gatsby-theme-blog/components/bio.js |
| 56 | +import React from "react" |
| 57 | +export default () => <h1>My new bio component!</h1> |
| 58 | +``` |
| 59 | + |
| 60 | +A successful shadow of the Bio component will result in the following directory tree: |
| 61 | + |
| 62 | +```txt |
| 63 | +user-site |
| 64 | +└── src |
| 65 | + └── gatsby-theme-blog |
| 66 | + └── components |
| 67 | + └── bio.js // highlight-line |
| 68 | +``` |
| 69 | + |
| 70 | +## Shadowing other themes |
| 71 | + |
| 72 | +Some themes, including `gatsby-theme-blog`, install other themes. `gatsby-theme-blog` uses `gatsby-plugin-theme-ui`. If you want to customize the implementation of any theme you can do so with shadowing. |
| 73 | + |
| 74 | +For example, to shadow `index.js` from `gatsby-plugin-theme-ui`, create a file named `user-site/src/gatsby-theme-blog/gatsby-plugin-theme-ui/index.js`. |
| 75 | + |
| 76 | +```js:title=src/gatsby-theme-blog/gatsby-plugin-theme-ui/index.js |
| 77 | +export default { |
| 78 | + fontSizes: [12, 14, 16, 24, 32, 48, 64, 96, 128], |
| 79 | + space: [0, 4, 8, 16, 32, 64, 128, 256], |
| 80 | + colors: { |
| 81 | + blue: `blue`, |
| 82 | + red: `tomato`, |
| 83 | + }, |
| 84 | +} |
| 85 | +``` |
| 86 | + |
| 87 | +Which will result in the following directory tree: |
| 88 | + |
| 89 | +```txt |
| 90 | +user-site |
| 91 | +└── src |
| 92 | + └── gatsby-theme-blog |
| 93 | + └── components |
| 94 | + └── tokens |
| 95 | + └──index.js // highlight-line |
| 96 | +``` |
| 97 | + |
| 98 | +## Any source file is shadowable |
| 99 | + |
| 100 | +The shadowing API isn’t restricted to React components; you can override any JavaScript, Markdown, MDX, or CSS file in the `src` directory. This gives you fine-grained control of all functionality, content, and styling that a theme provides. |
| 101 | + |
| 102 | +If you wanted to shadow a CSS file in `gatsby-theme-awesome-css` that's found at `src/styles/bio.css` you can do so by creating `user-site/src/gatsby-theme-awesome-css/styles/bio.css` |
| 103 | + |
| 104 | +```js:title=user-site/src/gatsby-theme-awesome-css/styles/bio.css |
| 105 | +.bio { |
| 106 | + border: 10px solid tomato; |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +The theme's `bio.css` file would then be replaced with your new CSS file. |
| 111 | + |
| 112 | +## Extending shadowed files |
| 113 | + |
| 114 | +In addition to overriding files, you can _extend_ shadowable files. |
| 115 | + |
| 116 | +This means that you can import the component you’re shadowing and then render it. Consider a scenario where you have a custom `Card` component that you want to wrap the author’s bio in. |
| 117 | + |
| 118 | +Without extending the component, you would have to manually copy over the entire component implementation from the theme to wrap it with your custom shadowed component. It might look something like: |
| 119 | + |
| 120 | +```js:title=src/gatsby-theme-blog/components/bio.js |
| 121 | +import React from "react" |
| 122 | +import { Avatar, MediaObject, Icon } from "gatsby-theme-blog" |
| 123 | +import Card from "../components/card" |
| 124 | + |
| 125 | +export default ({ name, bio, avatar, twitterUrl, githubUrl }) => ( |
| 126 | + <Card> |
| 127 | + <MediaObject> |
| 128 | + <Avatar {...avatar} /> |
| 129 | + <div> |
| 130 | + <h3>{name}</h3> |
| 131 | + <p>{bio}</p> |
| 132 | + <a href={twitterUrl}> |
| 133 | + <Icon name="twitter" /> |
| 134 | + </a> |
| 135 | + <a href={githubUrl}> |
| 136 | + <Icon name="github" /> |
| 137 | + </a> |
| 138 | + </div> |
| 139 | + </MediaObject> |
| 140 | + </Card> |
| 141 | +) |
| 142 | +``` |
| 143 | + |
| 144 | +This workflow isn’t too bad, especially since the component is relatively straightforward. However, it could be optimized in scenarios where you want to wrap a component or pass a different prop without having to worry about the component’s internals. |
| 145 | + |
| 146 | +## Importing the shadowed component |
| 147 | + |
| 148 | +In the above example it might be preferable to be able to import the `Bio` component and wrap it with your `Card`. When importing, you can do the following instead: |
| 149 | + |
| 150 | +```js:title=src/gatsby-theme-blog/components/bio.js |
| 151 | +import React from "react" |
| 152 | +import { Author } from "gatsby-theme-blog/src/components/bio" |
| 153 | +import Card from "../components/card" |
| 154 | + |
| 155 | +export default props => ( |
| 156 | + <Card> |
| 157 | + <Author {...props} /> |
| 158 | + </Card> |
| 159 | +) |
| 160 | +``` |
| 161 | + |
| 162 | +This is a quick and efficient way to customize rendering without needing to worry about the implementation details of the component you’re looking to customize. Importing the shadowed component means you can use composition, leveraging a great feature from React. |
| 163 | + |
| 164 | +### Applying new props |
| 165 | + |
| 166 | +In some cases components offer prop APIs to change their behavior. With component extending you can import that component and then add your new prop to change it. |
| 167 | + |
| 168 | +If `NewsletterCTA` accepts a `variant` prop which changes the look and colors of the call to action, you can use it when you extend the component. Below, `NewsletterCTA` is re-exported and `variant="link"` is added in the shadowed file to override its default value. |
| 169 | + |
| 170 | +```js:title=src/gatsby-theme-blog/components/newsletter/call-to-action.js |
| 171 | +import { NewsletterCTA } from "gatsby-theme-blog/src/components/newsletter" |
| 172 | + |
| 173 | +export default props => <NewsletterCTA {...props} variant="link" /> |
| 174 | +``` |
| 175 | + |
| 176 | +## Using the CSS prop |
| 177 | + |
| 178 | +In addition to passing a different prop to a component you’re extending, you might want to apply CSS using the [Emotion CSS prop](/docs/emotion/). This will allow you to change the styling of a particular component without changing any of its functionality. |
| 179 | + |
| 180 | +```js:title=src/gatsby-theme-blog/components/newsletter/call-to-action.js |
| 181 | +import { NewsletterCTA } from "gatsby-theme-blog/src/components/newsletter" |
| 182 | + |
| 183 | +export default props => ( |
| 184 | + <NewsletterCTA |
| 185 | + css={{ |
| 186 | + backgroundColor: "rebeccapurple", |
| 187 | + color: "white", |
| 188 | + boxShadow: "none", |
| 189 | + }} |
| 190 | + {...props} |
| 191 | + /> |
| 192 | +) |
| 193 | +``` |
| 194 | + |
| 195 | +**Note:** For this approach to work NewsletterCTA has to accept a `className` property to apply styles after the CSS prop is transformed by the Emotion babel plugin. |
| 196 | + |
| 197 | +```js:title=src/gatsby-plugin-theme-ui/index.js |
| 198 | +import theme from "gatsby-plugin-theme-ui/src" |
| 199 | + |
| 200 | +const { colors } = theme |
| 201 | +export default { |
| 202 | + ...theme, |
| 203 | + colors: { |
| 204 | + ...colors, |
| 205 | + primary: "tomato", |
| 206 | + }, |
| 207 | +} |
| 208 | +``` |
| 209 | + |
| 210 | +This provides a nice interface to extend an object if you want to change a couple values from the defaults. |
| 211 | + |
| 212 | +## How much shadowing is too much shadowing? |
| 213 | + |
| 214 | +If you find yourself shadowing a large number of components in a particular theme, it might make sense to fork and modify the theme instead. The official Gatsby themes support this pattern using a set of `-core` themes. For example, `gatsby-theme-blog` relies on `gatsby-theme-blog-core` so you can fork `gatsby-theme-blog` (or skip it completely) to render your own components without having to worry about dealing with any of the page creation or data sourcing logic. |
0 commit comments