Installation

Using Panda CSS with Storybook

Setting up Panda CSS in a Storybook project using PostCSS.

Setup

We are assuming that you already have a project set up with a framework like React, Vue or Svelte.

Install Storybook

Storybook needs to be installed into a project that is already set up with a framework. It will not work on an empty project.

pnpm dlx storybook@latest init

Install Panda

Install panda and create your panda.config.ts file.

pnpm install -D @pandacss/dev
pnpm panda init --postcss
💡

If you are using Storybook with the Vite builder, you will have to update your PostCSS config file to use the array syntax for the plugins instead of the object syntax. Simply change postcss.config.[c]js:

postcss.config.js
module.exports = {
-  plugins: {
-   '@pandacss/dev/postcss': {}
-  }
+  plugins: [require('@pandacss/dev/postcss')()]
}

Update package.json scripts

Open your package.json file and update the scripts section as follows:

web/package.json
{
  "scripts": {
+    "prepare": "panda codegen"
  }
}
  • "prepare" - script that will run Panda CSS CLI codegen before each build. Read more about codegen in the CLI section.
💡

This step ensures that the panda output directory is regenerated after each dependency installation. So you can add the output directory to your .gitignore file and not worry about it.

Configure the content

Make sure that all of the paths of your Storybook components are included in the include section of the panda.config.ts file.

panda.config.ts
import { defineConfig } from "@pandacss/dev"
export default defineConfig({
 // Whether to use css reset
 preflight: true,
 // Where to look for your css declarations
 include: [
  "./src/**/*.{js,jsx,ts,tsx}",
  "./pages/**/*.{js,jsx,ts,tsx}",
  "./stories/**/*.{js,jsx,ts,tsx}",
 ],
 // Files to exclude
 exclude: [],
 // The output directory for your css system
 outdir: "styled-system",
})

Configure the entry CSS with layers

Locate your main CSS file and add the following layers:

src/index.css
@layer reset, base, tokens, recipes, utilities;

Import the CSS in your Storybook config

Locate your .storybook/preview.ts file and import the CSS file.

In this example CSS file is located in the src folder.

.storybook/preview.ts
import "../src/index.css";
 
import type { Preview } from "@storybook/react";
 
const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: "^on[A-Z].*" },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
};
 
export default preview;

Start the Storybook server

Run the following command to start your Storybook server.

pnpm storybook

Start using Panda

Now you can start using Panda CSS in Storybook.

Here is the example of a Button component and its corresponding Storybook story:

src/stories/Button.tsx
import { ReactNode } from 'react';
import { css } from '../../styled-system/css';
 
export interface IButtonProps {
  children: ReactNode;
}
 
export const Button = ({ children }: IButtonProps) => {
  return (
    <button
      className={css({
        bg: 'red.300',
        fontFamily: 'Inter',
        px: '4',
        py: '3',
        borderRadius: 'md',
        _hover: { bg: 'red.400' },
      })}
    >
      {children}
    </button>
  )
};
src/stories/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { css } from '../../styled-system/css';
 
import { Button } from './Button';
 
const meta = {
  title: 'Example/Button',
  component: Button,
  tags: ['autodocs'],
  decorators: [
    (Story) => (
      <div className={css({ m: 10 })}>
        <Story />
      </div>
    ),
  ],
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const Default: Story = {
  args: {
    children: 'Hello 🐼!',
  },
};

Configuring Dark Mode

To enable dark mode in Storybook, you can use the @storybook/addon-themes package.

pnpm add -D @storybook/addon-themes

Then, update your .storybook/preview.ts file to include the following:

.storybook/preview.ts
import { withThemeByClassName } from '@storybook/addon-themes';
import type { Preview, ReactRenderer } from '@storybook/react';
 
const preview: Preview = {
  // ...
  decorators: [
    withThemeByClassName<ReactRenderer>({
      themes: {
        light: '',
        dark: 'dark',
      },
      defaultTheme: 'light',
    }),
  ],
};
 
export default preview;

With that in place, you should see the light/dark switcher in Storybook's toolbar.

Troubleshooting

Cannot find postcss plugin

If you are having issues with the PostCSS plugin similar to Cannot find module '@pandacss/dev/postcss', update the PostCSS config as follows:

postcss.config.js
module.exports = {
  plugins: [require('@pandacss/dev/postcss')],
}

HMR not triggered

If you are having issues with HMR not being triggered after a panda.config.ts change (or one of its dependencies), you can manually specify the files that should trigger a rebuild by adding the following to your panda.config.ts:

panda.config.ts
import { defineConfig } from "@pandacss/dev"
 
export default defineConfig({
  // ...
  dependencies: ['path/to/files/**.ts'],
})

Styles in args is not generated

If you are having issues with your args not generating the expected CSS, it's probably because of:

stories/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { button } from '../../styled-system/recipes';
 
export const Funky: Story = {
  // mark this as a button recipe usage
  args: button.raw({
    visual: 'funky',
    shape: 'circle',
    size: 'sm'
  })
}

Some recipes styles are missing

If you are having issues with your config recipes or slotRecipes not generating the expected CSS, it's probably because of:

  • you didn't add a file glob for the Storybook files in your config.include like "./stories/**/*.{js,jsx,ts,tsx}"
  • you haven't used every recipes variants in your app, so you might want to pre-generate it (only for storybook usage) with staticCss
panda.config.ts
import { defineConfig } from "@pandacss/dev"
 
export default defineConfig({
  // ...
  staticCss: {
    recipes: '*'
  },
})