DEV Community

Cover image for Build a Web Component library with Stencil and Storybook
Felix Haus
Felix Haus

Posted on • Edited on

Build a Web Component library with Stencil and Storybook

In this little tutorial we learn how to add Web Components created with Stencil to a Storybook setup. Stencil is a framework which let us write Web Components in a JSX-style syntax similar to React's class-components. It then compiles it to native Web Components code to make it usable in the browser.

1. Create a new Stencil project

First we initialize a new Stencil project from scratch with Stencil's handy CLI tool. It generates an initial project structure for us where we can simply add new components later on.

Note: If you already have a Stencil project you can skip this step and go straight to Step 2.

npm init stencil
Enter fullscreen mode Exit fullscreen mode

In the following dialog Stencil asks us to pick a project type. Since we want to generate a library of Web Components choose the component option here and continue.

Screenshot of Stencils project wizard where the user can choose the type of the project

In the last step of the project generator we choose a name for the project, for simplicity we name it storybook-stencil-example.

Screenshot of Stencils project wizard where the user can choose the name of the project

Now we navigate into our newly created stencil project and install the dependencies:

cd storybook-stencil-example  # Navigate into project dir
npm i                         # Install dependencies
Enter fullscreen mode Exit fullscreen mode

2. Add Storybook to the project

Because Stencil components are compiled to Web Components we simply can use Storybook for HTML project type here:

# Bootstrap storybook
npx -p @storybook/cli sb init --type html

# Install additional dependencies for our setup
npm i --save-dev \
  write-file-webpack-plugin \
  copy-webpack-plugin \
  @storybook/addon-notes
Enter fullscreen mode Exit fullscreen mode

Now we need to make some changes to the default configuration from Storybook. We

// .storybook/main.js
const fs = require('fs');
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
const WriteFilePlugin = require('write-file-webpack-plugin');

const OUTPUT_DIR = '../dist';
// Stencil names the project entry the same as the project
// Look for the file `dist/<your-project-name>.js` to find out what to insert here
const PROJECT_NAME = 'storybook-stencil-example';

module.exports = {
  stories: ['../src/**/*.stories.js'],
  addons: ['@storybook/addon-notes/register'],
  // Custom webpack config to tell Storybook where to find the compiled files from Stencil
  async webpackFinal(config) {
    config.entry.push(path.join(__dirname, OUTPUT_DIR, `${PROJECT_NAME}.js`));
    fs.readdirSync(path.join(__dirname, OUTPUT_DIR, 'collection/components')).map(file => {
      jsFilePath = path.join(__dirname, OUTPUT_DIR, `collection/components/${file}/${file}.js`);
      try {
        if (fs.existsSync(jsFilePath)) {
          config.entry.push(jsFilePath);
        }
      } catch (err) {
        console.error(err);
      }

      // Add CSS
      let cssFilePath = path.join(
        __dirname,
        OUTPUT_DIR,
        `collection/components/${file}/${file}.css`
      );
      try {
        if (fs.existsSync(cssFilePath)) {
          config.entry.push(cssFilePath);
        }
      } catch (err) {
        console.error(err);
      }
    });

    // Add all static files to Storybook
    config.plugins.push(
      new CopyPlugin([
        {
          from: '**/*',
          to: './',
          context: 'dist',
        },
      ])
    );

    // Write the files to disk and not to memory
    config.plugins.push(new WriteFilePlugin());

    return config;
  },
};
Enter fullscreen mode Exit fullscreen mode

3. Create our first story

The Stencil project setup has already added a sample component for us named my-component. So let's create our first story for this component. Inside the component folder add a new file my-component.stories.js:

src/
└── components/
    └── my-component/
        ├── my-component.css
        ├── my-component.e2e.ts
        ├── my-component.stories.js  # <-- Add the file here
        ├── my-component.tsx
        └── readme.md
...
Enter fullscreen mode Exit fullscreen mode

Of course we could also write the stories in TypeScript because stencil already relies on it but to keep things simple we use plain JavaScript for now.

// my-component.stories.js
import readme from './readme.md';

export default {
  title: 'My Component',
  parameters: {
    markdown: readme,
  },
};

export const Default = () => `
  <my-component first="Millie" middle="Bobby" last="Brown"></my-component>
`;
Enter fullscreen mode Exit fullscreen mode

Importing the auto-generated readme.md from Stencil gives us a free documentation of our component which can also be showed in Storybooks "Notes" tab of the component.

4. Development workflow

To make the best use of Stencil and Storybook, both support a live-reload development workflow so that we can see changes we make to our Stencil components directly in Storybook without reloading the browser.
To make this work we start the Stencil dev server and the Storybook dev server parallel in two different terminals:

# Terminal 1 - Run Stencil compiler in watch mode
npm start

# Terminal 2 - Run Storybook
npm run storybook
Enter fullscreen mode Exit fullscreen mode

Now the final result should now look like this in the browser. The canvas gives us a preview of our component while the note tab holds the documentation of the component itself.

Screen recording of the final result where a Stencil component is loaded inside storybook

You can also see a live-demo of the Storybook here: https://ofhouse.github.io/storybook-stencil-example

This is only a basic setup guide for Stencil and Storybook, to make use of Storybook's advanced features like Knobs or Actions I will add a second tutorial shortly. So follow me for more content!


This guide was made possible by the work of of Bobby Johnson. He has made a really nice video of the whole process of his Stencil and Storybook setup here: Using StencilJS with Storybook on YouTube


You can find the whole example project from this tutorial in the following repository:

GitHub logo ofhouse / storybook-stencil-example

Example project which uses Stencil components in Storybook.

Built With Stencil

Storybook Stencil Example

This is a starter project for building a Web Component library with Stencil and Storybook.

Getting Started

To start clone this repo into a new directory and run the following commands:

npm install
npm start
Enter fullscreen mode Exit fullscreen mode

For a step-by-step tutorial how to recreate this from scratch you can follow this series of blog posts:

Features

Author

License

MIT - see LICENSE for details.






Top comments (4)

Collapse
 
vprothais profile image
vprothais

Hello,
Thank you for this article. For information, with newer versions of stencil and storybook I had to make some adjustments in order to make it works.

The Webpack config uses the collection folder that seems to not be created by default with the npm start with stencil 1.12. I had to modify the start script in order to make it build es5 files in dev mode :

"start": "stencil build --dev --watch --serve --es5",
Enter fullscreen mode Exit fullscreen mode

The Storybook Notes plugin (5.3.18) documentation gives a different object in order to use markdown. You need a notes node in the story parameter.

export default {
  title: 'My Component',
  parameters: {
    notes: {
      markdown: readme
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Hope it can help. Have a good day.

Collapse
 
jborja79 profile image
jborja79

hey, great article, also with the newer version of copy-webpack-plugin is necessary patterns node in storybook config .storybook/main.js

      new CopyPlugin({
        patterns: [
          {
            from: '**/*',
            to: './',
            context: 'dist',
          }
        ],
      }),
    );```


Enter fullscreen mode Exit fullscreen mode
Collapse
 
aungkyawhein profile image
Aung Kyaw Hein

This article is very helpful. Thank you so much for this.
There are a few more things to update.
The latest version of copy-webpack-plugin (v7.0.0) will not work. So, I had to downgrade it to v6.2.1.
And stencil build doesn't automatically generate dist/<your-project-name>.js. It will generate dist/<your-project-name>/<your-project-name>.esm.js only. I had to add buildEs5: true in stencil.config.

Collapse
 
normabkrug profile image
normabkrug

please share full info here.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.