Migrating from Styleguidist to Storybook

June 6, 2022

Originally posted on Yelp's Engineering Blog

One of the core tenets for our infrastructure and engineering effectiveness teams at Yelp is ensuring we have a best-in-class developer experience. Our React monorepo codebase has steadily grown as developers create new React components, but our existing React Styleguidist (Styleguidist, for short) development environment has failed to scale in parallel. By transitioning from Styleguidist to Storybook, we were able to offer a faster and more user-friendly development environment for React components along with better alignment to developer and designer workflows. In this post we’ll take a deep dive into how and why we migrated to Storybook.

Background

Status Quo

Styleguidist is an interactive React component development environment that developers use to develop and view their user interfaces. Styleguidist can also be used to produce static documentation pages (style guides) that can be hosted and shared with stakeholders.

Documentation is created using Markdown with code blocks that render a React component in an isolated interactive playground. A simple example looks like the following:

The `<ButtonGroup />` component is used to arrange multiple `<Button />`
components side-by-side.
 
```jsx
const Button = require('../Button').default;
 
<ButtonGroup>
    <Button text="Foo" />
    <Button text="Bar" />
    <Button text="Baz" />
</ButtonGroup>;
```
An example Styleguidist playground

An example Styleguidist playground

At Yelp, we’ve encountered various drawbacks from using Styleguidist that have led to a subpar React development experience:

Why Storybook?

Storybook is an open source UI development and documentation tool that has gained popularity in the Web community in the past few years. It has strong community support and a rich add-ons ecosystem, making it easy to extend for accessibility testing, cross-browser testing, and other functionality.

Storybook allows users to browse and develop component examples one by one via Stories. Stories capture the rendered state of a React Component, just like a Styleguidist Markdown example. This contrasts with the significantly slower Styleguidist, which always renders every example of every component in a package.

In Styleguidist, developers often create one example per visual permutation of their component, resulting in added maintenance burden (e.g. updating every example after changing a component API). In Storybook, developers can utilize auto generated Controls via react-docgen that allow users to mutate and preview components directly in the documentation UI. This further streamlines the experience compared to Styleguidist, because documentation users no longer need to edit Markdown to change a component’s state.

An example Storybook playground

An example Storybook playground

The Migration

Our React monorepo contained thousands of Styleguidist files, each with many examples of component usage within it. It was not feasible to migrate these by hand, and it would be unreasonable to force developers to manually rewrite their examples in the new Storybook format. To maintain our existing React component examples and reduce developer overhead in our migration, we developed the following requirements:

With these goals in mind, we decided to use codemods to refactor our style guide files into the Storybook format. Codemods are a series of scripted actions that transform a codebase programmatically, and allow for large automated changes to be made without manual work.

First we extracted the Styleguidist code blocks; the rest of the contents of the Markdown file (e.g. plaintext descriptions) could be directly copied verbatim to the new MDX file. To achieve a one to one migration, we consider each code block as its own Story. We were able to leverage existing tools like remark-code-blocks to extract Javascript codeblocks, and 5to6-codemod to convert ES5 syntax within these codeblocks to ES6 syntax.

// before:
// const Button = require('../Button').default;
import Button from '../Button';

To reduce developer friction during this transition, we decided to contain all Stories for a component in the same component.stories.js file, which is then displayed in the component.stories.mdx Docs Page. However, we discovered that MDX code blocks are run in the same context, and our assumption of maintained playground isolation from Styleguidist is no longer true. This issue is particularly problematic when dealing with transforming multiple Styleguidist examples in the same file, because joining the code blocks together results in duplicate imports:

```jsx
import Button from '../Button';
 
Full width `ButtonGroup` example:
<ButtonGroup fill>
(omitted for brevity)
```
 
```jsx
import Button from '../Button'; // <-- this import is duplicated from above!
 
Disabled `ButtonGroup` example:
<ButtonGroup disabled>
(omitted for brevity)
```

After combining the above stories into a single JS file, the Button import is duplicated. Our codemod needs to parse and dedupe these imports to prevent runtime errors. Additionally, we need to include the components that Styleguidist implicitly imports for us:

// ButtonGroup.stories.js
import Button from '../Button'; // deduped
import { ButtonGroup } from './'; // added implicit import explicitly
 
<ButtonGroup>
    <Button text="Foo" />
    <Button text="Bar" />
    <Button text="Baz" />
</ButtonGroup>;

Next, we write the extracted Markdown code blocks with deduped imports and ES6 syntax in component.stories.js, and a component.stories.mdx file with standard Storybook boilerplate:

// ButtonGroup.stories.mdx
import { ArgsTable, Canvas, Description, Meta, Story } from '@storybook/addon-docs';
import * as stories from './ButtonGroup.stories.js';
import { ButtonGroup } from './';
 
<Meta
    title="yelp-react-component-button/ButtonGroup"
    component={ButtonGroup}
/>
 
The `<ButtonGroup />` component is used to arrange multiple `<Button />`
components side-by-side.
 
<Canvas>
  <Story name="Example0" story={stories.Example0} />
</Canvas>
 

Lastly, we needed Storybook to understand how to build our components. We were able to extend the Storybook build configuration with our existing production webpack configuration. This allowed us to preserve Storybook’s automatic docgen functionality, and miscellaneous features like code preview blocks. Using our existing webpack configuration also meant that components would appear and behave exactly as they do in real production pages.

Conclusion

Migrating our React component examples from Styleguidist to Storybook has massively improved developer experience and component playground performance. We were able to utilize Storybook features like on-demand loading to improve performance by generating a smaller bundle at compile time, resulting in faster playground boot times. Using our codemod migration strategy, we were able to transform nearly all of the examples in our monorepo without runtime errors, without blocking developers during the migration process.

Switching to Storybook opens up new possibilities for Yelp, and we’re excited to onboard add-ons to accelerate frontend developer productivity further.

We hope that this breakdown in our migration process helps teams facing similar migrations!

Back home