Using Storybook with Bit

Using Storybook to render React components in a Bit workspace

Eden Ella
Bits and Pieces

--

In this tutorial, we’ll use Storybook to render React components developed in a Bit workspace. But before we do so, we’ll briefly discuss their different roles in component-driven development.

If you’re unfamiliar with either, make sure to first go through their basic tutorials: Bit for React / Storybook for React.

The difference between Bit and Storybook

Bit transforms the way we structure our web projects and collaborate on code. It is a way to develop, version, and deliver components independently.

A ‘component’, in the context of Bit, could be any type of module, UI, and non-UI (e.g, React hooks, Node.JS modules, etc.)

Components developed and shared with Bit form a network of dependencies. That network is used by Bit to propagate updates from one modified component to all its dependents, in-and-across projects. That means every dependent component will be tested, built, and released with a new version.

Learn more about Bit’s independent components, here:

What all that means is that Bit should be seen as an alternative to the existing polyrepo and monorepo development strategies. It should not be seen as an alternative to Storybook, even though they both offer isolated component rendering.

A visualization of the dependency graph of a ‘dropdown’ component

Storybook does not affect the way we structure our projects. It is a way to render and examine components independently, either in monolithic projects or in monorepos. It allows us to inspect each component in a full range of states and variations, unlimited by its “hosting” projects.

In a nutshell, Storybook helps us resolve the discrepancy between the way we design our frontend projects — as compositions of components, and the way we build, deliver, and render them in the browser — as monoliths.

Understanding Bit’s isolated component previews

Since Storybook and Bit aim to solve different problems, they offer isolated component previews as tools for different goals.

Bit’s ‘compositions’ feature for isolated component previews, could be used as a way to examine components in a context-free environment (much like Storybook is often used) but that is not its main goal.

Compositions are primarily used as simulated integration tests. They are a way to validate that a component will be able to integrate successfully in its future consuming projects (before sharing it).

Why do we need “simulations”?

Bit promotes effective collaboration by enabling teams to work autonomously and deliver their features (i.e, components) independently. However, for this to work, each team must also be responsible for the way their products, their components, affect all consumers in their organization. This is where ‘compositions’ play an important role.

Setting up Storybook in a Bit workspace

Initialize Storybook in a Bit workspace

A Storybook initialized in a Bit workspace is not able to determine the type of technologies or frameworks used in a project, by itself. This is because a single Bit workspace may have different components using different frameworks.

It’s also because Storybook has no package.json file to analyze, as Bit workspaces only use aworkspace.jsonc to set configurations on every component in the workspace and on the workspace itself.

We’ll manually initialize a Storybook for React components using the --type option:

$ npx sb init --type react

Manage Storybook’s dependencies in a Bit workspace

Since dependencies in a Bit workspace are managed in the workspace.jsonc configuration file, we’ll move the added dependencies from the generated package.json file (generated by Storybook) to the workspace.jsonc file, and place them under the dependencies property.

We will also add the **/*.stories.tsx pattern to the devFilesPattern property, to automatically make every dependency of a ‘stories’ file, a dev dependency.

Bit auto-generates the dependency graph for each component by analyzing its files’ import / require statements.

The above dependency policy, in the workspace config root level, will only affect components that already have these dependencies listed in their auto-generated dependency graph.

This sort of dependency configuration allows us to run bit install to install all Storybook dependencies in our workspace but does not add these dependencies to each component’s dependency graph. That is the preferable way, as a Storybook setup is for the entire ptoject (in our case, the worksapce) and not for each individual component.

Configure Storybook to load stories from component directories

Each component in a Bit workspace should have all its files placed under a single directory. That includes a component’s ‘stories’ file. Files that are outside the directory will not get tracked by Bit as parts of the same component.

Set the .storybook/main.js file, in the workspace directory, to use a glob pattern that fits the way components are structured in a Bit workspace (i.e, one directory for each component).

module.exports = {
"stories": [
"../my-scope/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials"
]
}

Create a ‘stories’ file

My workspace already has a tracked button component. Let’s, add a ‘stories’ file to it:

$ cd my-scope/inputs/button
$ touch button.stories.tsx

Now, our button component directory looks like so:

├── my-component-library            
└── my-scope/inputs/button
├── button.composition.tsx # (Bit's) component previews
├── button.docs.mdx # component documentation
├── button.module.css # styles
├── button.spec.tsx # tests
├── button.stories.tsx # stories
├── button.tsx # implementation file
└── index.ts # the component's entry file

Create ‘stories’

This part has nothing new. Stories are written as they normally are.

For example:

import React from 'react';
import { Meta } from '@storybook/react';
export default {
title: 'inputs/Button',
component: Button,
} as Meta;
const Template = (args) => <Button {...args} />;export const InDefaultState = Template.bind({});InDefaultState.args = {
children: 'Click Me!',
isLoading: false,
};

Use ‘stories’ in Bit ‘compositions’

As you might expect, ‘args’, ‘templates’, and so on, are unique to Storybook and are not compatible with Bit compositions.

To use a story in a Bit composition, create a component with no additional [Storybook] metadata.

For example, the following story will show the button component in a dark theme context. We’ll achieve that using two other components in our workspace, the theme-provider (a React component) and the dark-palette (a CSS component).

// button.stories.tsximport React from 'react';
import { Meta } from '@storybook/react';
import { ThemeProvider } from '@my-scope/themes.theme-provider';
import { darkPalette } from '@my-scope/themes.dark-palette';
import { Button } from './button';
export default {
title: 'inputs/Button',
component: Button,
} as Meta;
const Template = (args) => <Button {...args} />;export const UsingDarkTheme = () => {
return (
<ThemeProvider className={darkPalette}>
<Button>Click Me!</Button>
</ThemeProvider>
);
};

Now, we are able to import that story into our button.composition.tsx file, just like any other component.

// button.composition.tsximport React from 'react';
import { UsingDarkTheme } from './button.stories';
export const ButtonInDarkMode = () => <UsingDarkTheme />;
Our button component rendered in isolation in Bit’s workspace UI ‘compositions’ tab

Limitations

As mentioned earlier Bit does not offer Storybook integration. The two tools work in parallel, with separate dev servers and configurations.

It’s important to realize that a Bit workspace is not a standard project. It is a temporary local hub for distributed independent components.

Since Storybook is designed to work with ‘projects’, either monoliths or monorepos, we are forced to limit our use of Bit so that a Bit workspace would act more like a standard project. That’s done in a few ways.

1. Use a single component dev environment in each Bit workspace

Components developed in the same Bit workspace may use different component development environments (using different frameworks).
Storybook is expecting to find just a single development setup. So, for a successful coupling of the two tools, make sure to use just a single framework in a single Bit workspace.

2. Collaborate on independent components using (only) their original workspace

Once a component is ‘exported’ (pushed and published) from its authoring Bit workspace, it can be updated from any other Bit workspace.
However, your Storybook setup will not be part of your components’ data (unlike other dev environment configs set by Bit). It will remain a workspace-level setup. For that reason, it is advisable to maintain these components only in a single Bit workspace.

Conclusion

Bit and Storybook are two very different tools for component-driven development. They are able to work together but (almost) only in parallel and only when using Bit in a limited way.

These limitations are quite reasonable when using the two tools for single projects, like a set of components developed for a single “component library” or design system.

--

--