Avatar of Sascha Jullmann

Sascha Jullmann

Maker of gatsbythemes.com. Interested in all things web. Focuses on making fast and esthetically pleasing websites with a mobile first approach.

11 min to read · October 14, 2017

How to use Redux and Emotion in Gatsby

Emotion is currently my favorite css-in-js library and fortunately there is a gatsby-plugin for that. For a simple project that works as expected, you can just install the plugin and be done with it. You get all the goodness of emotion without needing to add anything to your babelrc, setting up server-side-rendering and so on. While I was working on Gatsbythemes.com I encountered a small limitation of gatsby. I was using the emotion plugin for gatsby and at the same time was using redux for some state management. In development mode this works without any problems. When I tried to build the site though, my store was missing and I got an error telling me that my store could not be found. I didn't know what the heck was going on. After a short investigation I found out that the gatsby-plugin-emotion was causing the problem. At least when I removed it from my "gatsby-config.js", my site was build without any errors.

You could technically use emotion in your gatsby project without the gatsby-plugin-emotion. Just install emotion, react-emotion and go to town, but this way you won't have server-side-rendering enabled and without it, your built website will render without any styles on the first render. Only after the javascript kicks in, will emotion fill the website with styles. This is not very desirable and really nasty, so I will show you here how you can set up emotion with server-side-rendering alongside redux in your gatsby-project.

What is going on?

The problem is that gatsby allows only one renderer to replace the default server renderer. In your "gatsby-ssr.js", you get to define a custom renderer using the "replaceRenderer" method. The gatsby-plugin-emotion uses this method in order to extract critical css and render the html pages with those styles. If you take a look at the "using-redux" example in the gatsby github repo you can see that a custom renderer is also defined. This is necessary in order to use redux. So following this example and using the gatsby-plugin-emotion at the same time will cause trouble. The gatsby-plugin-emotion takes over server-side-rendering and the redux store will never get to see the light of day. This is the reason that I got the error "store not found" upon building the html pages.

What to do about it?

You have to configure emotion (or any other css-in-js library) yourself. This isn't really hard to do though, we can just peek at what the plugin does and combine this with the redux example :). I created a repo in which I demonstrated how to do this and I will walk you through the example here. So you can either follow along, or clone the repo and look at the code yourself. The repo can be found here.

First, create an empty folder and initialize a node project (inside the directory type "yarn init"). Install all the necessary dependencies for a simple gatsby project.

yarn add gatsby gatsby-link

From here on, we can create our first page. Create the following folders:

mkdir -p src/pages

Add an "index.js" file in the pages folder, open it in your favorite text editor (go Vim! :D) and type the following:

#src/pages/index.js
import React from 'react';

export default () => (
  <h1>This is the home page</h1>
);

This is nothing new so far, if you have any experience using gatsby this should (obviously) be familiar. Let's add redux to the picture.

yarn add redux react-redux

Create a folder called "state". In this folder we will create a reducer function and configure our redux store.

mkdir src/state

Add an "index.js" and a "reducer.js" file. Open those and add the following:

#taken from the official redux docs :).
#src/state/reducer.js
export default function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
    }
};

#src/state/index.js
import { createStore } from 'redux';
import reducer from './reducer.js';

export default function configureStore() {
  const store = createStore(reducer);

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept('./reducer.js', () => {
	  const nextReducer = require('./reducer.js');
	  store.replaceReducer(nextReducer);
	});
  }

  return store;
}

While we are at it, we can also enable hot reloading for the reducer function :). Now we have a very simple redux store, with two actions, adding one to the current state and substracting one from the current state, with an initial state of 0. In order to use the store in our gatsby pages we need to wrap the Provider component (provided by react-redux) around our whole app, so that redux can inject the state in every component that needs it. For our gatsby project this needs to be done in two places. Once in the "gatsby-browser.js" file and once in the "gatsby-ssr.js" file. In the "gatsby-browser.js" file we are going to replace the default Router component with a modified one, which additionally wraps the Router component with the Provider component. Gatsby provides a "replaceRouterComponent" method, which we can use for this. Since we're replacing the Router and gatsby uses 'react-router', we can install 'react-router-dom' which provides a BrowserRouter component, which we can use as the Router component.

yarn add react-router-dom
#gatsby-browser.js
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import configureStore from './src/state/index.js';

exports.replaceRouterComponent = () => {
  const store = configureStore();

  const ConnectedRouterWrapper = ({ children }) => (
    <Provider store={store}>
      <BrowserRouter>{children}</BrowserRouter>
    </Provider>
  )
  return ConnectedRouterWrapper;
}

Now we can start using our redux store state and our actions in our app. We're going to create a navigation component, which lets us navigate to different pages and holds the current state of our store. We're also gonna add an about page and a counter page. On the counter page, we'll be able to count up and down. Let's start by adding the navigation component.

mkdir -p src/components/Navigation

I like to create a components folder which holds all my components and depending on the type of component another folder for that specific component. In our case we're gonna add a "Navigation" folder, which holds an "index.js" file in which our component will be created.

#src/components/Navigation/index.js
import React from 'react';
import { connect } from 'react-redux';
import Link from 'gatsby-link';

const mapStateToProps = state => {
  return { state }
};

const Navigation = ({state}) => (
  <nav>
    <div>
      <h4>
        Redux + css-in-js in Gatsby
      </h4>
    </div>
    <ul>
      <li>
        <Link to="/">
          Home
        </Link>
      </li>
      <li>
        <Link to="/about">
          About
        </Link>
      </li>
      <li>
        <Link to="/counter">
          Counter
        </Link>
      </li>
      <li>
        <h5>
          Current Count: {state}
        </h5>
      </li>
  </ul>
  </nav>
);

export default connect(mapStateToProps)(Navigation);

Now let's create a layout component, which imports the Navigation component, so that we have our Navigation on every page.

mkdir src/layouts
#src/layouts/index.js
import React from 'react';
import Link from 'gatsby-link';
import Navigation from '../components/Navigation/index.js'

const Layout = ({ children }) => (
  <div>
    <Navigation />
    <div>
      { children() }
    </div>
  </div>
);

export default Layout;

Let's check out what we got so far. Type the following in the root folder

node_modules/.bin/gatsby develop

and check it out in your browser. It's nothing spectacular so far, but we can already "see" our store with an initial count of 0. The Links don't all work yet, so let's add the remaining pages.

#src/pages/about.js
import React from 'react';

export default () => (
  <h1>This is the about page</h1>
);

#src/pages/counter.js
import React from 'react';
import { connect } from 'react-redux';

const CounterPage = ({increment, decrement}) => (
  <div>
    <h1>Here you can count up or down</h1>
    <button onClick={increment}>Up</button>
    <br/>
    <br/>
    <button onClick={decrement}>Down</button>
  </div>
);

const mapDispatchToProps = (dispatch) => {
  return {
    increment: () => dispatch({ type: 'INCREMENT' }),
    decrement: () => dispatch({ type: 'DECREMENT' })
  }
};

export default connect(null, mapDispatchToProps)(CounterPage);

Go to your browser and open the "counter" page. You should now be able to count the state up and down and see the changes directly in the navigation component.

All that is missing now in order build the website is replacing the default renderer of gatsby with a renderer that wraps our app in a Provider component. To do that, just add the following to the "gatsby-ssr.js" in the root folder of the project.

#gatsby-ssr.js
import React from 'react';
import { Provider } from 'react-redux';
import { renderToString } from 'react-dom/server';
import createStore from './src/state/index.js';

exports.replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => {
  const store = createStore();

  const ConnectedBody = () => (
    <Provider store={store}>
      {bodyComponent}
    </Provider>
  );
  replaceBodyHTMLString(renderToString(<ConnectedBody />));
}

Now you can build the project using

node_modules/.bin/gatsby build

and deploy it to the host of your choice.

Just as a demonstration, I want you to install the gatsby-plugin-emotion and see what happens.

yarn add gatsby-plugin-emotion emotion emotion-server

Create a "gatsby-config.js" file and add the following:

#gatsby-config.js
module.exports = {
  plugins: [`gatsby-plugin-emotion`],
}

Try building the site now. You should see an error, indicating that "store" can't be found. This is because the gatsby-plugin-emotion takes over server-side-rendering and hence the site can't be build with a Provider component wrapping our app. But worry no more, the solution is extremely simple.

First uninstall the gatsby-plugin-emotion and remove the "gatsby-config.js" file. We won't need it. You can leave "emotion" and "emotion-server". Now add the following packages to the project:

yarn add react-emotion babel-plugin-emotion

In order to use emotion in our gatsby project we need to modify three places. The "gatsby-node.js" file, "gatsby-ssr.js" and "gatsby-browser.js".

#gatsby-node.js
exports.modifyBabelrc = ({ babelrc }) => {
  if (process.env.NODE_ENV !== `production`) {
    return {
      plugins: [
        [require.resolve(`babel-plugin-emotion`), { sourceMap: true }],
      ].concat(babelrc.plugins),
    }
  }
  return {
    plugins: [require.resolve(`babel-plugin-emotion`)].concat(babelrc.plugins),
  }
}

Here, we're just adding the babel plugin for emotion to the babelrc. If we're in dev mode, we'll additionally activate the sourceMap option, that has been added to emotion since emotion 8.

Now let's add emotion to our "gatsby-ssr.js". The gatsby-plugin-emotion alone would extract critical css from our bodyComponent and return the resulting html. Now that we already have redux using a custom renderer we need to combine both. Open the "gatsby-ssr.js" file and replace the "replaceRenderer" method from before with:

#gatsby-ssr.js
...
import { extractCritical } from 'emotion-server';

exports.replaceRenderer = ({ setHeadComponents, bodyComponent, replaceBodyHTMLString }) => {
  const store = createStore();

  const ConnectedBody = () => (
    <Provider store={store}>
      {bodyComponent}
    </Provider>
  )

  const { html, ids, css } = extractCritical(renderToString(<ConnectedBody />))

  const criticalStyle = <style dangerouslySetInnerHTML={{ __html: css }} />
  const criticalIds = (
    <script
      dangerouslySetInnerHTML={{
        __html: `window.__EMOTION_CRITICAL_CSS_IDS__ = ${JSON.stringify(ids)};`,
      }}
    />
    )
  setHeadComponents([criticalIds, criticalStyle])
  replaceBodyHTMLString(html)
}

First we'll wrap our "bodyComponent" in a react-redux Provider and then pass the resulting component (rendered to string) to the extractCritical function from emotion. We'll also set two head components with the critical styles and Ids. We'll replace the body html string with the html string that the extractCritical function returns.

Now that we've set critical Ids, we need to make sure that emotion won't reinsert all the rules on the client. We can use the "hydrate" function from emotion for that and we're gonna call it on client entry. Open up your gatsby-browser.js file:

#gatsby-browser.js
...
import { hydrate } from 'emotion';

#leave the redux part as is, but add the following:

exports.onClientEntry = () => {
  if (
    typeof window !== `undefined` &&
    typeof window.__EMOTION_CRITICAL_CSS_IDS__ !== `undefined`
  ) {
    hydrate(window.__EMOTION_CRITICAL_CSS_IDS__)
  }
}

We are now ready to use emotion in our gatsby project and make our app beautiful :).

Let's add some styling to our Navigation component.

#src/components/Navigation/index.js
...
import { css } from 'react-emotion';

const navStyles = css`
  height: 3.5rem;
  background-color: orange;
  display: flex;
  align-items: center;
  color: white;
  padding: 0 5rem;
  & div {
    width: 100%;
  }
  & ul {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: flex-end;
    list-style-type: none;
    margin: 0;
    margin-left: auto;
    align-items: center;
  }
  & ul li {
    display: inline-block;
    margin-left: 1rem;
    > a {
      text-decoration: none;
      color: inherit;
      font-weight: 600;
    }
    > a:hover {
      text-decoration: underline;
    }
  }
  & ul li h5 {
    padding: 0.5rem;
    border: 2px solid white;
  }
`;

#add the styles to the nav element
...
<nav className={navStyles}>...
...

We can also add some global styles to our layout component.

#src/layouts/index.js
...
import { injectGlobal, css } from 'react-emotion';

injectGlobal`
  body {
    margin: 0;
  }
`;

const bodyWrapper = css`
  padding: 2rem 5rem;
`;


#add a div around our { children() } function
#and attach the bodyWrapper styles
...
<div className={bodyWrapper}>
  { children() }
</div>
...

Finally let's create some nice styles for our buttons that control our counter. Go to the "counter.js" file and add the following styles:

#src/pages/counter.js
...
import { css } from 'react-emotion';

const buttonStyle = css`
  padding: 0.5rem 2rem;
  background-color: orange;
  font-weight: 600;
  border: 2px solid orange;
  color: white;
  border-radius: 5px;
  &:hover {
    cursor: pointer;
    background-color: transparent;
    color: orange;
  }
  &:focus {
    outline: 0;
  }
`;

#add the button style to the button element
...
<button className={buttonStyle} onClick={increment}>Up</button>
...
...
<button className={buttonStyle} onClick={decrement}>Down</button>
...

It's nothing fancy yet, but a solid foundation for a new project.

Check out that building the website works. Type:

node_modules/.bin/gatsby build

The website should now be build without any errors and our redux store in place.

I hope this will help you setting up a new gatsby project in which you want to use redux, as well as a css-in-js library. Thanks for reading, any questions, comments, pointers, feedback etc. is greatly appreciated, just write me an email at sascha@gatsbythemes.com.