How to use Storybook with Stencil
Storybook is an incredible tool that allows you to build, test, and document the components of your design system in isolation. Storybook provides a streamlined workflow and tons of addons to improve the developer experience. It is because of this that Storybook is used by indie developers and enterprise teams alike. In this tutorial, we are going to learn how to integrate Storybook into a Stencil project to make building, testing, and documenting even easier.
You can find all of the code for this tutorial on the Stencil Storybook repo here. If you prefer to watch a video tutorial, I recently created a video where I walk through this entire process. Check it out!
Storybook Stories
Stories are the foundation of any Storybook project. A story is a function that is meant to represent a particular state of a component. Multiple stories can be written for a single component to represent all of its different states. You can learn more about stories and how to write them from the Storybook docs. I just want to introduce the idea here, as we’ll be talking about them throughout the tutorial.
Add Storybook
The first thing we’ll want to do is add Storybook to our Stencil project. For this tutorial, I’ll be using the Stencil starter project that you get from running npm init stencil
, but feel free to use any Stencil project. To add Storybook, open the terminal in your Stencil project and run:
npx sb init --type html
The Storybook init
command will set up all the files and dependencies necessary for us to use Storybook. We’re also specifying our Storybook project type to be html
via the --type
flag. We do this because Stencil generates web components, and in this tutorial, we’re going to be using those web components in our stories as custom HTML elements.
Once installed, we can run Storybook with the command:
npm run storybook
You’ll notice that Storybook already has some example stories set up for us. These example stories come from the stories
folder that Storybook created in our project. I’m going to delete this folder, as we won’t be using it in this tutorial. Nevertheless, these files can serve as a great reference point, especially if you’re new to Storybook.
Define Our Custom Elements
In our Stencil project, you’ll notice that Storybook created a .storybook
folder for us. In this folder we have a preview.js
file. This file is where we put any kind of global code that we may have. This is really important because it’s where we’re going to define our Stencil components, so we can use them in our stories.
To do this, add the following at the top of preview.js
:
import {defineCustomElements} from '../loader';
defineCustomElements();
Here we are first importing our defineCustomElements
method from the loader
that Stencil generatedgenerates at build time. Calling defineCustomElements()
essentially registers all of our components so they can be used in our stories. Because this loader is generated at build time, we have to run:
npm run build
Or
stencil build
Writing Our First Story
With all our configurations in place, we can now write our first story. For our story, we’ll be using the MyComponent
component that is generated when you create a new Stencil project.
import { Component, Prop, h } from '@stencil/core';
import { format } from '../../utils/utils';
@Component({
tag: 'my-component',
styleUrl: 'my-component.css',
shadow: true,
})
export class MyComponent {
/**
* The first name
*/
@Prop() first: string;
/**
* The middle name
*/
@Prop() middle: string;
/**
* The last name
*/
@Prop() last: string;
private getText(): string {
return format(this.first, this.middle, this.last);
}
render() {
return <div>Hello, World! I'm {this.getText()}</div>;
}
}
First, let’s create a file where we’ll write our stories. Inside our my-component
folder, let’s create a file called my-component.stories.ts
.
NOTE: This file does not have to be created within the
my-component
folder. You could instead have astories
folder that contains all the stories for every component. This is just the way that I like to organize things. All that matters is that your stories files end in.stories.ts/js
.
The first thing we’ll add to our my-component.stories.ts
file is the default export. This is an object that allows us to define all kinds of metadata about our stories. For our purposes, we’ll just provide a title
for our stories. This title will manifest in the left navigation pane of our Storybook instance.
export default {
// this creates a ‘Components’ folder and a ‘MyComponent’ subfolder
title: 'Components/MyComponent',
};
Next we’re going to make a template for our stories. This is a common pattern that allows us to make multiple stories based on the same component. To do this, we’ll create a function called Template
that takes in an args
object and then returns our component with those args as attribute values.
const Template = (args) => `<my-component first="${args.first}" middle="${args.middle}" last="${args.last}"></my-component>`;
Once the template is created, we can create stories by using named exports. Our stories will bind to our Template
and specify the args
that we want to pass to our component.
export const Example = Template.bind({});
Example.args = {
first: 'Winnie',
middle: 'The',
last: 'Pooh'
};
With this pattern, we can create all kinds of stories to demonstrate different use cases of our component. We can also use the “Controls” in Storybook to directly edit the values that are passed to our component.
Update on Save
Turning back to our Storybook instance, everything seems to be working great! Our component is rendering properly and we can use Storybook’s controls to instantly change the values we pass to the component. In addition, if we click on the “Docs” tab and then click “Show code”, we can see how the component is being used.
One thing you may notice, though, is that if you make a change to your component, that change will not be reflected in Storybook. This is because our component is being defined by our loader and that loader only updates when we perform a build. So to propagate the change, we need to rebuild our Stencil project by running:
npm run build
Or
stencil build
Once we do that, our changes will propagate through to Storybook. Unfortunately, running a build every time we make a change is not a great developer experience. To automatically build our project when a change is made, we can run:
stencil build --watch
Now Stencil will watch for any changes in our project and rebuild our project when they occur. This means when we save a change, that change will be visible in Storybook.
Conclusion
This tutorial provideds an introduction to setting up Storybook in your Stencil project. There is much, much more that you can do with Storybook and Stencil, so I encourage you to check out the Storybook docs and play around with it in your Stencil project. I can’t wait to see all the ways you use Storybook in your Stencil design system.