Component Story Format 3 is here
Next gen story format to make you more productive
Stories visualize how a component behaves in different scenarios. Component Story Format (CSF) is the universal file format for stories.
Component Story Format 3 marks an evolution in stories that trims boilerplate code and improves ergonomics. This makes stories more concise and faster to write.
I'm excited to announce the full release of CSF3. During the 18 month beta period, the community helped us find issues and refine the format. Starting with Storybook 7, CSF3 will be the default way to write stories.
Improvements include:
- โป๏ธ Spreadable story objects to easily extend stories
- ๐ Default render functions for brevity
- ๐ Automatic titles for convenience
- โถ๏ธ Play functions for scripted interactions and tests
- โ 100% backwards compatible with CSF 2
Read on to learn more about the format, whatโs changed since the original announcement, and how to make the most of it in Storybook 7.
Wait, but why?
Developing UI components outside of your application is the best way to create high-quality components. Storybook pioneered this style of Component-driven Development (CDD).
Stories are now used for visual review by designers and product managers, as well as for design system documentation, automated testing, and even generating design assets from production components.
It's no surprise that Storybook is used to build many of the world's most popular UIs at Shopify, IBM, and Salesforce.
CSF3 is the next evolution of storiesโeasier to write and maintain
Much like its predecessor, CSF3 is based on ESM. The default export contains information about the component, and one or more named exportsโstoriesโcapture different component states. The main difference is that stories are now objects, and you can attach a play function to each story to simulate user interactions.
CSF3 is fully backwards compatible with CSF2, which is still supported in Storybook 7. Weโve even back ported play functions to CSF2.
We recommend migrating because CSF3 is more expressive and maintainable with less boilerplate code required from you. In most cases, you can automatically migrate from CSF 2 to 3 using a codemod.
CSF3 feature recap
Large projects can consist of hundreds of components and thousands of stories. When you write this many stories, ergonomic improvements result in noticeable quality of life improvements. Our goal was to streamline the story format to make writing, reading, and maintaining stories easier.
Let's see what CSF3 looks like in a hypothetical RegistrationForm
component.
The default export declares the component that youโre writing stories for:
// RegistrationForm.stories.js
import { RegistrationForm } from './forms/RegistrationForm';
export default {
title: 'forms/RegistrationForm',
component: RegistrationForm,
};
And stories are now objects:
export const EmptyForm = {
render: (args) => <RegistrationForm {...args} />,
args: { /* ... */ },
parameters: { /* ... */ },
};
Default render functions for brevity. 90% of the time, writing a story is just passing some inputs to your component in a standard way.
In CSF3, if you donโt specify the render function, each story renders the component and passes in all arguments. This greatly simplifies your code.
In our RegistrationForm
example, the render function is boilerplate:
export const EmptyForm = {
// render: (args) => <RegistrationForm {...args} />, -- now optional!
args: { /* ... */ },
parameters: { /* ... */ },
};
Spreadable story objects for reuse. When youโre trying to model complex states, itโs useful to be able to extend existing stories instead of writing them anew. Suppose you want to show the filled form, but in a different viewport:
export const FilledForm = {
args: {
email: 'marcus@acme.com',
password: 'j1287asbj2yi394jd',
}
};
export const FilledFormMobile = {
...FilledForm,
parameters: {
viewports: { default: 'mobile' }
},
};
Play functions for scripted interactions. Some UI states are impossible to capture without user interaction. The play function runs after the story has been rendered, and uses testing-library
to simulate user interactions. For example:
// RegistrationForm.stories.ts|tsx
import { userEvent, within } from '@storybook/testing-library';
// ...
export const FilledForm = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const emailInput = canvas.getByLabelText('email', {
selector: 'input'
});
await userEvent.type(emailInput, 'example-email@email.com', {
delay: 100
});
const passwordInput = canvas.getByLabelText('password', {
selector: 'input'
});
await userEvent.type(passwordInput, 'ExamplePassword', {
delay: 100
});
const submitButton = canvas.getByRole('button');
await userEvent.click(submitButton);
},
};
Automatic titles for convenience. In CSF, a storyโs title determines where it shows up in the navigation hierarchy in the UI. In CSF3, the title can be automatically generated based on the fileโs location relative to the root. Less to type, and nothing to update if you reorder your files.
export default {
// title: 'forms/RegistrationForm' -- optional
component: RegistrationForm,
};
For an in-depth description of CSF3โs features and rationale, and exactly how it differs from CSF2, please see the original CSF3 post.
Changes to the original
Over the past year and half, users have been testing CSF3 in their projects. Based on feedback weโve made a few changes from the original.
Better TypeScript types. Weโve updated the Meta/Story types in 7.0 to support type safety and autocompletion for stories. Stay tuned for a dedicated post about this in the coming weeks.
Updated autotitle heuristics. Autotitle generates the โtitleโ (path in the Storybook sidebar) based on a CSF fileโs disk location. For example, if /project/path/src
is the story root, /project/path/src/atoms/Button.stories.js
would get the title atoms/Button
. However, the naive heuristic doesnโt handle common patterns like atoms/Button/Button.stories.js
or atoms/Button/index.stories.js
. We updated the heuristics to account for this. For full migration instructions and several other autotitle improvements including better prefix handling and capitalization, see MIGRATION.md.
Upgraded documentation and CLI templates. Last but not least, weโve upgraded the official documentation for 7.0 with CSF3 source snippets. Weโve also updated the CLI to generate CSF3 for new projects.
Upgrade to CSF3 today
CSF3 is fully backwards compatible, so your existing CSF stories still work fine without modification. We wonโt deprecate the old format any time soon. However, CSF3 is a big step forward, and we recommend upgrading your stories as part of upgrading to Storybook 7.0 (SB7).
To upgrade to SB7, see our SB7 migration guide. After your project is upgraded, you can optionally migrate your CSF stories to CSF3 using the following codemod. Be sure to update the glob to include the files you want to update.
npx storybook@next migrate csf-2-to-3 --glob="**/*.stories.js"
If you cannot use the codemod, we also have upgrade instructions available.
Get involved
Component Story Format (CSF) helps you develop, test, and document your components in isolation. With CSF3 comes improved ergonomics to help you write more stories with less effort.
CSF3 was developed by Michael Shilman (me!), Kasper Peulen, Tom Coleman, and Pavan Sunkara with testing and feedback from the entire Storybook community.
Storybook is the product of over 1600 community committers. Join us on GitHub or chat with us on Discord. Finally, follow @storybookjs on Twitter for the latest news.