How to theme Material UI inside Storybook
Asked Answered
A

5

13

Currently this is what I am doing, passing a ThemeProvider above my component file:

import React from 'react';
import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import MUIButton from '@material-ui/core/Button';

const theme = createMuiTheme({
  palette: {
    primary: {
      main: "#ff0000"
    }
  },
  typography: {
    fontFamily: 'Nunito Sans, sans-serif',
    button: {
      textTransform: 'none'
    }
  },
  shape: {
    borderRadius: 3
  }
})


export default ({ variant, children }) => {
  return (
    <ThemeProvider theme={theme}>
      <MUIButton
        color="primary"
        variant={variant}
      >
        {children}
      </MUIButton>
    </ThemeProvider>
  )
}

I am trying to figure out how I can do this at a global level in Storybook. This is the first component I have built out called Button. So I want to be able to have the theme in an external file, and have the ThemeProvider coming in at a higher level so I don't have to wrap each component. Hope that makes sense, and if anyone has any ideas.

Adur answered 15/5, 2020 at 20:37 Comment(0)
N
26

First, I suggest you to move your theme into a separate file (such as src/stylesheet so you can access it from different files (your global App component and your storybook preview file).

// src/stylesheet.ts

import { createMuiTheme } from '@material-ui/core/styles';

export const muiTheme = createMuiTheme({
  palette: {
    primary: {
      main: "#ff0000"
    }
  },
  typography: {
    fontFamily: 'Nunito Sans, sans-serif',
    button: {
      textTransform: 'none'
    }
  },
  shape: {
    borderRadius: 3
  }
})

Then, you need to setup your storybook the same way you set up your react app. To do so, try to create a file called: .storybook/preview.js and put this inside of it:

// .storybook/preview.js

import React from 'react';

import { addDecorator } from '@storybook/react';
import { ThemeProvider } from '@material-ui/core/styles';

import { muiTheme } from '../src/stylesheet';

addDecorator((story) => (
    <ThemeProvider theme={muiTheme}>{story()}</ThemeProvider>
));

It will wrap all your stories inside of the ThemeProvider.

In your app, you can can also have a global App component which will encapsulate the whole app within the ThemeProvider

More help: https://storybook.js.org/docs/basics/writing-stories/

Northwards answered 15/5, 2020 at 22:53 Comment(1)
For the new version of Storybook v6 uses like that export const decorators = [<ThemeProvider theme={muiTheme}>{story()}</ThemeProvider>]Tramway
E
19

This is the only solution that worked for me with MUI v5:

// .storybook/main.js

const path = require('path');
const toPath = (filePath) => path.join(process.cwd(), filePath);

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


// .storybook/preview.js

import { ThemeProvider, createTheme } from '@mui/material/styles';
import { ThemeProvider as Emotion10ThemeProvider } from 'emotion-theming';

const defaultTheme = createTheme(); // or your custom theme

const withThemeProvider = (Story, context) => {
  return (
    <Emotion10ThemeProvider theme={defaultTheme}>
      <ThemeProvider theme={defaultTheme}>
        <Story {...context} />
      </ThemeProvider>
    </Emotion10ThemeProvider>
  );
};

export const decorators = [withThemeProvider];

// ...other storybook exports

It's from the MUI migration guide but it took me unnecessarily long to find so I want to share it here: https://mui.com/guides/migration-v4/

Ephraim answered 16/12, 2021 at 9:29 Comment(7)
Yes, the only solution that worked! Thanks!Ezaria
only Emotion10ThemeProvider wrapper was needed in the preview.js in order to make this work in my case (no need to change /main.js config file)Wintry
Yes, you are absolutely right! this is the only solution that truly propagates down MUI V5 theme(including RTL support)Wintry
Thank you so much!Chlorous
Thanks! Not .storybook/main.js configuration was needed it. The Emotion10ThemeProvider was enough for my case to work.Rifle
This worked for me with MUI v5Boleyn
Thanks! This worked for me with the latest versions of Mui and Storybook! You are a lifesaver!Crick
R
13

For Storybook 6.3+ it should look like the following

Update preview.js with:

import React from 'react';
import { theme } from './src/theme'; // whereever you have defined your material ui theme

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

More details on how to decorate your stories here: https://storybook.js.org/docs/react/writing-stories/decorators

And creating your theme for Material UI here: https://material-ui.com/customization/theming/

Rainer answered 17/7, 2021 at 8:27 Comment(0)
O
2

I believe this is the most up to date answer... and the cleanest, updating path/to/your/Theme with your path

// .storybook/preview.js

import React from 'react';
import { ThemeProvider } from '@mui/system';
import { Theme } from 'path/to/your/Theme';

export const decorators = [
  (Story) => (
    <ThemeProvider theme={Theme}>
      <Story />
    </ThemeProvider>
  ),
];
Orpiment answered 5/8, 2022 at 1:0 Comment(0)
B
1

This works for me with react-router and mui V5

my preview.js =>

import React from 'react';
import { addDecorator } from '@storybook/react';
import { MemoryRouter } from 'react-router';
import { ThemeProvider } from '@mui/material/styles';
import { ThemeProvider as Emotion10ThemeProvider } from 'emotion-theming';
import customTheme from '../src/theme/index';

addDecorator((story) => (
    <Emotion10ThemeProvider theme={customTheme}>
        <ThemeProvider theme={customTheme}>
            <MemoryRouter initialEntries={['/']}>{story()}</MemoryRouter>
        </ThemeProvider>
    </Emotion10ThemeProvider>
));

export const parameters = {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
        matchers: {
            color: /(background|color)$/i,
            date: /Date$/,
        },
    },
};
Biagi answered 5/5, 2022 at 14:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.