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:
- Setting a Babel Preset: @emotion/babel-preset-css-prop
- Using the JSX Pragma
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.
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-propmust run before@babel/preset-reactpreset to convert css-prop.Note: Babel preset-ordering runs reversed. So
@emotion/babel-preset-css-propshould be listed after@babel/preset-react.
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