Global State Management in Gatsby

Global State Management in Gatsby

Static websites are my favorite thing in the web development industry right now, and I don't imagine they'll ever give that position up. They're incredibly fast, secure, and they're just awesome!

Recently, I had to hook up a Gatsby site to a serverless database and authentication service. I needed to have a way to manage the state of the app, such as user info, whether or not someone is signed in, and other information.

I couldn't use Redux, because there's not a main root element with static site generators.

I ended up using Gatsby's wrapRootElement function that allowed me to effectively share state throughout every component on the site.

Here's how to effectively manage state in Gatsby.

Creating our State

In order to use our state throughout the site, the first thing we have to do is set up a state provider, using React Context.

Create a new folder at src/providers called StateProvider.js.

Inside this file, we'll use React.createContext() to create our state, and create a functional component called StateProvider that will provide our entire app with access to the context.

import  React, { useState } from  'react';

export  const  StateContext = React.createContext();

export  const  StateProvider = ({ children }) => {
  const [message, setMessage] = useState('A message!');

  return (
    <StateContext.Provider
      value={{
        message,
        setMessage
      }}
    >
      {children}
    </StateContext.Provider>
  );
};

Here's what we're doing.

First, we create a new context, called StateContext, and export it so it can be imported by other .js files.

export const StateContext = React.createContext();

Next, we define our functional component, called StateProvider.

export  const  StateProvider = ({ children }) => {
  const [message, setMessage] = useState('A message!');

  return (
    ...
  );
};

Inside of this component, we're creating the state item that we want to change and access throughout the app. In this case, it's a message that we can display and set.

const [message, setMessage] = useState('A message!');

Finally, we need to return the StateContext.Provider to the rest of the site, and wrap it around the children prop.

return (
  <StateContext.Provider
    value={{
      message,
      setMessage
    }}
  >
    {children}
  </StateContext.Provider>
)

You'll notice we're passing a value prop to the provider. This is necessary to give the rest of the site access to these values. If you don't pass these value here, they won't be accessible by the rest of the site, even if you defined them above using useState();

Using our State

After we've got our StateProvider set up, we need to wrap our site with it.

One way to do this would be to go into each page and wrap it with <StateProvider></StateProvider>.

A second way of doing this is to just wrap the entire site with it using Gatsby's wrapRootElement. This is the better option, because you only use the provider once, and the whole site receives the state.

Wrapping our Site

At the root of your project, open the gatsby-browser.js file. This is the file that Gatsby runs when the page first loads.

Inside this file, we're going to import our StateProvider and call the wrapRootElement function.

import  React  from  'react';
import { StateProvider } from  './src/providers/StateProvider';

export  const  wrapRootElement = ({ element }) => {
  return <StateProvider>{element}</StateProvider>;
};

We need to do to main things here: first, we need to access the element prop, which Gatsby passes to the wrapRootElement function when the page first loads.

The second and final thing we need to do is wrap the element prop (which represents the entire site) with the state provider and return it.

return <StateProvider>{element}</StateProvider>;

Setting and Reading State

Great! Now that we've got our state provider set up and wrapping our site, we can use it wherever we want.

For demo purposes, we'll be creating a simple messaging component that will display the current message, and allow you to set a new message as well.

In your src/pages directory, create a new file called message.js.

Inside this new file, we'll define our new page, and use our StateContext that we created earlier.

import  React, { useContext } from  "react";
import { StateContext } from  "../providers/StateProvider";
import  "../components/style.css";

const  Message = () => {
  const { message, setMessage } = useContext(StateContext);
  return (
    <div  className="wrapper">
      <h1>Current message</h1>
      <p>{message}</p>
      <label  htmlFor="set-message">Set new message</label>
      <input  type="text"  id="set-message"  placeholder="Message..."  />
      <button
        onClick={() =>  setMessage(document.getElementById("set-message").value)}
      >
        Set
      </button>
    </div>
  );
};

export  default  Message;

Basically, we import StateContext from our StateProvider. We then grab the message and the setMessage function from the context, and use those to display and update the message!

Conclusion

So, I hope you've learned something new in this post! This post only covers the very basics of state management in Gatsby, but the idea is very simple: use a global state object so React can update everything that needs to be updated when that state changes.

Thanks for reading! If you liked this article, you can check me out on Twitter, or on my website!

p.s For those of you wondering, the official reading time for this article is 4 minutes and 40 seconds 😉⏳

Written by

Jarod Peachey

Jarod Peachey is a front end developer and JAMstack enthusiast. He's the founder of The Five Minute Developer, and https://staticbox.io. It would make him happy if you followed him on Twitter. And maybe check out his website.