Gatsby: Gatsby build fails with emotion ThemeProvider in gatsby-browser

Created on 2 Feb 2019  ยท  10Comments  ยท  Source: gatsbyjs/gatsby

Description

After reading the documentation about gatsby-browser.js (https://www.gatsbyjs.org/docs/browser-apis/#wrapRootElement) and wrapRootElement, I figured after seeing that it's best suited for Providers to use it for my emotion ThemeProvider from the emotion-theming package (see https://github.com/emotion-js/emotion/tree/master/packages/emotion-theming).

This is how my gatsby-browser.js file looks like:

import React from 'react';
import { ThemeProvider } from './src/context/ThemeContext';

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

ThemeContext basically wraps the emotion theme provider with some other feature and is located in src/context/

import React from 'react';
import themeDark from '../theme_dark';
import themeLight from '../theme_light';

const defaultState = {
  dark: false,
  themeLoaded: themeLight,
  toggleDark: () => {},
};

const ThemeContext = React.createContext(defaultState);

const supportsDarkMode = () =>
  window.matchMedia('(prefers-color-scheme: dark)').matches === true;

class ThemeProvider extends React.Component {
  state = {
    dark: false,
  };

  toggleDark = () => {
    let dark = !this.state.dark;
    localStorage.setItem('dark', JSON.stringify(dark));
    this.setState({ dark });
  };

  componentDidMount() {
    // Getting dark mode value from localStorage!
    const lsDark = JSON.parse(localStorage.getItem('dark'));
    if (lsDark) {
      this.setState({ dark: lsDark });
    } else if (supportsDarkMode()) {
      this.setState({ dark: true });
    }
  }

  render() {
    const { children } = this.props;
    const { dark } = this.state;

    let themeLoaded = themeLight;

    if (dark) {
      themeLoaded = themeDark;
    }

    return (
      <ThemeContext.Provider
        value={{
          themeLoaded,
          dark,
          toggleDark: this.toggleDark,
        }}
      >
        {children}
      </ThemeContext.Provider>
    );
  }
}

export default ThemeContext;

export { ThemeProvider };

When running yarn develop I see no problem. The theme is injected as expected.
However thois doesn't work when running yarn build:

โฏ yarn build
yarn run v1.13.0
$ gatsby build
success open and validate gatsby-configs โ€” 0.011 s
success load plugins โ€” 0.191 s
success onPreInit โ€” 0.413 s
success delete html and css files from previous builds โ€” 0.122 s
success initialize cache โ€” 0.004 s
success copy gatsby files โ€” 0.083 s
success onPreBootstrap โ€” 0.011 s
success source and transform nodes โ€” 0.055 s
success building schema โ€” 0.221 s
success createPages โ€” 1.881 s
success createPagesStatefully โ€” 0.029 s
success onPreExtractQueries โ€” 0.086 s
success update schema โ€” 0.206 s
success extract queries from components โ€” 0.120 s
success run graphql queries โ€” 0.050 s โ€” 11/11 228.11 queries/second
success write out page data โ€” 0.003 s
success write out redirect data โ€” 0.001 s
success onPostBootstrap โ€” 0.002 s

info bootstrap finished - 4.97 s

success Building production JavaScript and CSS bundles โ€” 8.873 s
โ ‚ Building static HTML for pages{ theme: {},
  style: { marginBottom: '50px' },
  children:
   [ { '$$typeof': Symbol(react.element),
       type: [Function],
       key: null,
       ref: null,
       props: [Object],
       _owner: null },
     { '$$typeof': Symbol(react.element),
       type: [Function],
       key: null,
       ref: null,
       props: [Object],
       _owner: null },
     { '$$typeof': Symbol(react.element),
       type: [Function],
       key: null,
       ref: null,
       props: [Object],
       _owner: null },
     { '$$typeof': Symbol(react.element),
       type: [Function],
       key: null,
       ref: null,
       props: [Object],
       _owner: null } ] }

error Building static HTML for pages failed

See our docs page on debugging HTML builds for help https://gatsby.app/debug-html

  4 |
  5 | const BlackSectionWrapper = styled.div`
> 6 |   background: ${props => props.theme.colors.black};
    |                                             ^
  7 |   position: relative;
  8 |   left: calc(-50vw + 50%);
  9 |   width: 100vw;

Steps to reproduce

  1. Inject the ThemeProvider from emotion-theming in gatsby-browser.js
  2. Run yarn build

Expected result

The build should pass. This pattern works in development

Actual result

The build fails with the logs above

Environment

  System:
    OS: macOS 10.14.1
    CPU: (4) x64 Intel(R) Core(TM) i7-7567U CPU @ 3.50GHz
    Shell: 5.3 - /bin/zsh
  Binaries:
    Node: 10.12.0 - /usr/local/bin/node
    Yarn: 1.13.0 - /usr/local/bin/yarn
    npm: 6.7.0 - /usr/local/bin/npm
  Browsers:
    Chrome: 71.0.3578.98
    Firefox: 63.0.3
    Safari: 12.0.1
  npmPackages:
    gatsby: ^2.0.76 => 2.0.98 
    gatsby-image: ^2.0.20 => 2.0.28 
    gatsby-mdx: ^0.3.2 => 0.3.5 
    gatsby-plugin-emotion: ^4.0.1 => 4.0.1 
    gatsby-plugin-google-analytics: ^2.0.9 => 2.0.10 
    gatsby-plugin-manifest: ^2.0.9 => 2.0.13 
    gatsby-plugin-offline: ^2.0.16 => 2.0.21 
    gatsby-plugin-react-helmet: ^3.0.2 => 3.0.5 
    gatsby-plugin-sharp: ^2.0.14 => 2.0.18 
    gatsby-plugin-sitemap: ^2.0.4 => 2.0.4 
    gatsby-plugin-tslint: ^0.0.2 => 0.0.2 
    gatsby-plugin-typescript: ^2.0.3 => 2.0.3 
    gatsby-plugin-typography: ^2.2.5 => 2.2.5 
    gatsby-remark-autolink-headers: ^2.0.12 => 2.0.12 
    gatsby-remark-images: ^3.0.1 => 3.0.1 
    gatsby-source-filesystem: ^2.0.8 => 2.0.17 
    gatsby-transformer-sharp: ^2.1.8 => 2.1.12 
question or discussion

Most helpful comment

Hey, you need to add equivalent Providers in gatsby-ssr.js:

import React from 'react';
import { ThemeProvider } from './src/context/ThemeContext';

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

I think we need to expand documentation on those - that usually you would need to add this in both places and it would not be the case only some subtle edge cases

All 10 comments

Where is your ThemeContext.Consumer located?

https://www.gatsbyjs.org/blog/2019-01-31-using-react-context-api-with-gatsby/#editing-the-layout-file

ThemeContext.Consumer is used in my src/pages/index.js (this file doesn't use any layout at the moment, maybe this is why it's failing?) and in my src/layout/index.js

I'd recommend adding your ThemeProvider as the top level element in our Layout component and using the Layout component as the top level element of your page files.

Yeah that definitely works. However since I have multiple layouts I hoped that I could use gatsby-browser as the single entry point to pass my ThemeProvider

No worries. What do your theme files look like? Can you show an example of your consumer being used?

Hey, you need to add equivalent Providers in gatsby-ssr.js:

import React from 'react';
import { ThemeProvider } from './src/context/ThemeContext';

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

I think we need to expand documentation on those - that usually you would need to add this in both places and it would not be the case only some subtle edge cases

@dakebl here's an example of one of my Themes:

const white = '#FFFFFF';
const black = '#161617'; // For background color
const blackFont = '#2B2D3E'; // For font color
const gray = '#F8F8F9';

const theme = {
  backgroundColor: '#0e0e0e',
  colors: {
    black,
    blackFont,
    blue: '#3c83da',
    gray,
    pink: '#FED5D7',
    white,
  },
  fontColor: white,
  transitionTime: 0.5,
};

export default theme;

and here's an example on how it's being used in one of my layout files:

const Layout = (props: InterfaceLayoutProps) => (
  <StaticQuery
    query={graphql`
      query SiteTitleQuery {
        site {
          siteMetadata {
            title
            author
          }
        }
      }
    `}
    render={data => {
      const { frontmatter, cover, tableOfContents } = props.pageContext;
      const { title, subtitle } = frontmatter;

      return (
        <ThemeContext.Consumer>
          {(theme: {
            dark: boolean;
            themeLoaded: object;
            toggleDark: () => void;
          }) => (
            <React.Fragment>
              <SEO title={`${title} - ${data.site.siteMetadata.title}`} />
              <Header
                themeSwitcher={theme}
                sticky={true}
                siteTitle={data.site.siteMetadata.author}
              />
              <LayoutWrapper>
                <TitleSection>
                  <div>
                    <Title>{title}</Title>
                    <h2>{subtitle}</h2>
                  </div>
                </TitleSection>
                <div
                  style={{
                    margin: '0 auto',
                    maxHeight: '600px',
                    maxWidth: '1430px',
                  }}
                >
                  <Img
                    imgStyle={{ maxHeight: '600px', minHeight: '300px' }}
                    fluid={cover.childImageSharp.fluid}
                  />
                </div>
                <LayoutContentWrapper>
                  <MDXProvider
                    components={{
                      a: (aProps: any) => (
                        <a {...aProps} style={{ color: 'inherit' }} />
                      ),
                    }}
                  >
                    {props.children}
                  </MDXProvider>
                </LayoutContentWrapper>
              </LayoutWrapper>
              <Footer tableOfContents={tableOfContents} />
            </React.Fragment>
          )}
        </ThemeContext.Consumer>
      );
    }}
  />
);

@pieh I'll give this a try as soon as I can

@pieh it worked! Making sure the content between gatsby-browser.js and gatsby-ssr.js is the same fixed the build error.

Thank you!

Adding providers to gatsby-ssr.js also worked for me.

You can add the same Providers in gatsby-ssr.js and gatsby-browser.js the following way so you don't repeat code:

// gatsby-ssr.js
export { wrapRootElement } from './gatsby-browser'
Was this page helpful?
0 / 5 - 0 ratings

Related issues

KyleAMathews picture KyleAMathews  ยท  3Comments

brandonmp picture brandonmp  ยท  3Comments

Oppenheimer1 picture Oppenheimer1  ยท  3Comments

magicly picture magicly  ยท  3Comments

dustinhorton picture dustinhorton  ยท  3Comments