Back to blog

Component Story Format 3 is here

Next gen story format to make you more productive

loading
Michael Shilman
β€” @mshilman
Last updated:

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.

Join the Storybook mailing list

Get the latest news, updates and releases

6,587 developers and counting

We’re hiring!

Join the team behind Storybook and Chromatic. Build tools that are used in production by 100s of thousands of developers. Remote-first.

View jobs

Popular posts

Improved type safety in Storybook 7

CSF3 syntax combined with TypeScript satisfies gives you stricter types and an improved developer experience
loading
Kasper Peulen
Join the community
6,587 developers and counting
WhyWhy StorybookComponent-driven UI
Open source software
Storybook

Maintained by
Chromatic
Special thanks to Netlify and CircleCI