Javascript is required
7 min read

Run, Build & Deploy Stencil and Storybook From One Repository

Run, Build & Deploy Stencil and Storybook From One Repository Image

I recently joined a project where the team used two separate Git repositories for their web components based on Stencil and Storybook. But the idea of Storybook is that the so-called "stories" live next to the components source code. Therefore, it made no sense to me to have those two tools in different repositories, and I combined them both in one repository.

My goal was that developers can also use Storybook stories via hot reload during development. Additionally, it should still be possible to separately deploy the web components to a npm registry and Storybook to a public URL.

This article describes the necessary steps to combine Storybook and Stencil in one repository. I wrote this article as there is currently no official documentation available on how to use Storybook with Stencil. Let's start with some basics.


Stencil is a toolchain for building reusable, scalable Design Systems. Generate small, blazing fast, and 100% standards based Web Components that run in every browser.

Stencil combines the "best concepts of the most popular frameworks into a simple build-time tool" that provides features like:

  • TypeScript support
  • JSX support
  • One way data-binding

As you can see from these picked concepts, Stencil is a React-inspired web component library. I previously worked with lit-element but due to the above-mentioned features, I prefer working with Stencil, especially in React projects.

Init Stencil

Let's create a new Stencil project which will be the base for the demo project of this article which is available at GitHub:

npm init stencil

We choose the component starter as we want to build a web component library that can be shared via npm:

1? Pick a starter › - Use arrow-keys. Return to submit.
3  ionic-pwa     Everything you need to build fast, production ready PWAs
4  app           Minimal starter for building a Stencil app or website
5 component     Collection of web components that can be used anywhere

Now we modify the automatically created my-component.tsx to be a bit more complex:

1export interface CompOption {
2  value: string
3  displayText: string
7  tag: 'my-component',
8  styleUrl: 'my-component.css',
9  shadow: true,
11export class MyComponent {
12  /**
13   * The text which is shown as label
14   */
15  @Prop() label: string
17  /**
18   * Is needed to reference the form data after the form is submitted
19   */
20  @Prop({ reflect: true }) name: string
22  /**
23   * If true, the button is displayed as disabled
24   */
25  @Prop({ reflect: true }) disabled = false
27  /**
28   * Define the available options in the drop-down list
29   */
30  @Prop() options: CompOption[] = []
32  render() {
33    return (
34      <div>
35        <label htmlFor={}>{this.label}</label>
37        <select name={} id={} disabled={this.disabled}>
38          { => (
39            <option value={o.value}>{o.displayText}</option>
40          ))}
41        </select>
42      </div>
43    )
44  }

Our demo component is a native HTML select component that gets its options passed via property. Some values like the label text, the component name, and if the component is disabled are also passed via props to the web component.

Run Stencil web components

To be able to locally test our demo component we need to adjust src/index.html which is used if we start Stencil:

1<!DOCTYPE html>
2<html dir="ltr" lang="en">
3  <head>
4    <meta charset="utf-8" />
5    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
6    <title>Stencil Component Starter</title>
8    <script type="module" src="/build/ui-kit.esm.js"></script>
9    <script nomodule src="/build/ui-kit.js"></script>
10  </head>
11  <body>
12    <my-component id="my-comp" label="Label" name="MyComp" disabled="false"></my-component>
13  </body>
14  <script>
15    document.getElementById('my-comp').options = [
16      {
17        value: 'Item 1',
18        displayText: 'Item 1',
19      },
20      {
21        value: 'Item 2',
22        displayText: 'Item 2',
23      },
24      {
25        value: 'Item 3',
26        displayText: 'Item 3',
27      },
28    ]
29  </script>

Now we can locally test our demo component by running npm run start-stencil which is an auto-generated npm script from Stencil. The component should now be visible at http://localhost:3333:

Stencil Local

Build & deploy to npm registry

The next step is to deploy our component to an npm registry. For this demo, I use Verdaccio which is a "lightweight open source private npm proxy registry". First, it needs to be installed globally

npm install -g verdaccio

and then it can be started locally:

1 verdaccio
2 warn --- config file  - /Users/mhoffman/.config/verdaccio/config.yaml
3 warn --- Verdaccio started
4 warn --- Plugin successfully loaded: verdaccio-htpasswd
5 warn --- Plugin successfully loaded: verdaccio-audit
6 warn --- http address - http://localhost:4873/ - verdaccio/4.12.0

Now we have a local npm registry available at http://localhost:4873/ so we need to tell npm to use that registry, for example, by modifying .npmrc:


Additionally, we need to create a user in our registry:

npm adduser --registry http://localhost:4873