Using and customizing official Gatsby themes from scratch without starter sites
Tags: gatsby, react
Published: 2019-07-07 17:38:45

A Gatsby **theme** is a _reusable block of a Gatsby site_ that can be _shared, extended, and customized_ ([source](https://youtu.be/SV2zu9FvUTw?t=406)). It enables us to separate functionalities of our site to share, reuse, and modify in multiple sites in a modular way.

Early this week, Gatsby theme was [announced stable](https://www.gatsbyjs.org/blog/2019-07-03-announcing-stable-release-gatsby-themes/)! They have two official themes, the [blog theme](https://www.npmjs.com/package/gatsby-theme-blog) and the [notes theme](https://www.npmjs.com/package/gatsby-theme-notes). They also have three starter sites ([gatsby-starter-blog-theme](https://github.com/gatsbyjs/gatsby-starter-blog-theme), [gatsby-starter-notes-theme](https://github.com/gatsbyjs/gatsby-starter-notes-theme), and [gatsby-starter-theme](https://github.com/gatsbyjs/gatsby-starter-theme)) to get you started using the blog theme, notes theme, and both themes together respectively.

Using a starter site is ideal if:
- You want to get started quickly
- You don’t already have an existing site

However, I’d like to set up a Gatsby site from scratch to:
- get a better idea how the themes work, and
- see the minimum possible modifications to run a site

Follow along as I create a site, add the themes, add my own content and customizations! You can find the code for this post on [my Github under the `using-official-themes-without-starter` branch](https://github.com/ekafyi/eka-personal-site/tree/using-official-themes-without-starter). 

**Table of contents**
1. Create a Gatsby site
2. Install themes
3. Modify theme options and metadata
4. Add Markdown content and avatar image
5. Shadow layout and bio components
6. Customize the styles

⚠️ __Note:__ This post describes my _personal_ experience and perspective using the official themes for the first time. If you want to learn Gatsby themes, it’s a good idea to start from their [docs](https://www.gatsbyjs.org/docs/themes/using-a-gatsby-theme/) and [tutorial](https://www.gatsbyjs.org/tutorial/using-a-theme/).

---

## 1) Create a Gatsby site

I do this by manually creating a minimal `package.json` file in my root folder, then running `yarn install`. You may also use any regular, non-theme starter site such as [gatsby-starter-hello-world](https://github.com/gatsbyjs/gatsby-starter-hello-world) if you prefer.

```js
{
  "name": "eka-personal-site",
  "private": true,
  "description": "Personal site of @ekafyi",
  "version": "0.1.0",
  "license": "MIT",
  "scripts": {
    "build": "gatsby build",
    "develop": "gatsby develop",
    "start": "npm run develop",
    "serve": "gatsby serve",
  },
  "dependencies": {
    "gatsby": "^2.13.4",
    "react": "^16.8.6",
    "react-dom": "^16.8.6"
  }
}
```


## 2) Install themes 

We are installing _two_ official themes, `gatsby-theme-blog` and `gatsby-theme-notes`. 

We do it the same way we install any regular Gatsby plugin; first we install the theme packages by running `yarn add gatsby-theme-blog gatsby-theme-notes`.

Next, we add it to the `plugins` array in `gatsby-config.js`. I’m creating a new file as I’m starting from scratch; if you do this in an existing site, your config file would look different from mine. The exact content does not matter, as long as we add our themes in the `plugins` like so:

```js
// gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `gatsby-theme-notes`,
      options: {}
    },
    {
      resolve: `gatsby-theme-blog`,
      options: {}
    }
  ],
  siteMetadata: {
    title: `Ekaʼs Personal Site`
  }
};
```

As you can see, I start with the most barebones config. I only have `title` in my metadata and I have not modified any options yet. Let’s do that in the next step.


## 3) Modify theme options and metadata

How do we know what options are available to modify? I peek around and find _two places_ where we can find that information:

1. Published theme packages
2. Theme files in `node_modules`

At the time of writing, none of the three theme-specific starter sites provide exhaustive theme options list.

#### 3a) Modify blog theme options

We can see the following theme options in the [gatsby-theme-blog package README](https://www.npmjs.com/package/gatsby-theme-blog#theme-options):
- `basePath`
- `contentPath`
- `assetPath`
- `mdx`

Let’s say we’d like to change the blog posts folder, from the default `/content/posts` to `/content/writing`. We can do so by passing `contentPath` to the theme’s  `options`.

```js
// gatsby-config.js
module.exports = {
  plugins: [
    // gatsby-theme-notes
    {
      resolve: `gatsby-theme-blog`,
      // Default options are commented out
      options: {
        // basePath: `/`, // Root url for all blog posts
        contentPath: `content/writing`, // Location of blog posts
        // assetPath: `content/assets`, // Location of assets
        // mdx: true, // Configure gatsby-plugin-mdx
      }
    }
  ],
  // siteMetadata
};
```

The theme’s README also contains an [additional configuration](https://www.npmjs.com/package/gatsby-theme-blog#additional-configuration) section that describes what `siteMetadata` items are supported. I duly updated my config’s `siteMetadata` with my name, site description, and social links. 

#### 3b) Modify notes theme options

As with the blog theme, we can find the theme options in the [gatsby-theme-notes package README](https://www.npmjs.com/package/gatsby-theme-notes#options): 
- `basePath`
- `contentPath`
- `mdx`
- `homeText`
- `breadcrumbSeparator`

I’m going to modify the `homeText` into “Home” and `breadcrumbSeparator` into `»`. *(__Note:__ It turns out breadcrumbs are only for Notes in subfolders, so we will not see the breadcrumb in action in this post.)*

```js
// gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `gatsby-theme-notes`,
      // Default options are commented out
      options: {
        basePath: `/notes`, // Root url for all notes pages
        // contentPath: `content/notes`, // Location of notes content
        // mdx: true, // Configure gatsby-plugin-mdx
        homeText: "Home", // Root text for notes breadcrumb trail
        breadcrumbSeparator: "»", // Separator for the breadcrumb trail
      }
    }
    // gatsby-theme-blog
  ],
  // siteMetadata
};
```

You can see my [full `gatsby-config.js` file](https://github.com/ekafyi/eka-personal-site/blob/d3dcebfb0861b87f0f16dcc6a2744efa8285a038/gatsby-config.js) here.

#### Bonus: Theme files in `node_modules`

So far, the starter sites are well-documented in terms of theme options. What if we use unofficial themes with minimum info in the package README? 😕

We can assess the theme files either in the theme’s repository, or even quicker, in our project’s `node_modules` folder. For instance, to see the blog theme files, we can open `node_modules/gatsby-theme-blog`. There we can see the entire how the theme code actually resemble a regular Gatsby site, and what options are available.

![](https://thepracticaldev.s3.amazonaws.com/i/lnauxa4swsjxtrixabt2.png)

The screenshot above shows `node_modules/gatsby-theme-blog/gatsby-config.js`. We can see the `options` object passed into the config and used, among others, in the `gatsby-source-filesystem` plugin that looks for our content files. We also learn that if we do not define `contentPath`, then `content/posts` is used as default.

So—we have installed and modified our themes, but we don’t have any content yet. Let’s add them in the next step.


## 4) Add Markdown content and avatar image

Now we are adding our content in Markdown files. Based on the previous step, we are creating a folder called `content` in my project root with three folders in it:
- `content/writing` — contain Blog Post files
- `content/notes` — contain Notes files
- `content/assets` — I don’t know what exactly “assets” are, so I’m going to leave this empty

I’m going to do this via the command line, though you may do so elsewhere (from Finder, Windows Explorer, or your code editor).

```bash
mkdir content content/writing content/notes content/assets
```

I create a short blog post in `content/writing/hello-world.mdx` and a note in `content/notes/hello-note.mdx`. You can see my [`content` folder here](https://github.com/ekafyi/eka-personal-site/tree/c3859f3ae0b37a84d2cc12ab158ea7d0162505f4/content).

So far, we have: installed the theme, modified theme options, and added content. Is it possible to have a site running without even a `src` folder? We’re going to find out as I run the site for the first time. 

I run `gatsby develop` and got the following error:

```bash
There was an error in your GraphQL query:

- Unknown field 'childImageSharp' on type 'File'.

File: node_modules/gatsby-theme-blog/src/components/bio.js
```

I open the offending component and discover that we are required to have a PNG/JPG/GIF image file called `avatar`. 

```js
// node_modules/gatsby-theme-blog/src/components/bio.js
const bioQuery = graphql`
  query BioQuery {
    site {
      siteMetadata {
        author
      }
    }
    avatar: file(absolutePath: { regex: "/avatar.(jpeg|jpg|gif|png)/" }) {
      childImageSharp {
        fixed(width: 48, height: 48) {
          ...GatsbyImageSharpFixed
        }
      }
    }
  }
`
```

I peek at the [blog theme starter](https://github.com/gatsbyjs/gatsby-starter-blog-theme) and see that we should have the avatar image in our `content/assets` folder. I duly add a (badly, faux-artsy coloured selfie) avatar there and re-run the app. Aaaand… it works! 

![A standard blog site with the heading title “Eka’s Personal Site”, my avatar, a default bio section saying “Change me”, and one post entry titled “Hello world”. On the top right there is a toggle button with the sun icon. When the button is clicked, dark mode is activated and the toggle button now has moon icon.](https://thepracticaldev.s3.amazonaws.com/i/54tcnkec3093fvgr45b3.gif)

The site title, avatar image, and the social links correctly point to mine. I’ve got a site running without even having a `src` folder! 😯 

However, there are several issues:
- The bio text still uses the default (it’s not mentioned in the theme’s README or the [starter](https://github.com/gatsbyjs/gatsby-starter-theme) 😕)
- The `/notes` directory does exist and show my Notes content, but it’s not linked from the header navigation

Next we’re going to “shadow” the components to solve those issues.


## 5) Shadow layout and bio components

[Component Shadowing](https://www.gatsbyjs.org/blog/2019-04-29-component-shadowing/) is a technique that allows us to override a theme’s components without directly modifying or forking the theme.

Now we are going to _shadow_ three components:
- Blog theme’s bio text -> to use my own bio text
- Blog theme’s header -> to add “Notes” link to the navigation
- Note theme’s layout -> so it matches the rest of the site (ie. matches the Blog pages)

For the second and third components, I copy from the [gatsby-starter-theme](https://github.com/gatsbyjs/gatsby-starter-theme) as it seems to be the fastest way!

#### 5a) Shadow Blog theme’s bio component

I first check the [Blog theme’s `bio.js` component](https://github.com/gatsbyjs/gatsby/blob/master/themes/gatsby-theme-blog/src/components/bio.js), but turns out it renders another component called `<BioContent>`. I open [`bio-content.js`](https://github.com/gatsbyjs/gatsby/blob/master/themes/gatsby-theme-blog/src/components/bio-content.js) and yes, that’s our culprit.

Steps to shadow a theme’s file:
- Create a folder with the theme name in our `src` folder
	- *Example:* To shadow `gatsby-theme-blog`, I create the folder `src/gatsby-theme-blog`
- Create the component file in the folder above with the file/folder structure resembling the theme’s structure _after_ `src`
	- *Example:* The original file we want to shadow is `node_modules/gatsby-theme-blog/src/components/bio-content.js`. We copy `components/bio-content.js` into our theme folder from the step above. Hence our file is in `src/gatsby-theme-blog/components/bio-content.js`.

TL;DR version, relative from our project root:
- Original: `node_modules/gatsby-theme-blog/src/components/bio-content.js`
- Shadow: `src/gatsby-theme-blog/components/bio-content.js`

I create a simple file duplicating [the original `bio-content.js`](https://github.com/gatsbyjs/gatsby/blob/master/themes/gatsby-theme-blog/src/components/bio-content.js) with the Bio text changed.

```js
// src/gatsby-theme-blog/components/bio-content.js
import React, { Fragment } from "react"
export default () => (
  <Fragment>
    Personal site of Eka, front-end web developer and competitive napper.
  </Fragment>
)
```

I restart the app and now it shows my bio text. 👌🏾

#### 5b) Shadow Blog theme’s header component

For the header component, if I were to do what I did with the bio component (ie. export a new component), I’d be _overriding the entire header_.

```js
// src/gatsby-theme-blog/components/header.js
import React, { Fragment } from "react"
export default () => (
  <Fragment>
    My custom header <br/>
    The entire header is gone! 😱
  </Fragment>
)
```

![](https://thepracticaldev.s3.amazonaws.com/i/406c56sejsfhq8fhlx1i.png)

It’s _not_ what I want because for now I’m happy with the site title, dark mode toggle button (both UI and functionality), and the bio; all I want to do is *to add a link* to the Notes page.

Here we can see that *shadowing is more than just overriding a component*. We can also interact with the theme’s component, along with its original props, as needed.

As shown in [the Blog theme’s `header.js`](https://github.com/gatsbyjs/gatsby/blob/70311ec585f704f49a8718b13b95470e63dba751/themes/gatsby-theme-blog/src/components/header.js#L111), the `<Header>` component accepts `children` prop between the site title and the dark mode switch, where we can pass our content. 

Now we’re going to: (1) create the shadowing file in our site, (2) import the header component, and (3) render the header with our custom `children`.

```js
// src/gatsby-theme-blog/components/header.js
import React from "react";
import Header from "gatsby-theme-blog/src/components/header";

export default props => {
  return (
    <Header {...props}>
      <div style={{ color: "red" }}>My custom header</div>
    </Header>
  );
};
```

It works—I can add my own content without having to rewrite the entire header component! 💃🏽

![](https://thepracticaldev.s3.amazonaws.com/i/qhea03iwfh16buqpb337.png)

You can also pass props to the component (provided the component supports it). For instance, here I modify the `title` prop into “My Custom Title”.

```js
// src/gatsby-theme-blog/components/header.js
import React from "react";
import Header from "gatsby-theme-blog/src/components/header";

export default props => {
  return (
    <Header {...props} title="My Custom Title">
      <div style={{ color: "red" }}>My custom header</div>
    </Header>
  );
};
```

Here is the result.

![](https://thepracticaldev.s3.amazonaws.com/i/6rlggob8g3q297kexi5l.png)

Finally, I’m going to add a link to the Notes page with the code from [gatsby-starter-theme/header.js](https://github.com/gatsbyjs/gatsby-starter-theme/blob/master/src/gatsby-theme-blog/components/header.js). Here we use functionalities from [Theme UI](https://theme-ui.com), a theming library used by the Blog theme. In a nutshell, Theme UI’s `Styled` component and `css` prop allow us to use HTML element with the theme’s  `theme-ui` styles, for example to match the theme’s `heading` font family. `Styled` also supports the `as` prop (popularized by libraries like Emotion and Styled Component), so we can take advantage of Gatsby’s built-in routing through the `Link` component with `<Styled.a as={Link}>` (meaning: use `<Link>` component with `<a>` style).

```js
import React from "react";
import { Link } from "gatsby";
import { css, Styled } from "theme-ui";
import Header from "gatsby-theme-blog/src/components/header";

export default props => {
  return (
    <Header {...props}>
      <Styled.a
        as={Link}
        to="/notes"
        css={css({
          // styles
        })}
      >
        Notes
      </Styled.a>
    </Header>
  );
};


```

It works! You can see [the full code here](https://github.com/ekafyi/eka-personal-site/blob/4ce5943a3434dd35a2cb0fbe3040311fbd57c471/src/gatsby-theme-blog/components/header.js). 

![](https://thepracticaldev.s3.amazonaws.com/i/ged986k2l91za78606ce.png)

#### 5c) Shadow Note theme’s layout component

We already have a Notes page at `/notes` (ie. localhost:8000/notes), but it does not have the header and footer yet. That’s because this view comes from the Notes theme, separate from the Blog theme, which renders the header and footer.

Now we are going to shadow the Layout component in `src/gatsby-theme-notes/components/layout.js`, import the Blog theme’s Layout component, and wrap our content in the latter.

As with the previous step, the shadowing component in our site gets the props from the original component (ie. Notes theme’s Layout), so we can wrap the entire `props.children` (ie. Notes content) without having to rewrite anything else.

```js
// src/gatsby-theme-notes/components/layout.js
import React from "react"
import BlogLayout from "gatsby-theme-blog/src/components/layout"

export default props => <BlogLayout {...props}>{props.children}</BlogLayout>
```

Restart the app, and voila, the Blog theme layout (header and footer) now applies to the Notes section, too!

![](https://thepracticaldev.s3.amazonaws.com/i/1njjptd29ox7888k1tha.png)
<figcaption>Before (left) and after (right)</figcaption>


## 6) Customize the styles

Unless you happen to like the theme’s default purple, in all likelihood you would want to modify your theme-powered site’s visual styles such as colours and typography.

The Blog theme uses the theming library we discussed briefly above, Theme UI. Theme UI itself works as a “theme plugin” that exports a `theme` object from [`gatsby-theme-blog/src/gatsby-plugin-theme-ui`](https://github.com/gatsbyjs/gatsby/tree/master/themes/gatsby-theme-blog/src/gatsby-plugin-theme-ui). Check out [Theme UI’s docs](https://theme-ui.com/getting-started) to read more about the `theme` object.

The Blog theme breaks down the `theme-ui` object into separate files (colors, components, etc) that are imported in the [`gatsby-plugin-theme-ui` index file](https://github.com/gatsbyjs/gatsby/blob/master/themes/gatsby-theme-blog/src/gatsby-plugin-theme-ui/index.js). Accordingly, if we only want to customize the colors, we can shadow the `colors.js` file, and so on.

We customize the styles by shadowing the `gatsby-plugin-theme-ui` file(s) the same way we shadow any other components. To shadow `node_modules/gatsby-theme-blog/src/gatsby-plugin-theme-ui/colors.js`, for example, we take the part after `src` (`gatsby-plugin-theme-ui/colors.js`) and put it in our shadowing folder, `src/gatsby-theme-blog`. Thus, we create our file at `src/gatsby-theme-blog/gatsby-plugin-theme-ui/colors.js`. 

Now we’re going to modify the colors, using the [Blog theme starter’s file](https://github.com/gatsbyjs/gatsby-starter-blog-theme/blob/master/src/gatsby-theme-blog/gatsby-plugin-theme-ui/colors.js) as reference. As we don’t want to replace _all_ the colors, we import the theme’s default theme colors and merge them with our modified colors. We also import [lodash’s `merge`](https://lodash.com/docs/4.17.11#merge) to deep-merge the style objects. It’s not required but it helps us do the deep merge; we may omit it if we want to code the deep merge ourselves OR if we don’t need to merge with the default theme (ie. we rewrite the *entire* theme styles).

```js
// src/gatsby-theme-blog/gatsby-plugin-theme-ui/colors.js
import merge from "lodash.merge";
import defaultThemeColors from "gatsby-theme-blog/src/gatsby-plugin-theme-ui/colors";

export default merge({}, defaultThemeColors, {
  text: "rgba(0,0,0,0.9)",
  primary: "#0e43c5",
  background: "#fff1c1",
  modes: {
    dark: {
      text: "rgba(255,255,255,0.9)",
      primary: "#f7e022",
      background: "#151f48"
    }
  }
});
```

Other theme styling attempts:

- `gatsby-plugin-theme-ui/typography.js`
	- Result: ✅❌ Partial success. I could change `fonts.body` from the default *Merriweather* typeface to *system-ui*, but I could not change `fonts.heading`. It’s likely because the `fonts.heading` value is [overridden into *Montserrat* in `gatsby-plugin-theme-ui/index`](https://github.com/gatsbyjs/gatsby/blob/master/themes/gatsby-theme-blog/src/gatsby-plugin-theme-ui/index.js#L11). Which brings us to…
- `gatsby-plugin-theme-ui/index.js`
	- Result: ❌ Fail. My shadowing `index.js` does not seem to get detected. I test by running `console.log(‘Hello’)`, which did not get printed.
- `gatsby-plugin-theme-ui/styles.js`
	- Result: ✅ Success! I modify the hover link style to add underline and use the `secondary` color.

You can see [those three files here](https://github.com/ekafyi/eka-personal-site/tree/5a93a9175bdcfa8164b93eeac58d167cd4640d31/src/gatsby-theme-blog/gatsby-plugin-theme-ui).

**Note about theme order:** If multiple themes use `theme-ui`, the *last theme* specified in the `plugins` array in our `gatsby-config.js` wins.

This is the end result of the steps in this post.

![](https://thepracticaldev.s3.amazonaws.com/i/jtf0b0kcfq6j2t3a5nms.gif)

---

## Conclusion

Here are my impressions after trying the official themes.

- Themes help you get started building a simple, basic Gatsby site quickly without even needing a `src` folder. More advanced users can take advantage of themes to create modular, extendable, composable blocks of their site (though I have not personally got to this point). 
- The official themes are a good place to start using, modifying (through shadowing), and dissecting themes.
- The difficulty level of using and shadowing themes highly depends on the theme’s documentation, eg. what options are available, what data are required.

Do you have examples of non-official themes that you build and/or use? Let me know in the comments!

Next stop, learn to do more advanced customizations and/or build my own theme. Thanks for reading, til next post! 👋🏾