DEV Community

Cover image for Single-source-of-truth routing with GatsbyJS static site generator
Christian Kreiling
Christian Kreiling

Posted on • Originally published at christian-kreiling.com

Single-source-of-truth routing with GatsbyJS static site generator

The beauty of Gatsby

Gatsby is a beautiful tool for building static sites with modern tools.
It leverages the component architecture of React, and cleverly server-side renders
your site at build-time to generate a static website, ready to go.

All you have to do is export a component from any *.js file in the auto-generated src/pages directory in your project, and it'll create a route when it builds your static website based on the name of that file. That alone is enough
to get a static website off the ground in no time!

Gatsby also supplies a slew of build hooks you can write functions for in order to generate pages, GraphQL nodes, wrap your app with a component, and just about everything in between.

The problem with Gatsby

When creating a website, it's nice to have a single source of truth when creating
routes. Endpoints are the backbone of the internet, and managing them in a
quickly growing application can become tiresome if there isn't a single module that you can point to for truth.

With regards to routing, Gatsby's biggest strength is also its biggest weakness;
the src/pages directory, as magical as it sounds, supplies no means of referencing the paths that are built based on the filenames. That means in order to link to another page in your site, you have to hard code the reference as a string to another page. Gross!

My solution

If you want to just peak at the final solution, here's a link to the repo.

We're going to use Gatsby's createPages build hook in order to create our routes. Gatsby uses Redux under the hood in order to maintain state, and passes bound action creators to all of its build hooks. We'll use the createPage action creator within the creatPages build hook. I'll walk you through every step, starting with generating a fresh static site with Gatsby's build tool.

If you haven't installed it, run the following in your command line:

npm install -g gatsby

Generate the website

This is the easy part! To create a new Gatsby site in the gatsby-routing directory, run the following command:

gatsby new gatsby-routing

Setting up the routes

Let's create a directory src/routes, and move index.js and page-2.js
from src/pages to the new routes directory. Rename index.js to home.js.
After that, create a new file called index.js, and put the following code
inside it:

// src/routes/index.js
const path = require('path')

const routes = {
  home: {
    path: '/',
    component: path.resolve(__dirname, 'home.js')
  },
  page2: {
    path: '/page-2',
    component: path.resolve(__dirname, 'page-2.js')
  }
}

export { routes }

We have to use Node APIs like path instead of ES6 import because we'll be
requiring this file in Gatsby's gatsby-node.js file. Let's set these routes up
so they're ready to render - open gatsby-node.js and place the following
code inside.

const { routes } = require('./src/routes')

exports.createPages = ({ actions }) => {
  const { createPage } = actions
  Object.keys(routes).forEach(route => createPage(routes[route]))
}

So in createPages we use the createPage action creator to make new pages,
using the objects from the routes object that we imported. createPage takes
an object with a path string and a React component to render for that path,
and an optional context that you can pass to the React component as props.

Run gatsby develop from the root of your project to see the effects!

Converting links to reference our routes

If you look inside the components in the src/routes directory, you'll notice
we're still hard-coding paths in our links. Let's fix that now!

First, let's unnest our routes and create an object with the same keys as our
routes, but with just the path strings as their values. Your src/routes/index.js
should now look like this:

// src/routes/index.js
const path = require('path')

const routes = {
  home: {
    path: '/',
    component: path.resolve(__dirname, 'home.js')
  },
  page2: {
    path: '/page-2',
    component: path.resolve(__dirname, 'page-2.js')
  }
}

// Same keys as 'routes', but the value is only the path.
const paths = Object.keys(routes).reduce((acc, route) => {
  acc[route] = routes[route].path
  return acc
}, {})

export { routes, paths }

Now let's import our new paths object, and use it to link around our Gatsby
site!

// src/routes/home.js
import React from 'react'
import { Link } from 'gatsby'

import Layout from '../components/layout'

import { paths } from './'

const IndexPage = () => (
  <Layout>
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <Link to={paths.page2}>Go to page 2</Link>
  </Layout>
)

export default IndexPage


// src/routes/page-2.js
import React from 'react'
import { Link } from 'gatsby'

import Layout from '../components/layout'

import { paths } from './'

const SecondPage = () => (
  <Layout>
    <h1>Hi from the second page</h1>
    <p>Welcome to page 2</p>
    <Link to={paths.home}>Go back to the homepage</Link>
  </Layout>
)

export default SecondPage

And just like that, we're no longer hard-coding our site's paths in any of
our components! Now creating a new route is as easy as creating a new component,
and adding it to src/routes/index.js.

Wrap-up

If you're new to JavaScript, or want to learn React, Gatsby is a great place to
start. I'm in the process of building a large application, and can't afford to
be hard-coding URLs into my app. I hope that this helps someone else that's in
a similar situation!

This is my first official blog post, I'd love some feedback :)

Oldest comments (1)

Collapse
 
haavardb profile image
Håvard Bergersen • Edited

Nice :) Note that

export { routes, paths }

should be

module.exports = { routes, paths }