Guide for Storybook
1. Getting started
1.1. Installation
bluh bluh bluh
1.2. Configure Storybook with Tailwind
To have Storybook and tailwind working together is rather simple.
All Storybook needs is a css, generated by tailwind as an output, as its input.
That same css file needs to be imported in the preview.js
configuration file of Storybook:
import '../styles/tailwind.css';
/** @type { import('@storybook/react').Preview } */
const preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;
This will make Tailwind’s style classes available to all of your stories.
To generate that css file with tailwind, all you gotta do is wrote a script that does so:
{
"pre-storybook-prod": "npx tailwindcss -o ./styles/tailwind.css --minify"
}
This is for production.
For development, you would most-likely want a watch
version of that script, so that you could make changes to the source code, and see changes in real-time:
{
"pre-storybook-dev": "npx tailwindcss -i ./src/index.css -o ./styles/tailwind.css --watch"
}
1.3. Configure Storybook With Dark Mode
First of all, update your tailwind.config.js file to change themes based on a class or data-attribute. This example uses a data-attribute.
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
// Toggle dark-mode based on .dark class or data-mode="dark"
darkMode: ['class', '[data-theme="dark"]'],
theme: {
extend: {},
},
plugins: [],
};
Next, install the @storybook/addon-themes addon to provide the switcher tool.
npm i -D @storybook/addon-themes
Then, add following content to .storybook/main.js:
export default {
addons: ['@storybook/addon-themes'],
};
Toggle themes by class name
Add the withThemeByClassName decorator to your Storybook from @storybook/addon-themes:
import { withThemeByClassName } from '@storybook/addon-themes';
/* snipped for brevity */
export const decorators = [
withThemeByClassName({
themes: { light: 'light', dark: 'dark' },
defaultTheme: 'light',
}),
];
Toggle themes by data-attribute
Add the withThemeByDataAttribute decorator to your Storybook from @storybook/addon-themes:
import { withThemeByDataAttribute } from '@storybook/addon-themes';
/* snipped for brevity */
export const decorators = [
withThemeByDataAttribute({
themes: {
light: 'light',
dark: 'dark',
},
defaultTheme: 'light',
attributeName: 'data-mode',
}),
];
2. The *.stories.jsx Files
Storybook scans your project and looks for files which end with: .stories.js
, .stories.jsx
, .stories.ts
, .stories.tsx
.
Notice how it has stories
in it's path, in plural, to note that each file representing a component can export multiple stories. A *.stories.js file defines all the stories for a component. Each story has a corresponding sidebar item in the Storybook app. When you click on a story, it renders in the Canvas an isolated preview iframe.
USE ONLY .stories.jsx
with jsx
extension!!
If you use .js
extension, than jsx you write would result in Storybook crashing! With a useless explanation as to why!
3. A Component's Meta
Each .stories.jsx
file must include a Component's meta
, and export default it.
The meta
is simply a javascript object with properties.
At the very least, the meta
object must contain the component
key, which points to the actual component.
The default export metadata controls how Storybook lists your stories and provides information used by addons. For example, here’s the default export for a story file Button.stories.js|ts:
import Button from './Button';
export default {
component: Button,
};
Starting with Storybook version 7.0, story titles are analyzed statically as part of the build process. The default export must contain a title property that can be read statically or a component property from which an automatic title can be computed. Using the id property to customize your story URL must also be statically readable.
4. Layout Centered
Another nice-to-have key inside meta is the parameters.layout
, which tells Storybook where to render the component on the screen. By default, it renders it on the top-left, but it would be nice to have it centered, right?
To do so, simply add:
import Button from './Button';
export default {
component: Button,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
layout: 'centered',
},
};
5 Writing a Story
5.1. Introduction
A story
is merely a javascript object which hold an args
key. combination of values for the component's props, which describes how to render the component.
A story
needs to be named-exported from the *stories.js
file.
A story
with an empty as an object, will simply mean that all the component's props are undefined.
The variable name holding the story
will be the name presented in the Storybook app, describing that story
, so it's a good idea to have it uppercased.
If a stories.js file does not export a single story, no visuals of that component would appear in the Storybook app. It would be like the component doesn't even exist.
import { YourComponent } from './YourComponent';
// 👇 This default export determines where your story goes in the story list
export default {
component: YourComponent,
};
export const FirstStory = {
args: {
// 👇 The args you need here will depend on your component
},
};
5.2. Defining stories
Use the named exports of a file to define your component’s stories. We recommend you use UpperCamelCase for your story exports. Here’s how to render Button
in the "primary" state and export a story called Primary
.
import { Button } from './Button';
export default {
component: Button,
};
/*
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
* See https://storybook.js.org/docs/api/csf
* to learn how to use render functions.
*/
export const Primary = {
render: () => <Button primary label='Button' />,
};