DEV Community


Posted on • Updated on

Gatsby theme shadowing for beginners: How I built a starter for LekoArts’ Minimal Blog theme

GitHub logo ekafyi / gatsby-starter-minimal-blog-lekoarts-unofficial

Unofficial starter site for @lekoarts /gatsby-theme-minimal-blog

LekoArts’ Minimal Blog — Unofficial Starter

This is the “unofficial” starter site for @lekoarts/gatsby-theme-minimal-blog that adds modifications to the Blog page.

Comparison between original theme and this starter site

More information about the theme:

💡 If you have never used Gatsby before, head to their quick start guide or beginner-friendly tutorials. If you are familiar with Gatsby but not familiar with themes, see their Themes reference guide.


Use this starter to create a new site in the directory you specify.

# create a new site at "YOUR-SITE-DIRECTORY"
gatsby new YOUR-SITE-DIRECTORY ekafyi/gatsby-starter-minimal-blog-lekoarts-unofficial
# go to your directory
# start your site
gatsby develop

If adding to an existing site, follow the regular theme installation instruction, then copy this starter’s src directory content to your site’s src directory.

Note: If you encounter ERROR #85923 GRAPHQL, run SHARP_IGNORE_GLOBAL_LIBVIPS=true yarn in your directory as discussed in this comment.


I’m back to playing with Gatsby after a few months away!

I was looking at Gatsby blog themes for my perpetually unfinished personal site. I love the simplicity of LekoArts’ Minimal Blog theme, but there were things I wanted to modify. For instance, I wanted to display posts grouped by year in the Blog page. I also wanted to show list of tags in the Blog and Tag pages instead of in a separate page.

Fortunately, Gatsby’s shadowing feature enables me to achieve what I want in a simple way. I can “shadow” relevant component files—that is, override the files from this site’s src/@lekoarts/gatsby-theme-minimal-blog directory—so I can make my modifications without having to fork the theme and modify the theme files directly. Neat!

If you’d like to try the starter (or the theme itself), check out the repository above. But if you’ve never “shadowed” a theme before and are curious about how it works from a (relatively) beginner’s perspective, I’m going to outline the steps I take to shadow the theme in this post.

1. Create shadowing directory

Shadowed theme directory lives in your site’s src/THEME-NAME. All files and subdirectories are relative to that directory.

Example: To shadow a theme called gatsby-theme-blog (Gatsby’s official blog theme), we create src/gatsby-theme-blog.

Now find the file. To shadow src/components/bio-content.js, in our site we create src/gatsby-theme-blog/components/bio-content.js.

So, for this theme, I create src/@lekoarts/gatsby-theme-minimal-blog in my site directory.

├── src
│   ├── @lekoarts
│   │   └── gatsby-theme-minimal-blog # empty for now
# ... and other stuff


2. Find the files to shadow

I know what I’d like to do (ie. display posts by year, show all tags) but I don’t know yet where to do those because I haven’t seen the code yet. So now I delve into the theme source code to find relevant files.

I want to modify the Blog page, so I look at the Blog component (source). There, I find the list of posts is rendered by a component called Listing (source). I also want to modify the Single Tag page (page displaying posts with a specific tag), which I’ll do in the Tag component (source).

To show posts by year, I have to modify:

  • components/listing.tsx
    • I have two choices here: either shadow this component or create a new one. I go with the latter, creating my own ListingByYear component based on Listing, because it provides better flexibility—eg. for the Home page I can still use the default component, and display posts by year only on the Blog page.
  • components/blog.tsx
    • I have to shadow this to render posts with ListingByYear instead of Listing .

To show all tags in Blog and Tag pages, I have to modify:

  • components/blog.tsx
  • components/tag.tsx
    • In both pages I have to do these: (1) get list of all tags, and (2) render them under the title, before the posts list.


3. Copy and modify the files

I copy the theme files above into my site, which now looks like this:

├── src
│   ├── @lekoarts
│   │   ├── gatsby-theme-minimal-blog
│   │   │   ├── components
│   │   │   │   ├── blog.js
│   │   │   │   ├── listing-by-year.js # Content copied from listing.tsx
│   │   │   │   ├── tag.js
│   │   │   │   └── tags-list.js
│   │   │   └── texts
│   │   │       └── hero.mdx # To customize Home Hero text
# ... and other stuff

As you see, there are two files that are not in the theme’s original source code, listing-by-year (like the original listing but grouped by year) and tags-list (I make the tags list a separate component that can be imported from Blog and Tag—or anywhere else).

I don’t have to put those “non-shadowing” files there—I could, for instance, put them in src/components or elsewhere—but I prefer to put it in the shadowing directory because:

  • The import path is more simple, eg. import ListingByYear from "./listing-by-year" in the Blog component.
  • These components render data queried and processed by the Minimal Blog theme; hence I feel it’s reasonable to put the components in its shadowing directory.

The rest is writing code as usual, like with any regular Gatsby site. I create two other directories, hooks and utils to keep the component code tidy.

If you’re wondering what the usePostTags hook (source) is for: It makes use of Gatsby’s static query to get the tags data without dealing with each page templates’ GraphQL query. That way, we can import and use it anywhere (eg. display it in a sidebar or footer). Why not run that query in the TagsList component? It is possible, but I choose to keep the TagsList component presentational so it’s more versatile, eg. I can reuse it if I’m adding a “Notes” section that also has tags. This is personal preference unrelated to theming or shadowing.


  • In this site I only override files, but we may also extend rather than override files. In the shadowing file, we import the component we want to modify and render it with our modification (eg. with different props, extra child content, etc). See:
  • Likewise, we can import any components from the theme using the package name and directory path. For example, this is how the Layout component is imported to the Blog component.
    • In the theme: import Layout from "./layout" (source)
    • In my shadowing file: import Layout from "@lekoarts/gatsby-theme-minimal-blog/src/components/layout" (source)


Themes provide a new approach to developing and working with Gatsby. It helps us focus on our content first and foremost, while also allowing us to gradually dive into the source code and make our modifications in parts that we actually need. Need to manipulate how the data is displayed in the UI (like me in this example)? Just work on that part, no need to install dependencies for eg. type checking or theming.

Shadowing a theme may appear complicated, but anyone who can modify a regular Gatsby site will be able to do it. It’s also a fun way to learn things you might otherwise not be able to figure out on your own yet, as I personally experienced when shadowing this theme. Gatsby’s documentation provides comprehensive information to get you started. If you are looking for themes for inspiration, check out this unofficial theme list—although I really just search by keyword, eg. “gatsby theme mdx blog”.

Have you been working with Gatsby themes? Post your links in the comments!

Top comments (2)

sidrsingh profile image

Hi there, I tried this but soon I ran into errors.
Here is what I wanted to achieve. I wanted to modify the Layout.js available here.
The modification I wanted is:
In line 47 you see the <Switcher /> getting rendered. But in my website, I wanted the Switcher to be removed. So this is what I wrote in my Layout.js

import React, { useLayoutEffect } from 'react';
import Layout from 'gatsby-theme-carbon/src/components/Layout';
const customLayout = (props) => {
  const is404 = children.key === null;

  useLayoutEffect(() => {
    // eslint-disable-next-line global-require
    const scroll = require('smooth-scroll')('a[href*="#"]', {
      speed: 400,
      durationMin: 250,
      durationMax: 700,
      easing: 'easeInOutCubic',
      clip: true,
      offset: tabs ? 112 : 64,
    return scroll.destroy;
  }, [tabs]);

  return (
      <Header />
      <LeftNav homepage={homepage} is404Page={is404} theme={theme} />
      <Container homepage={homepage} theme={theme}>
        <Footer />
export default customLayout;
Enter fullscreen mode Exit fullscreen mode

This is the error that I am getting:

   4:17  error    'children' is not defined                                                                                                                                                                                                           no-undef
   6:3   error    React Hook "useLayoutEffect" is called in function "customLayout" which is neither a React function component or a custom React Hook function                                                                                       react-hooks/rules-of-hooks
  14:15  error    'tabs' is not defined                                                                                                                                                                                                               no-undef
  17:6   warning  React Hook useLayoutEffect has an unnecessary dependency: 'tabs'. Either exclude it or remove the dependency array. Outer scope values like 'tabs' aren't valid dependencies because mutating them doesn't re-render the component  react-hooks/exhaustive-deps
  17:7   error    'tabs' is not defined                                                                                                                                                                                                               no-undef
  21:8   error    'Meta' is not defined                                                                                                                                                                                                               react/jsx-no-undef
  22:20  error    'titleType' is not defined                                                                                                                                                                                                          no-undef
  23:20  error    'pageTitle' is not defined                                                                                                                                                                                                          no-undef
  24:26  error    'pageDescription' is not defined                                                                                                                                                                                                    no-undef
  25:23  error    'pageKeywords' is not defined                                                                                                                                                                                                       no-undef
  27:8   error    'Header' is not defined                                                                                                                                                                                                             react/jsx-no-undef
  28:8   error    'LeftNav' is not defined                                                                                                                                                                                                            react/jsx-no-undef
  28:26  error    'homepage' is not defined                                                                                                                                                                                                           no-undef
  28:61  error    'theme' is not defined                                                                                                                                                                                                              no-undef
  29:8   error    'Container' is not defined                                                                                                                                                                                                          react/jsx-no-undef
  29:28  error    'homepage' is not defined                                                                                                                                                                                                           no-undef
  29:45  error    'theme' is not defined                                                                                                                                                                                                              no-undef
  30:10  error    'children' is not defined                                                                                                                                                                                                           no-undef
  31:10  error    'Footer' is not defined                                                                                                                                                                          
Enter fullscreen mode Exit fullscreen mode
lestgabo profile image
Lestley Gabo • Edited

Thanks! This was helpful. I got confused how to import from shadow because the Gatsby documentation was misleading.
Here it says

In this case, the file to shadow is gatsby-theme-blog/src/components/bio.js.

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

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 
and importing shadowed files.

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:

It says to create a file named user-site/src/gatsby-theme-blog/components/bio.js. But it should be user-site/gatsby-theme-blog/src/components/bio.js instead or at least just make sure the path to the node_module is correct. In which case, your blog implemented the shadow importing correctly and with an example. Nice!