Picture of a dog in a moving box

How to move your Gatsby blog to a sub folder

Based on the title of this article, you are likely expecting one of two things:

  1. How to host your Gatsby blog from a sub folder of an existing site.
  2. How to rearrange the pages and folders within an existing Gatsby site.

This blog post will cover option number two, and I will keep option number one as a potential future article. If you need information on that topic now, I suggest researching Gatsby’s pathPrefix configuration as a good place to start.

How to rearrange your Gatsby files and folders

Moving the files and folders in a Gatsby site requires a little knowledge of how Gatsby creates pages and requires you to consider what other changes may be needed as a result of your moves.

How to create pages in Gatsby

With Gatsby there are 2 primary ways to create pages on a site: adding pages to the pages folder and with the gatsby-node.js file.

The first, and most easily understood, way to create pages with Gatsby is using the pages folder. Gatsby will create a web page for each file in the pages folder. This is best suited to pages that are individual or unique. A good example would be an About page, or a 404 page. This also works with sub-folders. If you want a page at /pets/whiskers then simply create a file at /pages/pets/whiskers.

The second, and more powerful, way to create web pages, is the gatsby-node.js file. In this file, more advanced page creation takes place that can query various data structures and build pages based on the data it retrieves. For example, on a blog site, this is typically where the blog posts would be created.

Time to rearrange things

Now that I’ve explained the basics of how Gatsby creates pages, I’m going to walk though a move. I want to expand my site to be more than just a blog, and include different areas, but since I used a blog starter, the blog index is located at the root of my site. Now I want the index to be at the /blog URL and for a home page to be at the root. To achieve that, I will need to do a little rearranging.

This is the current structure of my site

/index.html   - home, which is the blog index page showing the most recent blog posts
/archive.html - the blog archive page where you can see links to all the blog posts
/404.html     - the 404 page.
/blog/        - all blog pages are stored under the blog sub-folder

What I want the structure to be:

/index.html         - new home page with intro, etc. (coming later - using blog index for now)
/404.html           - no changes to the 404 page location
/blog/index.html    - move the blog index under the blog sub-folder
/blog/archive/html  - move the blog archive under the blog sub-folder
/blog/              - no changes to the blog articles.

In simple terms - I need to move the index and archive files under the blog sub-folder and create a new root index page.

Beware! When moving pages, consider more than the moving of the pages themselves, but also any references to those pages. The most important references are navigation links and any external links we may have created that point to our site. Moving a page without addressing these issues results in broken links and images.

Moving the pages
Since my Sanity.io CMS, already has my blog entries separated in its own structure called ‘Blog Posts’, I don’t need to make any changes to Sanity. This this will be a Gatsby only change.

Also, since my blog posts are already written to the blog sub-folder on my site, my blog posts don’t need any changes either, which means I won’t need to update my gatsby-config where the blog posts are created.

The leaves only two pages that I need to move to the blog sub-folder: the Blog index and Blog archive. As these files are in the /pages folder, to move them I simply need to to create a new /pages/blog sub-folder and then move these files there. I will then need to have a new index page at the root of my site to represent my home page. For now I’m going to leave a copy of the blog index there so the page is not blank, and I will come back to add a new homepage at a later time.

Once I copy the index.js to /blog/index.js, I need to make some changes to the /blog/index.js. The first thing I need to do is to rename the graphql query. Gatsby requires that all graphql queries within a project have a unique name, and so if I do not change the name I will get a compile error as I will have two pages that share a query name.

Current index query

query IndexPageQuery {

I change that to:

query BlogIndexPageQuery {

The next thing I need to check is my imports. Since I have moved this file down a directory, I will need to change my imports accordingly.

Note: VSCode will often see you doing this and ask if you want to update the references. While this is a nice feature and will catch most of the needed changes, you should double check to be sure it is correct.

My references before:

import React from 'react' 
import {graphql} from ‘gatsby’
import {
  mapEdgesToNodes,
  filterOutDocsWithoutSlugs,
  filterOutDocsPublishedInTheFuture
} from ‘…/lib/helpers’
import BlogPostPreviewList from ‘…/components/blog-post-preview-list’
import Container from ‘…/components/container’
import GraphQLErrorList from ‘…/components/graphql-error-list’
import SEO from ‘…/components/seo’
import Layout from ‘…/containers/layout’

My new references:

import React from ‘react’
import {graphql} from ‘gatsby’
import {
  mapEdgesToNodes,
  filterOutDocsWithoutSlugs,
  filterOutDocsPublishedInTheFuture
} from ‘…//lib/helpers’
import BlogPostPreviewList from ‘…//components/blog-post-preview-list’
import Container from ‘…//components/container’
import GraphQLErrorList from ‘…//components/graphql-error-list’
import SEO from ‘…//components/seo’
import Layout from ‘…//containers/layout’

At this point I want to move the archive.js page to the blog directory and I will need to make the same references changes I did for the blog/index.js page (I won’t list these here). I do not need to change the query name as I moved this file, so there is no duplicate query name on the site.

This move will cause an issue, however: it will break the Archive link in our nav menu, which is a good segue into the next changes I need to make.

External links and Nav changes
Now that the pages are moved, it’s time to address any external links or nav changes that are needed. External links are not an issue, fortunately, as I am not changing the URL of any of my actual blog posts since they are not moving. If I were changing these, then I would need to set up redirects for any external links to prevent them from breaking.

For the my nav menu, I want to add a new “Blog” link, and then I want to make it so that the “Archive” link only shows when the user is in the blog area of the site.

Edit the Nav menu

There are 4 changes I need to make:

  1. Add a new nav menu item called “Blog” that links to the blog root.
  2. Change the archive menu item to point to /blog/archive instead of /archive.
  3. Change any other links to /archive on the site to point to /blog/archive
  4. Make the archive link only show on pages under the /blog structure (not on the home page or other sections of the site that don’t yet exist).

1. Add Blog to the nav menu

Start by finding where in the code your nav is located. Most Gatsby sites will use a layout component and will have a header component inside the layout. That is the case for my blog starter - the file I need to edit is /web/src/components/header.js

This is the file to begin with:

import {Link} from ‘gatsby’
import React from ‘react’
import Icon from./icon’
import {cn} from ‘…/lib/helpers’
import styles from./header.module.css’

const Header = ({onHideNav, onShowNav, showNav, siteTitle}) => (
  <div className={styles.root}>
    <div className={styles.wrapper}>
      <div className={styles.branding}>
        <Link to='/'>{siteTitle}</Link>
      </div>
  
      <button className={styles.toggleNavButton} 
        onClick={showNav ? onHideNav : onShowNav}> 
        <Icon symbol='hamburger' /> 
      </button> 
      
      <nav className={cn(styles.nav, showNav && styles.showNav)}> 
        <ul> 
          <li> <Link to='/archive/'>Archive</Link> </li> 
        </ul> 
      </nav> 
    </div>
  </div>
) 

export default Header

1. Add a new nav menu item called “Blog” that links to the blog root

<li>
  <Link to='/blog/'>Blog</Link>
</li>

2. Change the archive menu item to point to /blog/archive instead of /archive

<Link to='/blog/archive'>Archive</Link>

3. Change any other links to /archive on the site to point to /blog/archive

I have a "browse more" link at the bottom of my blog index page that links to the archive page.

I had to make a small change to /web/pages/blog/index.js

<BlogPostPreviewList
  title='Latest blog posts'
  nodes={postNodes}
  browseMoreHref='/blog/archive/'
/>

4. Make the archive link only show on pages under the /blog structure (not on the home page or other sections of the site that don’t yet exist).

This one is a little trickier ... There are several ways to do it. Here are a few ideas (not a comprehensive list):

  • Create a separate layout for the blog pages and have that layout pass a prop to the header component telling it to show the archive link.
  • Create a separate header component and the layout determines which header to call based on the URL path.
  • Create a conditional in the header that shows the archive link based on the URL path.

None of these option are inherently better than the others. The strategy you choose should be dependent on the site structure and needs of your site. For example, if different sections of your site will have different layouts, each with a different Nav menu, then I would lean towards creating a different Blog header with the correct nav menu. In my situation, everything will be similar across my site, therefore I don’t want a separate blog header just to handle a small variation. Instead, I’m choosing to put a conditional on the archive link that is based on the URL.

The first thing I need to do is programmatically identify the current page. Once I know that, I can add the conditional statement around the Archive Link.

To determine the page, I pull the location info from @reach/router.


Start with an import

import { globalHistory as history } from ‘@reach/router’

Next get the location from history by defining a const. I am going to change our component a little to support this by adding a return block (that is currently implied by the syntax).

Current structure:

const Header = ({onHideNav, onShowNav, showNav, siteTitle}) => (
... )

New:

const Header = ({onHideNav, onShowNav, showNav, siteTitle}) => {
  return (
  ... ) 
}

This is a little more verbose but it allows me to add an additional const in a clean, easy to understand way.

const Header = ({onHideNav, onShowNav, showNav, siteTitle}) => {
  const { location } = history
  return (
  ... )
}

My location object now has a pathway that contains the path without the url. I can easily use that to determine if the Archive link should be shown.

Here is complete code for the nav area of my new header.js file:

<nav className={cn(styles.nav, showNav && styles.showNav)}>
  <ul>
    <li>
      <Link to='/blog/'>Blog</Link>
    </li>
    {location.pathname.includes('/blog') && 
      <li>
        <Link to='/blog/archive/'>Archive</Link>
      </li>
    }
  </ul>
</nav>

Now I will only see the Archive link on pages that begin with /blog.

Conclusion

With my blog now in it's own folder, I am now free to add other areas to my site like a portfolio or a collection of epic dad jokes.