Angular Storybook (2026): Setup, Docs & Design System Guide
Your Angular Components Already Have Documentation. It Is Just Out of Date.
The short answer: to set up Angular Storybook in 2026, run npm create storybook@latest. The current stable line is Storybook 10 (10.6.3 on npm as of 25 October 2026). Use @storybook/angular-vite on Angular 21 or newer, and @storybook/angular on Angular 18 to 20. Then write one story file per component, switch on autodocs, and add accessibility and visual checks in CI.
learn about Angular Development
Angular Design System: Tokens, Components & Theming (2026)
Most Angular design systems die the same way. A button ships with three variants. Six months later it has seven, two of them undocumented, and the Figma file disagrees with the code. Developers fork components rather than ask which one to use.
Storybook fixes this by making the component itself the documentation. Every state is a story, every input is a control, and every change is checked before it merges. That is also why Storybook belongs in the build described in our step-by-step guide to building a design system in Angular.
This guide covers the 2026 setup end to end: which package to install, how to write stories for signal inputs, and how to turn the result into tested, deployable documentation.
Section 1: Pick the Right Storybook Package for Your Angular Version
Storybook ships two Angular frameworks, and choosing wrongly is the most common setup mistake.
@storybook/angular |
@storybook/angular-vite |
|
|---|---|---|
| Bundler | Webpack 5 | Vite 8 or newer |
| Angular versions | 18 to 22 | 21 to 22 |
| Status | Stable | Preview, stable planned for Storybook 11 |
| Vitest addon | Not supported | Supported |
| Component docs | Compodoc | Read from TypeScript source |
| Zoneless | Optional builder flag | Default |
The peer dependency ranges come from the published 10.6.3 packages on npm. The Storybook docs recommend the Vite framework for Angular 21 and newer when you want faster builds and in-browser component tests, and the Webpack framework when you need Angular 18 to 20 or custom Webpack tooling.
Which one should you choose?
- Angular 21 or newer, greenfield design system:
@storybook/angular-vite. You get the Vitest addon, and you avoid maintaining Compodoc output. - Angular 18 to 20, or heavy
webpackFinalcustomisation:@storybook/angular. It is the safer route until you upgrade Angular. - Unsure: run the installer. It inspects your dependencies and picks a configuration for you.
Two requirements apply to both. Storybook 10 is ESM-only, so your .storybook/main.ts must be valid ESM, and Node must be 20.19 or newer, or 22.12 or newer.
Security note: in October 2026 Storybook published a critical advisory (GHSA-f2ph-cmg5-wxc8) about missing origin validation in the preview of the dev server. The fix landed in 10.6.2. If you installed Storybook earlier in the year, upgrade before you run storybook dev again.
Section 2: Install and Configure Angular Storybook
From the root of your Angular workspace:
npm create storybook@latest
The CLI asks whether you are new to Storybook and which configuration you want. Choose Recommended to include docs, testing and accessibility features. To upgrade an existing project instead, run npx storybook@latest upgrade, which applies the automigrations for you.
What changed since Storybook 8
If you are following an older tutorial, these changes will bite you:
@storybook/addon-essentialsis gone. Controls, actions, viewport and backgrounds moved into core in Storybook 9. Docs live in@storybook/addon-docs.@storybook/addon-interactions,@storybook/addon-linksand@storybook/blocksare empty. Remove them frompackage.json.- Imports moved.
@storybook/testbecamestorybook/test,@storybook/themingbecamestorybook/theming, and@storybook/manager-apibecamestorybook/manager-api. - Doc blocks now come from
@storybook/addon-docs/blocks.
A minimal .storybook/main.ts for Angular 21 and newer looks like this:
import type { StorybookConfig } from '@storybook/angular-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.ts'],
addons: ['@storybook/addon-docs', '@storybook/addon-a11y', '@chromatic-com/storybook'],
framework: '@storybook/angular-vite',
};
export default config;
On Angular 18 to 20, swap both angular-vite strings for angular. Your installer may also add @storybook/addon-vitest and @storybook/addon-onboarding, which is fine.
Global providers in preview.ts
Components that need the router or animations get them from the applicationConfig decorator:
import { applicationConfig, type Preview } from '@storybook/angular-vite';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideRouter } from '@angular/router';
const preview: Preview = {
decorators: [
applicationConfig({
providers: [provideAnimationsAsync(), provideRouter([])],
}),
],
parameters: {
controls: {
matchers: { color: /(background|color)$/i, date: /Date$/i },
},
},
};
export default preview;
Zoneless change detection deserves a note. The Vite framework runs zoneless by default, and you can opt out with "zoneless": false on the builder target in angular.json. On the Webpack framework, the equivalent builder option is experimentalZoneless. If your app is already zoneless, as covered in our Angular zoneless and defer blocks guide, match that setting so stories behave like production.
Section 3: Write Stories for Standalone, Signal-Based Components
Start with a modern component. Standalone is the default, inputs are signals, and the output is a function:
// src/lib/button/button.component.ts
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
@Component({
selector: 'ds-button',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<button
type="button"
[class]="'ds-button ds-button--' + variant()"
[disabled]="disabled()"
(click)="pressed.emit()"
>
{{ label() }}
</button>
`,
})
export class ButtonComponent {
/** Visual style of the button. */
readonly variant = input<'primary' | 'secondary' | 'ghost'>('primary');
/** Text shown inside the button. */
readonly label = input('Button');
/** Disables the button and blocks interaction. */
readonly disabled = input(false);
/** Emits when the user activates the button. */
readonly pressed = output<void>();
}
Storybook has supported signal-based input() and output() since version 8.1, so the story needs no special handling. This pairs well with the patterns in our Angular Signals guide for design system components.
// src/lib/button/button.stories.ts
import type { Meta, StoryObj } from '@storybook/angular-vite';
import { ButtonComponent } from './button.component';
const meta: Meta<ButtonComponent> = {
title: 'Components/Button',
component: ButtonComponent,
tags: ['autodocs'],
argTypes: {
variant: { control: 'inline-radio', options: ['primary', 'secondary', 'ghost'] },
pressed: { action: 'pressed' },
},
args: { label: 'Save changes', variant: 'primary', disabled: false },
};
export default meta;
type Story = StoryObj<ButtonComponent>;
export const Primary: Story = {};
export const Secondary: Story = { args: { variant: 'secondary' } };
export const Disabled: Story = { args: { disabled: true } };
export const AllVariants: Story = {
render: () => ({
template: `
<div style="display: flex; gap: 12px">
<ds-button variant="primary" label="Primary" />
<ds-button variant="secondary" label="Secondary" />
<ds-button variant="ghost" label="Ghost" />
</div>
`,
moduleMetadata: { imports: [ButtonComponent] },
}),
};
Three habits that keep stories useful:
- One story per meaningful state. Default, disabled, loading, error, long text, and right-to-left if you support it. Stories are your visual test cases.
- Set shared
argson the meta, and override only what differs. It keeps stories short and diffs small. - Use a template story only for composition. For a single component, args and controls are enough.
CSF Next: the typed alternative
Since Storybook 10.3 the type-safe CSF Next format, also called CSF factories, is available for Angular. Instead of Meta and StoryObj, you call preview.meta() and meta.story(). The chain from definePreview to meta to story gives you typed args and typed addon parameters.
Treat it as an option, not a requirement. Storybook lists CSF Next as preview, with a stable release planned for Storybook 11, and the older format keeps working. If you start a new system today, write CSF 3 and migrate with the provided codemods when it stabilises.
Section 4: Turn Stories into Living Documentation
Stories show components. Docs explain them. Storybook generates most of the second from the first.
Autodocs and where the prop descriptions come from
The tags: ['autodocs'] line in the meta creates a Docs page per component, with a canvas, a controls table and every story. The descriptions in that table come from JSDoc comments above your inputs and outputs, which is why the ButtonComponent above carries them.
How the metadata is extracted depends on your framework:
@storybook/angular-vitereads your TypeScript sources on the Storybook server. There is nothing to install, and editing a component updates its controls without a restart.@storybook/angularuses Compodoc. Add@compodoc/compodoc, setcompodoc: trueandcompodocArgson both builder targets inangular.json, and callsetCompodocJsoninpreview.ts. The installer can do this for you.
If you migrate from Webpack to Vite, run npx storybook automigrate. It removes the Compodoc wiring. Storybook documents the Compodoc path on the Vite framework as a temporary migration aid, so plan around source extraction.
MDX for guidance that code cannot express
Usage rules, do and do not examples, and content guidelines belong in MDX:
{/* src/lib/button/button.mdx */}
import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';
import * as ButtonStories from './button.stories';
<Meta of={ButtonStories} />
## When to use
Use **primary** for the single most important action on a screen.
Use **ghost** for tertiary actions inside toolbars.
<Canvas of={ButtonStories.Primary} />
<Controls of={ButtonStories.Primary} />
Document design tokens next to components
The same doc blocks package includes ColorPalette and ColorItem, which render swatches from CSS custom properties. Point them at the variables your pipeline generates, so the swatch always matches production. Our design tokens guide for Angular, Figma and Style Dictionary covers how to generate those variables from a single source.
Load the generated token stylesheet through your Storybook builder's styles setting, or import it in the preview file, so components render with real tokens rather than browser defaults.
Brand the Storybook shell
Storybook documents the manager theme through addons.setConfig from storybook/manager-api, and the docs pages are themed separately through parameters.docs.theme in the preview file. A small branded theme makes the documentation feel like your product rather than a developer tool.
Section 5: Test Components with the Vitest Addon, Accessibility Checks and Chromatic
Documentation that nobody trusts is worse than none. Testing is what makes stories trustworthy.
Component tests with the Vitest addon
The Vitest addon turns every story into a component test that runs in a real browser. It needs a Vite-based Storybook framework and Vitest 3 or newer, so for Angular it means @storybook/angular-vite.
npx storybook add @storybook/addon-vitest
The command installs and registers the addon, configures Vitest in browser mode with Playwright's Chromium, and prompts you to install the browser binaries. On Angular it also scaffolds storybookAngularVitest() in vitest.config.ts, so a standalone vitest run picks up your Angular build options. Add play functions to stories for interaction tests, using helpers from storybook/test.
If you are on @storybook/angular, the Vitest addon is not available. The docs point to the Vite framework as the route to Vitest integration, so plan an Angular and framework upgrade together.
Accessibility checks that can fail a build
Register @storybook/addon-a11y, and every story gets axe checks plus an Accessibility panel. The important part is the a11y.test parameter:
| Value | Behaviour |
|---|---|
'off' |
No automated check, panel still available |
'todo' |
Violations appear as warnings in the Storybook UI |
'error' |
Violations fail the test, including in CI |
Storybook warns that only 'error' produces failures in CI, so a project set to 'todo' will look green in the pipeline while the UI shows warnings locally. A pragmatic rollout:
// .storybook/preview.ts (excerpt)
parameters: {
a11y: { test: 'todo' },
},
Mark legacy components 'todo', fix them one at a time, and switch the global default to 'error' once the backlog is clear. Automated checks do not replace manual testing with a keyboard and screen reader, but they catch the repeatable failures. For the wider picture, see our web accessibility guide.
Visual regression with Chromatic
Chromatic is a separate service from the Storybook maintainers. It snapshots every story, compares it with the accepted baseline, and gives designers an approval step. The GitHub workflow from the Chromatic docs, trimmed:
name: Chromatic
on: push
jobs:
chromatic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-node@v7
with:
node-version: 24.20.0
- run: npm ci
- uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
autoAcceptChanges: main
Three details from the docs matter. Run it on push, because the pull_request event can lose baselines. Keep fetch-depth: 0 so Chromatic can read history. By default the action exits with code 0 when it finds changes, so set exitZeroOnChanges: false if you want the check to block merges until someone accepts them.
Section 6: Publish Storybook for Designers and Consumers
npm run build-storybook produces a static site. With the Storybook CLI the default output folder is storybook-static, and with the Angular builders you configure outputDir, for example dist/storybook/your-project. Any static host can serve it.
Practical publishing rules:
- Publish on every merge to main, and keep the URL stable. A link that changes breaks bookmarks in Figma and tickets.
- Publish a preview per pull request if reviewers are designers who will not run the code. Chromatic does this out of the box, and most static hosts can do it with a branch deploy.
- Version the docs with the library. If you ship the components to npm, as in our guide to publishing an Angular component library, tag the Storybook build with the same version.
- Put Storybook in the monorepo next to the library. An Nx workspace makes the build target and caching straightforward, which we cover in the Nx monorepo design system guide.
The loop that results is simple. A designer specifies a component in Figma. A developer builds it and writes its stories. The designer reviews the stories instead of the code. CI blocks regressions. Consumers read the docs page, not the source.
Section 7: When Storybook Is the Wrong Tool
Storybook is not free. It adds a second build, a second dependency tree, and a maintenance habit.
Skip or delay it when:
- You have a handful of components and one app. A route in the app that lists the components may be enough.
- Your components cannot render in isolation because they depend on global state or a backend. Fix that coupling first, or you will spend your time writing mocks.
- Nobody owns it. An unmaintained Storybook is the same stale documentation you were trying to escape. Name an owner.
- You are two Angular versions behind. Upgrading Angular first often costs less than fighting peer dependency ranges.
- You need to compare UI libraries rather than build one. Start with our comparison of Angular Material and PrimeNG, because both ship their own docs.
Storybook 11 is currently in alpha, with a beta targeted for early November 2026 according to the maintainers' release plan. Do not move a production design system onto an alpha. Stay on 10.x, keep up with patch releases, and read the migration guide when 11 reaches release candidate.
Conclusion: Make the Component the Documentation
The 2026 Angular Storybook workflow is shorter than it used to be. One command installs it. Standalone, signal-based components need no special treatment. Docs come from your own JSDoc, and tests run against the same stories your designers review.
Choose the framework package that matches your Angular version, write one story per real state, make accessibility checks fail the build, and publish a stable URL. After that, the hard part is not tooling. It is keeping the habit that no component merges without its story.
If a component has no story, it is not part of the design system yet.
