Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@emotion/react ThemeProvider does not work with @emotion/styled #10231

Closed
fabian-hiller opened this issue Mar 27, 2020 · 45 comments
Closed

@emotion/react ThemeProvider does not work with @emotion/styled #10231

fabian-hiller opened this issue Mar 27, 2020 · 45 comments

Comments

@fabian-hiller
Copy link
Member

Describe the bug
I use @emotion/react ThemeProvier to access the theme prop when styling a component. When I open my app with "npm start" everything works smoothly. With "npm run storybook", however, the theme object for my styled emotion components is empty.

To Reproduce
Steps to reproduce the behavior:

  1. npx create-react-app my-app
  2. npx -p @storybook/cli sb init
  3. npm install @emotion/[email protected] @emotion/[email protected]
  4. Creat Theme and Button component with logs for the theme prop
  5. Creat Button.stories.js
  6. Add Theme and Button component to App.js
  7. Add Theme component (with addDecorator) as decorator to .storybook/preview.js
  8. Run "npm start" and "npm run storybook"

Expected behavior
The pre-defined theme object is displayed in the console, so that you can access it when styling.

Screenshots
image

Code snippets
GitHub repo: https://github.com/fabian-hiller/a7hs5hsk2

System:
Environment Info:
System:
OS: macOS 10.15.3
CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Binaries:
Node: 12.13.1 - /usr/local/bin/node
npm: 6.14.4 - ~/.npm-global/bin/npm
Browsers:
Chrome: 80.0.3987.149
Firefox: 74.0
Safari: 13.0.5
npmPackages:
@storybook/addon-actions: ^5.3.17 => 5.3.17
@storybook/addon-links: ^5.3.17 => 5.3.17
@storybook/addons: ^5.3.17 => 5.3.17
@storybook/preset-create-react-app: ^2.1.1 => 2.1.1
@storybook/react: ^5.3.17 => 5.3.17

@shilman
Copy link
Member

shilman commented Mar 27, 2020

We are also using emotion 10.0.x in Storybook. I'm guessing the two are not playing nicely together. cc @ndelangen

@fabian-hiller
Copy link
Member Author

Hello @shilman , thank you very much for the fast processing. I am currently in the process of moving our design system to Storybook. Due to this problem, a part of our frontend development is currently at a standstill. If it helps you, I will support you in solving this problem.

@fabian-hiller
Copy link
Member Author

Is there any news yet? @shamin @ndelangen

@shilman
Copy link
Member

shilman commented Apr 1, 2020

I don't know what's happening here. Hopefully @ndelangen does.

@ndelangen
Copy link
Member

Are you wrapping your stories with a decorator providing the theme context?

@fabian-hiller
Copy link
Member Author

Are you wrapping your stories with a decorator providing the theme context?

Yes. As described in step 7 (steps to reproduce), I have done that. Because the useTheme hook works, as you can see in the photo, the theme context is available.

I created a repository where you can look at the code or clone it for testing.
https://github.com/fabian-hiller/a7hs5hsk2

@tajo
Copy link

tajo commented Apr 2, 2020

Might be related to #10296

It seems that context provider doesn't work on the top level in stories.

@fabian-hiller
Copy link
Member Author

It seems that context provider doesn't work on the top level in stories.

In my case it just does not work with the styled components. In the other cases, as you can see in the screenshot, the context works.

But maybe the reason for the problem is the same for both of us.

@ndelangen
Copy link
Member

I spoke to one of the emotion maintainers yesterday, and this is essentially due to both storybook & your app/component using emotion & theming.

Emotion theming solution uses React context, meaning is somewhat global.

The proper solution is to not use emotion context but our own.

Here's the suggested solution from https://github.com/Andarist:

styled.div(props => ({
  const theme = useMyTheme();
  return {
    color: theme.color,
  };
})}

I'm not mentioning this because I think YOU should do this, but actually, this is something Storybook should do, so we would never conflict with someone using emotion.

We think emotion is the best, and we certainly don't want to stand in the way of anyone using it + storybook.

@fabian-hiller I have some pretty strong ideas on how to make this work from storybook, would you be interested in working with me to solve this in storybook?

@fabian-hiller
Copy link
Member Author

@fabian-hiller I have some pretty strong ideas on how to make this work from storybook, would you be interested in working with me to solve this in storybook?

Yeah, I'd be honored. How much effort do you estimate?

@ndelangen
Copy link
Member

We need

  • to expose a custom context for theming that derives the theme from the manager-context state
  • replace the ThemeProvider from emotion with this custom ContextProvider
  • change @storybook/theming so styled & useTheme auto-inject them theme into any functions

part 3 is going to be the most challenging, particularly to make all the typings correct.

If you'd want to tackle this together in a pair programming session, let's schedule something:
https://calendly.com/ndelangen/storybook

@fabian-hiller
Copy link
Member Author

If you'd want to tackle this together in a pair programming session, let's schedule something:
https://calendly.com/ndelangen/storybook

All right. See you later.

@connor-baer
Copy link
Contributor

connor-baer commented Apr 10, 2020

Woohoo! It's great to see this being worked on. We face the same issue at sumup-oss/circuit-ui when trying to upgrade to Emotion 11. If there's anything that I can do to help here, please let me know.

@fabian-hiller
Copy link
Member Author

Thanks for your message @connor-baer.

If there's anything that I can do to help here, please let me know.

Could you find a solution? Where can I find the code for it in your repository?

@connor-baer
Copy link
Contributor

connor-baer commented Apr 11, 2020

@fabian-hiller I searched the Emotion and Storybook issues extensively and there doesn't seem to be a solution that can be implemented on the user's side.

I came across the same suggestion that @ndelangen relayed from @Andarist: frameworks should implement and use their own theme context. The steps that @ndelangen outlined are much more straightforward than what I had in mind. Did you two already get started on those?

I looked a bit into @storybook/theming and how we could auto-inject the custom theme context into styled & useTheme (step 3). Looks like we need to create our own styled instance from @emotion/styled-base, something like this (pseudo code):

import React from 'react';
import emotionStyled from '@emotion/styled-base'

const useTheme = () => React.useContext(StorybookThemeContext);

const styled = (...args) => {
  const Styled = emotionStyled(...args);
  const theme = useTheme();
  return React.forwardRef((props) => <Styled theme={theme} {...props} />;
}

Then we just need to apply the same magic that @emotion/styled does to make the styled.<tag> syntax work.

@fabian-hiller
Copy link
Member Author

@connor-baer I had an approach similar to yours, but I haven't been able to make it work yet. I will test your code the days and if it works, I can create a PR.

@lucascurti
Copy link

lucascurti commented Apr 14, 2020

Doing something like this as a workaround would be a bad idea?

import styled from "@emotion/styled";
import { theme } from "custom-theme";

export const Button = styled.button`
  background-color: ${theme.colors.green};
`;

Another thing that seems to work is keep using ThemeProvider from emotion-theming until this is fixed for @emotion/react

import { ThemeProvider } from "emotion-theming";
import { theme } from "custom-theme";

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

addDecorator(ThemeDecorator);

@connor-baer
Copy link
Contributor

Doing something like this as a workaround would be a bad idea?

This would require updating every component inside Storybook or your own codebase which would be a huge amount of work. It also makes it impossible to dynamically update the theme, e.g. from light to dark.

Another thing that seems to work is keep using ThemeProvider from emotion-theming until this is fixed for @emotion/react

I assume you're talking about using Emotion 11 in the component library? Does emotion-theming work with Emotion 11? I figured it wouldn't since @emotion/styled imports the theme context from @emotion/react, doesn't it?

@lucascurti
Copy link

lucascurti commented Apr 14, 2020

Good point.
Yes. I'm using Emotion 11 and seems that using ThemeProvider from emotion-theming works. This is how I ended up doing it.

@connor-baer
Copy link
Contributor

Interesting, I'll give that a shot. Thanks for the tip!

@grncdr
Copy link
Contributor

grncdr commented Aug 26, 2020

Given that the above PR is not yet merged, I think this issue should be re-opened.

@stale
Copy link

stale bot commented Sep 20, 2020

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!

@ndelangen
Copy link
Member

potential complications:

  • dynamic story source rendering ?
  • render multiple instances of framework (for examples angular)
    solved in the existing inline rendering solution for example Angular (@ThibaudAV)

@ndelangen
Copy link
Member

potential additional upside:

  • No more need for the whole prepareForInline business (converting non-react to react)

@HoverBaum
Copy link

Also ran into this issue today and it took some time to figure out which combination of things was causing the issue. I am building a component library upon styled-system and emotion. Inside my App, everything worked fine but in Storybook the components didn't take any values from the ThemeProvider (added via decorators in preview.js).
My workaround, for now, is to import { ThemeProvider } from 'emotion-theming'. As suggested by lucascurty.

Would love to see support for ThemeProvider from @emotion/react 🙂

@badsyntax
Copy link

For me the solution was to add the emotion packages to the webpack.config.js:

module.exports = {
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
    alias: {
      ...,
      '@emotion/react': '@emotion/react',
      '@emotion/styled': '@emotion/styled',
    },
  },
  ...

@fslc92
Copy link

fslc92 commented Jun 16, 2021

I have it working like this

.storybook/main.js

const path = require("path")
const { override, babelInclude, addWebpackAlias, addWebpackResolve } = require("customize-cra")

module.exports = {
  stories: [
    "../stories/**/*.js"
  ],
  addons: [
    "@storybook/addon-links",
    {
      name: "@storybook/addon-essentials",
      options: {
        actions: false,
        controls: false,
        viewport: false,
      }
    },
    "@react-theming/storybook-addon"
  ],
  webpackFinal: async (config, env) => {  
    return Object.assign(
      config,
      override(
        /* TODO: remove once this is merged: https://github.com/storybookjs/storybook/pull/13300
         *
         * Storybook does not support emotion 11 yet.
         * Sometimes the modules get crossed. If that happens, uncomment these lines, delete/re-install modules and run storybook again.
         * IT WILL FAIL until you re-comment out these lines and restart the storybook server.
         * import addWebpackAlias from customize-cra
         *
        **/
        addWebpackAlias({
          "@emotion/core": path.resolve("node_modules/@emotion/react"),
          "@emotion/styled": path.resolve("node_modules/@emotion/styled"),
          "emotion-theming": path.resolve("node_modules/@emotion/react"),
          "@emotion/react": path.resolve("node_modules/@emotion/react"),
        }),
        addWebpackResolve({
          modules: [
            ...(config.resolve.modules || []),
            path.resolve(__dirname, "../../webapp/src")
          ] 
        }),
      )(config, env)
    )
  },
}

.storybook/preview.js

import { ThemeProvider } from "@emotion/react"
import { withThemes } from "@react-theming/storybook-addon"

const themes = []

export const decorators = [
  withThemes(ThemeProvider, themes),
]

export const parameters = {
  backgrounds: {
    default: "light",
    values: themes.map(({ name, colors }) => ({ name, value: colors.background }))
  },
}

bkiac added a commit to bkiac/tarnhelm that referenced this issue Jun 26, 2021
* refactor(web): migrate to emotion

* fix(web): fix emotion theme collision in storybook

Related issues:
- storybookjs/storybook#12262
- storybookjs/storybook#10231

Solution by storybookjs/storybook#10231 (comment)

* style(web): rename theme type

* refactor(web): remove theme import

* refactor(web): add emotion animation snippet helper

See: emotion-js/emotion#239
@onpaws
Copy link

onpaws commented Jul 21, 2021

For anyone else having problems with MUIv5 + Storybook 'Docs' tab, this was my solution.

We use MUIv5 (currently in beta) which depends on Emotion 11. Like many others here, I added a decorator in preview.js.

export const decorators = [
  (Story) => (
    <MUIThemeProvider theme={muiTheme}>
      <CssBaseline />
      <Story />
    </MUIThemeProvider>
  ),
];

Many (all?) MUI components depend on the MUI theme object, which users typically provide via MUI's <ThemeProvider>
e.g. <OutlinedInput> includes:

borderColor: theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)'

The problem: as we started trialling Storybook v6.3, while Storybook's 'Canvas' tab was working OK, the 'Docs' tab was crashing in some cases. Apparently components whose styling depends on values from the MUI theme (e.g. dark/light relies ontheme.palette.mode) were unable to read the MUI theme object -> Uncaught TypeError: Cannot read properties of undefined (reading 'mode')

Inspecting via React DevTools, it looks like Storybook wraps things using its own ThemeProvider, which if I understand correctly, seems to 'win' (overwrite?) vs MUI's ThemeProvider.

Solution: I started with @lucascurti tip (thanks!), but also wrapped that with MUI's theme provider.

preview.js

import { ThemeProvider as MUIThemeProvider } from "@material-ui/core";
import { ThemeProvider } from "emotion-theming";

export const decorators = [
  (Story) => (
    <MUIThemeProvider theme={muiTheme}>
      <ThemeProvider theme={muiTheme}>
        <CssBaseline />
        <Story />
      </ThemeProvider>
    </MUIThemeProvider>
  ),
];

I'm pretty sure this is mixing both Emotion 10 and 11, which seems not great and hope we don't have new issues down the road.
Regardless, for today at least, I'm glad Docs tab works again.

It seems preferable to have just one ThemeProvider, for this reason I'd also love to see Emotion 11 support land (#13300), which I believe would make my solution no longer needed. If anyone has insight into what's going on would be nice to hear, either way hope this helps someone else.

onpaws added a commit to splitgraph/splitgraph.com that referenced this issue Jul 21, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
onpaws added a commit to splitgraph/splitgraph.com that referenced this issue Jul 29, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
onpaws added a commit to splitgraph/splitgraph.com that referenced this issue Aug 1, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
onpaws added a commit to splitgraph/splitgraph.com that referenced this issue Aug 4, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
milesrichardson pushed a commit to splitgraph/splitgraph.com that referenced this issue Aug 9, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
milesrichardson pushed a commit to splitgraph/splitgraph.com that referenced this issue Aug 18, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
milesrichardson pushed a commit to splitgraph/splitgraph.com that referenced this issue Aug 19, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
milesrichardson pushed a commit to splitgraph/splitgraph.com that referenced this issue Aug 20, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
milesrichardson pushed a commit to splitgraph/splitgraph.com that referenced this issue Aug 23, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
milesrichardson pushed a commit to splitgraph/splitgraph.com that referenced this issue Aug 23, 2021
- Discovered some kind of theme object/Context conflict imposed by the fact Storybook wraps our components with its own ThemeProvider. (Echoes of Theme-UI vs MUI issue from before)
- Resolution: learning info from storybookjs/storybook#10231 + a small change storybookjs/storybook#10231 (comment)
- Result: Docs tab now works again
@cabello
Copy link
Contributor

cabello commented Oct 17, 2021

My problem revolves around the fact that useTheme returns empty object {}, while styling with styled.div and props.theme.foo works as expected.

preview.js contains the decorator:

import { ThemeProvider } from 'emotion-theming';
import { lightTheme } from 'src/common/theme';

export const decorators = [
  (Story) => (
    <ThemeProvider theme={lightTheme}>
      <Story />
    </ThemeProvider>
  ),
];

Any component that uses useTheme:

const SomeComponent = () => {
  const theme = useTheme(); // returns empty object

My dependencies are:

"@storybook/addon-actions": "^6.4.0-beta.12",
"@storybook/addon-essentials": "^6.4.0-beta.12",
"@storybook/addon-links": "^6.4.0-beta.12",
"@storybook/addons": "^6.4.0-beta.12",
"@storybook/node-logger": "^6.4.0-beta.12",
"@storybook/preset-create-react-app": "^3.2.0",
"@storybook/react": "^6.4.0-beta.12",
"@emotion/css": "^11.0",
"@emotion/react": "^11.0",
"@emotion/styled": "^11.0",

Potentially unrelated but I also depend on "@chakra-ui/react": "^1.6.4", which is a component library that uses emotion 11 as dependency.

@HosseinAgha
Copy link

HosseinAgha commented Nov 24, 2021

We have the exact same issue @cabello. The @mavlikwowa's solution only makes theme available in the emotion styled-components but it looks like useTheme is not working.

@totszwai
Copy link

totszwai commented Nov 25, 2021

Edit:

Ok, on top of @onpaws's workaround, I also had to add the following in order to get them to work:

  webpackFinal: async (config) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      "@emotion/core": resolvePath("node_modules/@emotion/react"),
      "emotion-theming": resolvePath("node_modules/@emotion/react")
    }
    return config;
  }

Original:
I tried using the solution from @onpaws

import React from 'react';
import { ThemeProvider as MuiThemeProvider, CssBaseline } from '@mui/material';
import { ThemeProvider } from 'styled-components';
import { GlobalStyle } from './index';

...

<StyledEngineProvider injectFirst>
    <MuiThemeProvider theme={props.theme}>
      <ThemeProvider theme={props.theme}>
        <GlobalStyle />
        <CssBaseline />
        {props.children}
      </ThemeProvider>
    </MuiThemeProvider>
</StyledEngineProvider>

But the typography setting configured for MUI is not passing down... If I run my project directly with react-scripts everything is working. Anyone here know how to get it to work with storybook? :(

@rrsai
Copy link

rrsai commented Dec 3, 2021

Storybook has provided a feature-flag for storybook users that works around this issue for now, see the other issue: #13145 For those who spend time looking for a fix for this after me.

Or to skip to the docs:

After much debugging of the clash between versions of emotion used in storybook and in app, and for those who come after me, I found that storybook has now provided a feature flag to avoid clashing with later versions of emotion: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#emotion11-quasi-compatibility
I tested it and it works well to prevent emotion version-out-of-sync problems in storybook, and is a 3 line change.

@shilman
Copy link
Member

shilman commented Dec 3, 2021

@rrsai thanks so much for spreading the word!!! 🙏 🙏 🙏

@azizoid
Copy link

azizoid commented May 11, 2022

I ran to that problem recently, so everything that need to be upgraded need to be upgraded

@slim-hmidi1
Copy link

I'm using emotion v11 and storybook v7 and I face the same issue:

    "@storybook/addon-essentials": "^7.5.3",
    "@storybook/addon-interactions": "^7.5.3",
    "@storybook/addon-links": "^7.5.3",
    "@storybook/addon-onboarding": "^1.0.8",
    "@storybook/addon-themes": "^7.5.3",
    "@storybook/blocks": "^7.5.3",
    "@storybook/nextjs": "^7.5.3",
    "@storybook/react": "^7.5.3",
    "@storybook/testing-library": "^0.2.2",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "eslint": "^8",
    "eslint-config-next": "14.0.3",
    "eslint-plugin-storybook": "^0.6.15",
    "storybook": "^7.5.3",
    "typescript": "^5",
     "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests