Describe the bug
I can't seem to get emotion's css
prop to work inside of Storybook. Seems like emotion is now the dominant css-in-js library, and the css
prop is emotion's recommended primary way of styling components, so I thought I was choosing a well-travelled happy path here, but I'm stuck. I'm very open to the fact that I'm doing something dumb though, of course.
In my actual project repo I'm getting the same thing described here and here. I tried all of the recommendations in both those threads, and still no joy -- and it looks like Iām not the only one.
So, I created a brand new React Storybook repo to just hello-world using emotion css prop and storybook. And I can't get that to work either! I'm wondering if someone could look at the super simple repro repo I made and tell me where I went wrong, here it is:
https://github.com/jaredh159/storybook-emotion-css
I'd be up for submitting a PR to the docs clarifying how to do this, if someone can show me where I messed up.
To Reproduce
Steps to reproduce the behavior:
git clone [email protected]:jaredh159/storybook-emotion-css.git
cd storybook-emotion-css && yarn && yarn storybook
TypeError: Cannot read property 'name' of null
Expand for full error message
```
ERROR in ./src/CssPropButton.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: Cannot read property 'name' of null
at getDeclaratorName (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:212:32)
at getIdentifierName (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:267:24)
at getLabelFromPath (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:190:19)
at transformExpressionWithStyles (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:450:19)
at transformCssCallExpression (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:708:31)
at /test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:797:9
at Array.forEach (
at /test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:796:26
at Array.forEach (
at Object.emotionCoreMacroThatsNotARealMacro [as @emotion/core] (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:794:27)
at PluginPass.ImportDeclaration (/test-repo/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.dev.js:891:45)
at newFn (/test-repo/node_modules/@babel/traverse/lib/visitors.js:193:21)
at NodePath._call (/test-repo/node_modules/@babel/traverse/lib/path/context.js:53:20)
at NodePath.call (/test-repo/node_modules/@babel/traverse/lib/path/context.js:40:17)
at NodePath.visit (/test-repo/node_modules/@babel/traverse/lib/path/context.js:88:12)
at TraversalContext.visitQueue (/test-repo/node_modules/@babel/traverse/lib/context.js:118:16)
@ ./stories/index.stories.js 5:0-49 6:143-156
@ ./stories sync .stories.js$
@ ./.storybook/config.js
@ multi ./node_modules/@storybook/core/dist/server/common/polyfills.js ./node_modules/@storybook/core/dist/server/preview/globals.js ./.storybook/config.js (webpack)-hot-middleware/client.js?reload=true
</details>
**Expected behavior**
I expected to be able to use the `css` prop from emotion within Storybook.
**Code snippets**
The repro repo does add a `.babelrc` inside the `stories` dir as recommended in the emotion docs and on [this similar issue](https://github.com/storybookjs/storybook/issues/6853), like so:
```JSON
{
"presets": ["@emotion/babel-preset-css-prop"]
}
I also tried this workaround, but no joy.
System:
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
I'm seemingly bumping into this too. Also have tried all the suggested workarounds in the referenced issues.
My CSS props work just fine outside of Storybook but once I am running in storybook my css props aren't working. Anything that's basically just a string works but any time I am referencing props in the css
/ styled
tag, the css property name isn't present.
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!
I'm seeing this same issue in @storybook/[email protected]
, it makes Storybook's core features basically unusable. I've also attempted all suggested solutions, including .babelrc
inside .storybook
, updating .storybook/webpack.config.js
and others.
Has there been any traction on this issue?
Also just upgraded to 5.2.0
, seeing the same issues.
š require.resolve('@emotion/babel-preset-css-prop')
+ blowing away node_modules
and rerunning yarn
fixed it.
Based our now working config kinda on this: https://github.com/emotion-js/emotion/issues/1359#issuecomment-509798083 but with some additional crap for resolving sass stuff (we're in the process of migrating and our storybook stuff stopped working without anyone noticing).
Sorry for any inconvenience and thanks for the awesome library.
@shilman in case you missed, I think this ticket is potentially resolvable but I'd ask @ryanlanciaux / @jaredh159 to verify
@brekk I'm not clear exactly what the fix is you found, and where I would apply it. Could you send a PR to my simple repro repo linked in the OP?
@jaredh159 @shilman I was able to track at least part of the problem down in the linked storybook-emotion-css
repo: https://github.com/emotion-js/emotion/blob/master/packages/babel-plugin-emotion/src/utils/label.js#L66 Seems to be the relevant line -- At least in my testing I was able to get one of the two examples (CssPropButton
) rendering correctly by modifying the if
to be:
if (parent.isFunctionDeclaration() && parent.node && parent.node.id && parent.node.id.name) {
With that one of the two components renders correctly. I'd be happy to help troubleshoot more but that's all I was able to uncover in that repo.
I also ran into this issue with Babel7+Typescript+React+Emotion+Storybook setup. After hours of digging through docs and experiments I confirmed that .storybook/webpack.config.js
:
const path = require("path");
module.exports = function({ config }) {
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")],
},
});
config.resolve.extensions.push(".ts", ".tsx");
return config;
};
works _only when root .babelrc does not specify @emotion/babel-preset-css-prop plugin_ (which creates problems when transpiling for publish).
Here is working .babelrc
in the package root:
{
"presets": [
"@babel/preset-env",
"@babel/preset-typescript"
// "@emotion/babel-preset-css-prop"
]
}
Un-commenting babel-preset-css-prop
plugin mysteriously reverts to non-working behavior.
I understand that ordering of presets is key here, since we want babel-preset-css-prop
plugins to run before plugin-transform-react-jsx
from preset-react
. In fact, as far I understand we don't need _plugin-transform-react-jsx_ since the goal is to use jsx
from emotion.
At the same time I don't completely understand how presets in _root .babelrc_ interact with webpack _babel-loader_ options. The observed behavior suggests that root presets are processed first, then loader option presets are processed and babel-preset-css-prop
from options is merged into the first instance from the root config. This would explain the situation, but I am speculating at this point. In any case I still don't understand how to make configuration work for both storybook and regular publish.
@illinar Have you tried the babelrc: false
option in the loader https://github.com/storybookjs/storybook/issues/8062#issuecomment-539632591
@illinar Have you tried the
babelrc: false
option in the loader
Thanks. This option seems to do the trick.
Can somebody create a preset for this? https://storybook.js.org/docs/presets/writing-presets/#docs-content
The OG post is unrelated to most of the discussion on this thread. I actually pulled his repo and fixed the error. He was exporting an anonymous component.
Error message:
TypeError: /storybook-emotion-css/src/CssPropButton.js: Cannot read property 'name' of null
Original CssPropButton.js:
export default function() {
return (
<button
css={css`
color: hotpink;
`}
>
I should be hot pink!
</button>
);
}
Fixed with no error:
export default function CssPropButton() {
return (
<button
css={css`
color: hotpink;
`}
>
I should be hot pink!
</button>
);
}
Unfortunately, this didn't solve the error I am having. His build set with the .bablerc
in .storybook
dir is working fine.
I cannot get this to work at all. The only way I can get this to work is if I use the jsx pragma, and then, it breaks storybook's ability to generate or infer prop descriptions for the docs. I'm not certain, but I'm assuming this is because both storybook's doc addon and emotion want to rewrite React.createElement?
In my project I was able to get Storybook to recognize css-prop (without using the jsx pragma) by adding this config to /.storybook/webpack.config.js
.
Line 17 - Line 19:
config.module.rules[0].use[0].options.presets = [
require.resolve('@babel/preset-react'),
require.resolve('@babel/preset-env'),
// Emotion preset must run BEFORE reacts preset to properly convert css-prop.
// Babel preset-ordering runs reversed (from last to first). Emotion has to be after React preset.
require.resolve('@emotion/babel-preset-css-prop'),
];
Also, I am including the @emotion/babel-preset-css-prop
for .ts
and .tsx
files
Line 41 - Line 43:
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [
['react-app', { flow: false, typescript: true }],
// Emotion preset must run BEFORE reacts preset to properly convert css-prop.
// Babel preset-ordering runs reversed (from last to first). Emotion has to be after React preset.
require.resolve('@emotion/babel-preset-css-prop'),
],
// other plugins here...
},
});
I created a template to share my project config that uses Gatsby + TypeScript + Emotion + Storybook + React Intl + SVGR + Jest:
https://github.com/duncanleung/gatsby-typescript-emotion-storybook
Here's a link to my webpack.config.js
that enables @emotion/babel-preset-css-prop
:
Here's the .storybook/main.js
that works for me, using Storybook 5.3.19
module.exports = {
stories: ["../stories/**/*.stories.tsx"],
addons: ["@storybook/preset-typescript"],
webpackFinal: async (config) => {
config.module.rules[0].use[0].options.presets.push(
require.resolve("@emotion/babel-preset-css-prop")
);
return config;
},
};
There's a babel
property in main.js
too that allows you to change the babel config, without having to find the exact webpack loader. That's a bit safer, since it's pretty fragile to find the correct loader to mutate this way.
@ndelangen I couldn't find an example using a custom babel
prop in main.js
. To get it working I imported Storybook's default Babel config and added it into the babel
config in main.js
.
This is what I ended up with:
// main.js
const babelConfig = require('@storybook/core/dist/server/common/babel').default();
module.exports = {
babel: {
...babelConfig,
presets: [
...babelConfig.presets,
require.resolve('@emotion/babel-preset-css-prop'),
],
};
Is there a better way to do it? It would be cool to add to the default config rather than replacing it.
Thanks @ndelangen & @nerdyman - I didn't know you could set a babel
export.
Here's the .storybook/main.js
I ended up with, using Storybook 6.0.12
module.exports = {
stories: ["../stories/**/*.stories.tsx"],
babel: (config) => {
config.presets.push(require.resolve("@emotion/babel-preset-css-prop"));
return config;
},
};
Perhaps we could create a storybook preset for emotion?
@JakeElder would you be interested in helping us with that?
@ndelangen - That's a good idea. Sure - I'm a big fan of Storybook, I'd be happy to contribute. I'll take a look at the preset API and some existing presets tomorrow šµļøāāļø
Feel free to open a meeting request with me, and I can show you around the codebase, get your started:
https://calendly.com/ndelangen/storybook
Here's the docs for writing presets:
https://storybook.js.org/docs/react/api/writing-presets
I've created a (very) MVP in a personal project I'm currently working on, linking the package locally just as a sanity check so I know what to expect in making a preset - https://github.com/JakeElder/tenjin/compare/develop...feature/add-storybook-emotion-preset?expand=1
In order to complete a package ready to be used publicly, I'll need to
babel-plugin-emotion
@babel/plugin-transform-react-jsx
@emotion/babel-preset-css-prop
must come after @babel/preset-react
/@babel/preset-typescript
)package.json
'sstyled-system
ās css
functionI imagine it will take around a day. I have a busy week ahead so won't have the chance to complete until late next week. @ndelangen - I have requested a meeting next Thursday - it'd be cool to have a tour of the code base š
I can give you access to the presets repo, so no fork is required! @shilman would you be available, you know a bit more about the presets repo than I do.
I think @mrmckeb 's your best bet!
I had this issue when migrating to Storybook v6 from v5.
Here is my config in v5 which did not work in v6:
// https://storybook.js.org/docs/configurations/custom-webpack-config/
const babel = require('@babel/core');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const path = require('path');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const webpack = require('webpack');
module.exports = {
addons: [
'@storybook/addon-a11y',
'@storybook/addon-links',
'@storybook/addon-knobs',
'@storybook/addon-essentials',
],
stories: ['../stories/**/*.stories.tsx'],
webpackFinal: (config) => {
const babelConfig = babel.loadPartialConfig({
configFile: require.resolve('../babel.config'),
});
if (!babelConfig) {
throw new Error('Failed to load Babel config');
}
config.module.rules.push({
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: require.resolve('babel-loader'),
options: babelConfig.options,
},
});
config.resolve.extensions = [...(config.resolve.extensions || []), '.ts', '.tsx'];
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
configFile: path.join(__dirname, '..', 'tsconfig.json'),
extensions: config.resolve.extensions,
}),
];
config.plugins = [
...(config.plugins || []),
new webpack.DefinePlugin({
// Define `process.browser` so that Storybook can build Next.js code which depends on this value.
// This is needed to prevent webpack from parsing '@sentry/node' (referenced in initSentry.ts)
'process.browser': true,
}),
new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: path.join(__dirname, '..', 'tsconfig.json'),
mode: 'write-references',
},
}),
];
return config;
},
};
Here is the config which made the Emotion css prop work in v6:
// https://storybook.js.org/docs/configurations/custom-webpack-config/
const babel = require('@babel/core');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const path = require('path');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const webpack = require('webpack');
module.exports = {
addons: [
'@storybook/addon-a11y',
'@storybook/addon-links',
'@storybook/addon-knobs',
'@storybook/addon-essentials',
],
stories: ['../stories/**/*.stories.tsx'],
babel: (config) => {
const babelConfig = babel.loadPartialConfig({
configFile: require.resolve('../babel.config'),
});
if (!babelConfig) {
throw new Error('Failed to load Babel config');
}
return {
...config,
...babelConfig.options,
};
},
webpackFinal: (config) => {
// Use @svgr/webpack to load SVGs as React components.
// https://github.com/gregberge/svgr/tree/868e5dca21703b4fc1e09798e727669ba66d06eb/packages/webpack
const svgRule = config.module.rules.find((rule) => rule.test.test('.svg'));
svgRule.exclude = [/\.svg$/];
config.module.rules.push({
test: /\.svg$/,
use: [require.resolve('@svgr/webpack')],
});
config.resolve.extensions = [...(config.resolve.extensions || []), '.ts', '.tsx'];
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
configFile: path.join(__dirname, '..', 'tsconfig.json'),
extensions: config.resolve.extensions,
}),
];
config.plugins = [
...(config.plugins || []),
new webpack.DefinePlugin({
// Define `process.browser` so that Storybook can build Next.js code which depends on this value.
// This is needed to prevent webpack from parsing '@sentry/node' (referenced in initSentry.ts)
'process.browser': true,
}),
new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: path.join(__dirname, '..', 'tsconfig.json'),
mode: 'write-references',
},
}),
];
return config;
},
};
Note:
I just moved babel specific config to the new babel
key.
The Emotion Babel preset lives in my babel.config.js outside of this file.
Hey,
Same issue, but I'm working with Styled Components. I tried to substitute the babel plugin, but it breaks the build.
Has anyone had to do this, but with SC?
Thanks.
Still running 5.x here but I had an issue getting the css
prop to work in mdx stories as well.
I ended up hacking together a custom renderer
for the mdx loader in the webpack.config.js
like this:
const mdxLoaderRenderer = `
import React from 'react'
import { jsx } from '@emotion/core'
import {mdx as _mdx } from '@mdx-js/react'
const mdx = (name, props, ...children) => {
return (typeof name === 'string' || typeof name === 'symbol') && !(props && 'css' in props) ? _mdx(name, props, ...children) : jsx(name, props, ...children)
}
`
config.module.rules[4].use[1].options.renderer = mdxLoaderRenderer
@caspardue is that something we can add to Storybook's MDX loader/compiler somehow?
Adding this in .storybook/main.js
worked for me. Thanks for mentioning.
babel: (config) => { const babelConfig = babel.loadPartialConfig({ configFile: require.resolve('../babel.config'), }); if (!babelConfig) { throw new Error('Failed to load Babel config'); } return { ...config, ...babelConfig.options, }; },
@shilman Maybe. I will updating my project to sb6 soon. If the code is still needed for my use case I will create a repository demonstrating the issue.
Most helpful comment
In my project I was able to get Storybook to recognize css-prop (without using the jsx pragma) by adding this config to
/.storybook/webpack.config.js
.Line 17 - Line 19:
Also, I am including the
@emotion/babel-preset-css-prop
for.ts
and.tsx
filesLine 41 - Line 43:
I created a template to share my project config that uses Gatsby + TypeScript + Emotion + Storybook + React Intl + SVGR + Jest:
https://github.com/duncanleung/gatsby-typescript-emotion-storybook
Here's a link to my
webpack.config.js
that enables@emotion/babel-preset-css-prop
: