DEV Community

Aravind Balla
Aravind Balla

Posted on • Originally published at on

Single Page Apps w/ Gatsby

Gatsby, which is a static site generator, is famous for blogs and documentation websites. But there is no wrong in build full blown React apps with it.

In the case of a blog, Gatsby builds a page for every article at the build time and generates a directory which can be hosted directly. But when it comes to Single Page App (SPA), we have only one index.html at the root, and the components are loaded depending on the route that the user is in.

We can get the benefits of code spitting with some React lazy suspense features with some minimal extra work too.

const Contact = React.lazy(() => import('../components/Contact'));
const LazyContact = props => (
  <React.Suspense fallback={'<p>Loading...</p>'}>
    <Contact {...props} />

The Contact component is loaded only when it will be rendered, which is when we hit a particular route. We will have a look at the routes in a moment.

But wait

Before that, we need a gatsby-node.js file to let Gatsby know that we want all the route to end up in index.html

After cloning Gatsby’s Default starter, let’s add this file.

// gatsby-node.js

exports.onCreatePage = ({ page, actions }) => {
  const { createPage } = actions;
  if (page.path === `/`) {
    page.matchPath = `/*`;

Now we are ready.

Components for each Routes

Let’s plan to have two routes, /contact and /about . In src/components we will create basic components like this.

// src/components/Contact.js
import React from "react"

console.log("contact component")

export default function() {
  return <div>Contact Us as you like.</div>

// src/components/About.js
import React from "react"

console.log("about component")

export default function() {
  return <div>We are a great bunch of people</div>

I’ve added the console logs to check when this file is loaded. We don’t want it to load at the homepage. Rather only when the route is visited.

Main App

In the main page, which is src/pages/index.js we make use for @reach/router which Gatsby itself, uses for routing.

// src/pages/index.js

import React from 'react';
import { Router, Link } from '@reach/router';

const Contact = React.lazy(() => import('../components/Contact'));
const About = React.lazy(() => import('../components/About'));

const LazyComponent = ({ Component, ...props }) => (
  <React.Suspense fallback={'<p>Loading...</p>'}>
    <Component {...props} />

const Home = () => <h2>Hello and Welcome</h2>;

const IndexPage = () => (
    <h1>Hi people</h1>
    <Link to="/">Home</Link>
    <br />
    <Link to="/contact/">Contact</Link>
    <br />
    <Link to="/about-us">About Us</Link>
    <br />

    <input />

      <Home path="/" />
      <LazyComponent Component={Contact} path="contact" />
      <LazyComponent Component={About} path="about-us" />

export default IndexPage;

LazyComponent renders the Component we pass to it as a prop under React.Suspense with a fallback.

If you build this project and serve, you can open the Networks tab in the browser console and see that a new JS file is loaded when you hit the /contact route for the first time.

That’s it, this is all we need to make an SPA using Gatsby.

Here is a working codesandbox link -

Top comments (4)

vilinyexc profile image

Hey! Seems like the index.js in the article is missing the default export : export default IndexPage -- in case some beginner attempts to run this without success. It was present in the working codebox sample though

aravindballa profile image
Aravind Balla

Fixed it. Thanks :)

simplenotezy profile image
Mattias Fjellvang

Thanks for the article. I miss some information about how route params would be handled, and whether or not this is possible.

the_hampsta profile image
The Hampsta • Edited

Hey. I thought Gatsby went into spa mode after loading the static site if JS isnt off using React Hydration