Storybook Addon Angular Manifest
Storybook addon to generate a manifest of Angular components
Storybook Addon Angular Manifest
A Storybook addon that builds an Angular component manifest from your stories and Compodoc documentation. It plugs into Storybook's experimental_manifests API so tools like AI assistants and the MCP server can understand your Angular component library: inputs, outputs, selectors, descriptions and ready-to-use template snippets.
Features
- Compodoc integration — reads
documentation.jsonand matches each story's component to its Compodoc entry to extract inputs, outputs, selector, standalone flag and change detection strategy. - Angular template snippets — generates a
<component ...>snippet per story from the component's selector and args. When the selector has multiple comma-separated variants (e.g.button[lib-btn], a[lib-btn]), the first variant is used. - JSDoc tags — parses
@summary,@describe/@descand other JSDoc tags from the story'smetaor the story export itself. @useTemplateopt-in — use the story'sparameters.docs.source.code(the same code Storybook's Docs "Show code" panel displays) as the snippet instead of the generated one, falling back to the story'srendertemplate if nodocs.source.codeis set.- Import statement resolution — infers the import specifier for each component, preferring the nearest
package.jsonname when the component belongs to a published package. - Works with
@storybook/angularand@storybook/angular-vite.
Requirements
-
Storybook
10.5.0or later, withexperimental_manifestssupport. -
Compodoc documentation generated for your Angular project. The easiest way is to enable it in your
angular.jsonbuild target:{ "options": { "compodoc": true } }This produces a
documentation.jsonfile that the addon looks for atdocumentation.jsonor.compodoc/documentation.jsonrelative to your Storybook working directory before Storybook starts.Alternatively, generate it manually:
npx compodoc -p tsconfig.json
Installation
npm install --save-dev @anrouxel/storybook-addon-angular-manifest
# or
pnpm add -D @anrouxel/storybook-addon-angular-manifest
# or
yarn add -D @anrouxel/storybook-addon-angular-manifest
Then register it in .storybook/main.ts, and enable Storybook's componentsManifest feature flag so the manifest is actually written out:
import type { StorybookConfig } from '@storybook/angular-vite';
const config: StorybookConfig = {
framework: '@storybook/angular-vite',
addons: ['@anrouxel/storybook-addon-angular-manifest'],
features: {
componentsManifest: true,
},
// ...
};
export default config;
Running storybook build then writes the manifest to storybook-static/manifests/components.json.
How it works
For every story indexed by Storybook, the addon:
- Resolves the story's
meta.componentand its import declaration to find the Angular component/directive class. - Looks it up in the Compodoc
documentation.json(components, directives, pipes, injectables and classes are all searched). - Builds an Angular template snippet from the component's selector, its
inputsClass/outputsClass, and the story'sargs— statically extracted from the story file's AST. - Extracts JSDoc metadata (
description,summary, custom tags) from the story or component comment. - Assembles everything into a manifest entry served through Storybook's
experimental_manifestsmechanism.
When a component can't be resolved or isn't found in the Compodoc output, the entry still appears in the manifest with an error field explaining why (e.g. missing meta.component, or the class not covered by your tsconfig.json).
Example output
Given this component and story:
@Component({
selector: 'app-button',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<button [disabled]="disabled">{{ label }}</button>',
})
export class ButtonComponent {
/** Text displayed inside the button. */
@Input() label = 'Click me';
/** When true the button is non-interactive. */
@Input() disabled = false;
/** Emitted when the user clicks the button. */
@Output() clicked = new EventEmitter<void>();
}
export const Primary: StoryObj<ButtonComponent> = {
args: { label: 'Click me', disabled: false },
};
the addon generates a manifest entry like this (real output, generated by building this repo's own test Storybook against the snippet above):
{
"id": "components-button",
"name": "ButtonComponent",
"path": "./src/button/button.stories.ts",
"stories": [
{
"id": "components-button--primary",
"name": "Primary",
"snippet": "<app-button label=\"Click me\" [disabled]=\"false\"></app-button>"
}
],
"import": "import { ButtonComponent } from \"@my-org/my-lib\";",
"jsDocTags": {},
"description": "Primary UI component for user interaction.",
"compodoc": {
"name": "ButtonComponent",
"type": "component",
"selector": "app-button",
"standalone": true,
"changeDetection": "ChangeDetectionStrategy.OnPush",
"inputs": [
{ "name": "label", "type": "string", "optional": true, "defaultValue": "'Click me'", "description": "Text displayed inside the button." },
{ "name": "disabled", "type": "boolean", "optional": true, "defaultValue": "false", "description": "When true the button is non-interactive." }
],
"outputs": [
{ "name": "clicked", "type": "EventEmitter<void>", "description": "Emitted when the user clicks the button." }
],
"description": "Primary UI component for user interaction."
},
"standalone": true,
"changeDetection": "ChangeDetectionStrategy.OnPush",
"selector": "app-button"
}
Opting out of generated snippets
If a story already has the exact template you want exposed, tag it with @useTemplate and the addon will use that instead of generating one from the selector. It prefers parameters.docs.source.code — the same code Storybook's Docs "Show code" panel displays — and falls back to the story's render template when docs.source.code isn't set:
/**
* Uses the Docs "Show code" template instead of the Compodoc snippet.
* @useTemplate
*/
export const CustomTemplate: StoryObj<ButtonComponent> = {
render: (_args) => ({
template: `<app-button label="custom template"></app-button>`,
}),
parameters: {
docs: {
source: {
code: `<app-button label="custom template"></app-button>`,
},
},
},
};
API
The package also exposes its manifest type for consumers building on top of it:
import type { AngularComponentManifest } from '@anrouxel/storybook-addon-angular-manifest';
Troubleshooting
"No Compodoc documentation.json found" — enable compodoc: true in your angular.json build options, or run npx compodoc -p tsconfig.json before starting Storybook.
"We could not detect the component from your story file" — make sure your story's default export sets component (e.g. meta.component = ButtonComponent).
"<Component> was not found in the Compodoc documentation" — check that the component's source file is included in the tsconfig.json used to generate Compodoc's documentation.
Contributing
pnpm install
pnpm build # build the addon
pnpm test # run the test suite
pnpm check # lint with Biome