Duncan Leung
Set Up Storybook Decorators - Emotion Theme Provider
Published on

Set Up Storybook Decorators - Emotion Theme Provider

Authors

Related Post: Set Up Storybook Decorators - React Intl Provider

Problem: Storybook Not Recognizing Emotion Theme Provider

I was running into an issue setting up Storybook with Emotion Theming.

I was receiving an error when attempting to create a Storybook decorator with Emotion's <ThemeProvider />:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of `storyFn`.

I had set up Emotion's Theme provider in Storybook .storybook/preview.js in the similar pattern that React context providers are set up - Using a provider to pass the current theme to the tree below.

.storybook/preview.js

import { ThemeProvider } from "emotion-theming";
import theme from "~/theme";

addDecorator((story) => (
  <>
    <GlobalStyles />
    <ThemeProvider theme={theme}>
      <div style={{ padding: "3rem" }}>{story()}</div>
    </ThemeProvider>
  </>
));

This was the pattern used in our parent <App /> React component.

~/src/components/App.tsx
import { ThemeProvider } from "emotion-theming";

import theme from "~/theme";
import { ModalProvider } from "~/components/Modal";

/**
 * This component provides a reusable application wrapper for use
 * in Gatsby APIs, testing, etc.
 */
const App = ({ element }: { element: ReactNode }) => {
  return (
    <ThemeProvider theme={theme}>
      <ModalProvider>{element}</ModalProvider>
    </ThemeProvider>
  );
};

export default App;

Extract ThemeProvider into its own Decorator

The fix is to extract Emotion's <ThemeProvider /> into its own decorator.

.storybook/preview.js

import { addDecorator } from "@storybook/react";

import { EmotionThemeProvider } from "./decorators";
import GlobalStyles from "../src/components/Layout/GlobalStyles";

// Global Styles ==============================
addDecorator((story) => (
  <>
    <GlobalStyles />
    <div style={{ padding: "3rem" }}>{story()}</div>
  </>
));

// Emotion Theme Provider =====================
addDecorator(EmotionThemeProvider);

// ... other decorators
.storybook/decorators/EmotionThemeProvider.js
import { ThemeProvider } from "emotion-theming";
import theme from "~/theme";

const EmotionThemeProvider = (storyFn) => (
  <ThemeProvider theme={theme}>{storyFn()}</ThemeProvider>
);

export default EmotionThemeProvider;

Gatsby Starter: TypeScript + Emotion + Storybook

If you want to see the full config in a project, I created a Gatsby Starter that uses Gatsby + TypeScript + Emotion + Storybook + React Intl + SVGR + Jest.

Check it out at: Gatsby Starter: TypeScript + Emotion + Storybook