Use Emotion CSS Prop in Storybook

Storybook Not Parsing CSS Prop without JSX Pragma

I was running into an issue where my Storybook webpack.config.js was not correctly parsing and evaluating Emotion styles passed via the css prop for React components without the Emotion JSX Pragma.

Instead of evaluating a computed class name, [object Object] was being passed to the HTML element.

<div css="[object Object]">
  <div css="[object Object]">
    <h1 css="[object Object]"></h1>
  </div>
</div>

The Emotion API enables styling React components or element that accepts a className prop by using a css prop. Emotion evaluates the styles passed to the css prop to a computed class name, which is applied to the className prop.

The Emotion docs specify two ways to use the css prop:

Emotion CSS Prop Works in Gatsby, not Storybook

React components in Gatsby would parse and render the Emotion css prop styles correctly via the @emotion/babel-preset-css-prop by using gatsby-plugin-emotion in gatsby-config.js.

gatsby-config.js
const plugins = [
  "gatsby-plugin-emotion"  // ... other gatsby-plugins
];

module.exports = {
  plugins
};

However, Storybook needs its own webpack configuration to load the @emotion/babel-preset-css-prop to parse styles properly via the css prop without the JSX pragma.

Setting Up .storybook/webpack.config.js

A custom webpack config for Storybook needs to be set up to specify @emotion/babel-preset-css-prop as a babel preset.

Order is important when setting up babel presets, and @emotion/babel-preset-css-prop must run before @babel/preset-react preset to convert css-prop.

Note: Babel preset-ordering runs reversed. So @emotion/babel-preset-css-prop should be listed after @babel/preset-react.

.storybook/webpack.config.js
const path = require("path");

module.exports = ({ config }) => {
  // use @babel/preset-react for JSX and env (instead of staged presets)
  config.module.rules[0].use[0].options.presets = [
    require.resolve("@babel/preset-react"),
    require.resolve("@babel/preset-env"),
    require.resolve("@emotion/babel-preset-css-prop")  ];
  // ... other configs

  // Add Webpack rules for TypeScript
  // ========================================================
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    loader: require.resolve("babel-loader"),
    options: {
      presets: [
        ["react-app", { flow: false, typescript: true }],
        require.resolve("@emotion/babel-preset-css-prop")      ]
      // ... other configs
    }
  });
  // ... other configs

  config.resolve.extensions.push(".ts", ".tsx");

  return config;
};

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