Component Story Format
Purpose and Scope
Component Story Format is the authoring shape Storybook uses to turn ordinary module exports into browseable, testable, and documentable component examples. A story file describes one component family with a default export, then exposes individual component states as named exports. The supplied CSF 3 examples show that the modern form is intentionally data-oriented: a story can be an object containing args rather than a render function. That makes stories easier for Storybook, addons, documentation pages, and portable story tooling to inspect consistently across React, Angular, Vue, Svelte, Solid, and Web Components.
Sources: docs/_snippets/csf-3-example-starter.md, docs/_snippets/csf-2-example-starter.md
Use this page when you already know what a story is and need the concrete file structure for writing or migrating story files. The examples focus on a Button component, but the same contract applies to larger components, page-level compositions, and design-system primitives. CSF does not require every renderer to render in the same language syntax, but it does give every renderer the same conceptual vocabulary: metadata for the story collection, named story exports for individual states, and args for inputs. That shared shape is why stories can feed Canvas, Docs, Controls, interaction tests, and composition workflows.
Relevant Source Files
- docs/_snippets/csf-3-example-starter.md — Shows the primary CSF 3 starter pattern for Angular, React, Solid, Svelte, Vue, and Web Components, including default metadata exports and object stories with args.
- docs/_snippets/csf-3-example-title.md — Shows how titles and story names are chosen, and how MDX documentation pages can reference CSF story modules and individual story exports.
- docs/_snippets/csf-2-example-starter.md — Shows the older CSF 2 function-story pattern used for comparison and migration, including assigned args after each exported function.
Core Primitives
A CSF file starts with component metadata. In the CSF 3 starter, React, Vue, Svelte, Solid, and Angular examples import the component and export a default metadata object that identifies it. Most framework examples can rely on the component field, while Web Components examples also set an explicit story container title and use a tag name as the component value. This metadata is the anchor for sidebar grouping, docs generation, type inference, and addon behavior. Without it, Storybook would not have the stable component-level context needed to organize the named stories that follow.
Sources: docs/_snippets/csf-3-example-starter.md
Named exports define the actual stories. In CSF 3, the supplied examples export a Primary story as an object whose args set primary to true. That small object is important: it describes a state rather than embedding all rendering logic inline. Storybook can then pass args through the renderer, expose them in controls, display them in docs tables, and reuse the same state in testing flows. When a story export does not provide an explicit display name, the variable name becomes the story name, which keeps the common path concise while still allowing customization when needed.
TypeScript users should prefer the typed CSF 3 shape shown in the snippets. The React, Vue, Svelte, and Solid examples use framework-specific Meta and StoryObj types, often with the TypeScript satisfies operator, so the metadata remains a normal object while still being checked against the component type. After exporting the metadata, the examples define a Story alias from StoryObj and annotate Primary with that alias. This pattern lets args be checked against the component contract and reduces the chance that a story silently documents an impossible component state.
Sources: docs/_snippets/csf-3-example-starter.md
CSF 3 Authoring Flow
Start by choosing the story file next to the component it documents, commonly with a Button.stories extension matching the project language. Import the component, create the metadata object, export that metadata as the default export, define a story type alias if using TypeScript, and then export each state as a named story object. The minimal CSF 3 version is deliberately compact: most examples need only the component reference and an args object. Add more fields only when the reader or addon needs them, such as a title override, a story name, parameters, decorators, or custom rendering.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary: Story = { args: { primary: true } };The title and name snippet clarifies how Storybook chooses labels. At the component level, a title field sets the story container name, such as components/Button. If the title is not set, the component name can be used instead. At the story level, the export variable name is used if no explicit name is provided. When a story object includes a name field, that value becomes the visible story name. This means teams can keep ordinary code identifiers stable while still presenting clearer labels to designers, product managers, and documentation readers.
Sources: docs/_snippets/csf-3-example-title.md
CSF also connects to MDX documentation. The title snippet shows a documentation-only MDX page using Meta with a title, and a component documentation page importing all Button stories, passing them to Meta, and embedding the Primary story with Story. That pattern is useful when generated docs need additional explanation, design rationale, usage guidance, or cross-links. The key idea is that MDX consumes the same CSF module that the Canvas uses, so authored documentation and runnable examples stay aligned instead of drifting into separate examples maintained by hand.
Sources: docs/_snippets/csf-3-example-title.md
CSF 2 to CSF 3 Migration
CSF 2 examples model stories primarily as exported functions, then attach args after the function declaration. React and Solid examples render the Button with spread args, Angular maps args into props, Svelte returns a component and props object, Vue returns a component setup object and template, and Web Components returns a lit template. CSF 3 moves the common case from a renderer-specific function into a renderer-neutral story object. That does not mean custom rendering disappears, but it makes the simple state-description path consistent and easier for tooling to analyze.
Sources: docs/_snippets/csf-2-example-starter.md, docs/_snippets/csf-3-example-starter.md
| Concern | CSF 2 pattern | CSF 3 pattern |
|---|---|---|
| Component metadata | Default export with title and component | Default export with component, optionally title |
| Story shape | Exported function | Exported object |
| Args placement | Assigned after export | Declared inside the story object |
| Renderer logic | Often embedded in each story | Usually inferred from metadata and args |
| TypeScript helpers | ComponentStory, ComponentMeta, StoryFn, or Story | Meta and StoryObj in the starter examples |
For migration, convert the easiest stories first: those that only pass args to the component. Move the assigned args into the exported object and remove the function body. Preserve the default export metadata, but consider adopting the CSF 3 TypeScript pattern with Meta, StoryObj, and satisfies where the framework package supports it. Stories with custom renderer-specific templates can remain function based until they are simplified or given an explicit render field. This incremental approach keeps behavior stable while gradually increasing portability and consistency across story files.
Portable Story Structure and Rendering Context
The official Storybook docs describe stories as rendering inside the preview iframe, also called the Canvas, while configuration such as preview setup and layout can apply to every story. CSF fits into that rendering model by providing a serializable description of each component state before renderer-specific code turns it into UI. A story object with args can be reused by Docs, Controls, tests, and composition because the important state is not hidden inside an opaque function. The more a story is expressed as metadata and args, the more useful it becomes outside the immediate Canvas view.
Portable structure does not mean every framework snippet is identical. Angular uses Meta with the component class type, React and Vue infer from the imported component, Svelte imports a .svelte component, Solid uses its framework package types, and Web Components uses a custom element tag name. The shared contract is the same even when imports and component references differ. That distinction helps teams with mixed stacks standardize their story review practices without forcing every renderer to abandon its own idioms or rendering requirements.
Sources: docs/_snippets/csf-3-example-starter.md
Implementation Details and Edge Cases
Be intentional about title and name overrides. A title such as components/Button controls the sidebar grouping for the story container, while a story-level name controls the display label for one export. If these values are absent, Storybook can derive labels from the component and variable names, which is often enough for small projects. Explicit titles become more valuable in large repositories where folder layout, package boundaries, or design-system taxonomy should not be inferred from filenames alone. Explicit story names are helpful when code-friendly identifiers do not match reader-friendly labels.
Sources: docs/_snippets/csf-3-example-title.md
When writing Web Components stories, notice that the CSF 3 examples identify the component as demo-button rather than importing a JavaScript component value. The CSF 2 Web Components example instead imports html from lit and returns a template for Primary. That contrast highlights a practical edge case: some renderers can infer a useful render path from metadata and args, while others may need explicit rendering in more complex cases. Prefer the simplest object story that works, but keep renderer-specific rendering available when the component model requires it.
Sources: docs/_snippets/csf-3-example-starter.md, docs/_snippets/csf-2-example-starter.md
Next Steps
After creating a CSF 3 story file, open the story in Canvas, verify that args produce the intended state, and then enrich the story only where the project needs more context. Add parameters for layout or addon behavior, decorators for providers and wrappers, loaders for asynchronous setup, and play functions for interaction checks. If you are migrating a repository, use the CSF 2 examples as a checklist for old patterns and convert simple function stories to object stories first. Then read the related pages on args, parameters, decorators, play functions, and MDX documentation.