Lineage
A dbt-style lineage graph of your components in Storybook: what each file is built with, where it is used, and what a change affects.
🧬 @rocksoliddigital/storybook-addon-lineage
A dbt-style lineage graph of your components in Storybook. Every file in your source folder is a node and every import an edge, read left to right from what a file is built with to where it is used.
Works with Storybook 10 on Vite (@storybook/react-vite, @storybook/nextjs-vite) and React 18 or 19.
✨ Features
👀 Explore
- A Lineage page in the sidebar, added by the addon, with no story file to write.
- Layers colour the nodes (UI primitives, components, blocks, pages…), set by path in the addon's options. Switch any layer on or off; noisy ones, such as utilities, can start hidden.
- Grouped by layer, as dbt groups sources, staging and marts: each layer is a band of columns
under its name, ordered by the imports between layers (utilities, then primitives, components,
pages), or by each layer's
column. Group by layer switches it off for a free layout. - Hover a node to trace its whole lineage: everything upstream and downstream lights up, the rest dims.
- Search every file, hidden layers included; picking a result switches its layer on.
- Hide unconnected files, counts of files, imports and files without a story, a minimap, and pan and zoom.
- Copy as Mermaid copies the graph as shown, focus and hidden layers included, as a Mermaid flowchart: paste it into a PR, a doc or a ticket, and GitHub, GitLab and most docs tools draw it.
🎯 Focus
- Click a node to see only its lineage, with Upstream and Downstream levels (None, +1,
+2, +3, All), shown as a dbt selector such as
1+Header+2. - Double-click, or Open story, to go to its docs page. Nodes show whether they have a story, borrow one from their folder ("story via View"), or have none.
🔗 Links
- Lineage in the Storybook toolbar, on every story and docs page, opens the graph focused on that component.
- The URL keeps the view: focus and depths are saved however they change (a click, the graph's panel, the Controls panel) and survive a reload, so a view can be shared. Files without a story get a URL-safe key too.
- The Controls panel sets the focus and depths as well.
🔄 Changes
- Changed since main (dbt's
state:modified+): the files changed on your branch, committed or not, and everything that uses them, with the stories to check. Changed files carry a badge in every view. - Live updates: the graph rebuilds when imports change, without reloading the page.
🧩 Setup
- One entry in
.storybook/main.ts, no story file to write. Options for the source folder,tsconfig(so aliases such as@/resolve), excluded paths, the base branch and layers. - Works in
storybook devand in the staticbuild-storybookoutput. - The viewer passes Storybook's accessibility checks. The default layer colour keeps AA contrast with the white node text; the Options table below says how to pick others that do.
📥 Install
pnpm add -D @rocksoliddigital/storybook-addon-lineage
Before moving to npmjs.com, it was published to GitHub Packages as
@rock-solid/storybook-addon-lineage.
If the project sets pnpm's minimumReleaseAge, a new version can't be installed until it's that
old; exempt the scope with minimumReleaseAgeExclude: ['@rocksoliddigital/*'] in pnpm-workspace.yaml.
⚙️ Set up
Add it to .storybook/main.ts. The options are optional; without them every file in src is one
blue layer.
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
addons: [
'@storybook/addon-docs',
{
name: '@rocksoliddigital/storybook-addon-lineage',
options: {
layers: [
{
id: 'primitive',
label: 'UI primitives',
match: /^src\/components\/ui\//,
color: '#7c3aed',
},
{ id: 'component', label: 'Components', match: /^src\/components\//, color: '#2563eb' },
{ id: 'page', label: 'Pages', match: /^src\/(app|pages)\//, color: '#be123c' },
{
id: 'utility',
label: 'Utilities',
match: /^src\/lib\//,
color: '#475569',
hidden: true,
},
],
},
},
],
// ...
};
The addon adds the Lineage page to the sidebar and the toolbar button; there's no story file to write.
🎛️ Options
| Option | Default | |
|---|---|---|
srcDir |
'src' |
Folder to scan, relative to the project root (the folder above .storybook) |
tsConfig |
'tsconfig.json' |
Its paths resolve aliases such as @/ |
exclude |
[] |
Path patterns (regex source) to leave out, e.g. generated files |
baseBranch |
'main' |
Branch that changed files are compared with; origin/<branch> is tried first |
layers |
one layer | { id, label, match, color, hidden?, column? }, matched against paths from the project root, first match wins. hidden layers start switched off. column places a layer's band when grouped, from 0 on the left; layers without one are ordered by their imports around it. Node text is white, so keep each colour at 4.5:1 or better against white. Use hex (#2563eb) or a colour name: Mermaid can't read rgb(), hsl() or var(), so Copy as Mermaid leaves those layers uncoloured. |
🔍 How it works
- The graph is built with dependency-cruiser when Storybook starts (a fraction of a second for a few hundred files), and rebuilt when you save a file whose imports changed. Type-only imports aren't edges: they don't render anything.
- A story links to the file its
componentis imported from, soGrid.stories.tsxwithcomponent: GridBlockfrom./Componentlinks toComponent.tsx. A file without a story of its own borrows one from its folder ("story via View"); the rest show "no story". - A barrel that re-exports a single file, such as
ui/button/index.tswithexport * from './button', isn't a node of its own: what imports it links tobutton.tsx. A barrel that also re-exports anything else, such as another file or a package, stays a node, since which of them an importer uses isn't known. - Changed files are the diff from the merge base with the base branch to the working tree, plus untracked files. Without that branch, as in a shallow CI clone, the toggle is off.
🚧 Limits
- Storybook 10 on Vite with React only.
- One node per file, from static imports, except barrels over a single file. Type-only imports aren't edges.
- Story titles and
componentmust be written as literals for a story to link.
Known issues, the full list of limitations, and what's planned: ROADMAP.md.