Storybook stories without retyping the boilerplate
Storybook documents a component by rendering it in different states, each one a “story”. In the modern Component Story Format (CSF3), a file exports a typed Meta default and then one object per state with args. The shape is mechanical, so this builder generates it from a component name and a list of states.
How it works
The tool writes a .stories.tsx file. It imports Meta and StoryObj types from the framework package (@storybook/react or @storybook/vue3), imports your component, and exports a const meta: Meta<typeof Component> default with the title and component set plus tags: ['autodocs']. It defines type Story = StoryObj<typeof Component> and then emits one export const Name: Story = { args: { ... } } per state you list. Common states (Primary, Secondary, Disabled, Large) get sensible default args; any custom names get an empty args object for you to fill.
Generated file structure
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "./Button";
const meta: Meta<typeof Button> = {
title: "Components/Button",
component: Button,
tags: ["autodocs"],
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = { args: { variant: "primary", label: "Button" } };
export const Disabled: Story = { args: { disabled: true, label: "Button" } };
export const Large: Story = { args: { size: "large", label: "Button" } };
Drop this file next to your component as Button.stories.tsx and it appears in the Storybook sidebar immediately.
CSF3 vs earlier formats
In CSF2 (Storybook 6), stories were functions: export const Primary = (args) => <Button {...args} />. In CSF3, a story is just an object with an args property, and Storybook handles the rendering. This removes the render function boilerplate from every story, keeps files shorter, and enables Storybook’s Controls addon to auto-generate an interactive props panel — since the args are plain data, not embedded in a function.
The Meta default export changed in CSF3 to be typed directly as Meta<typeof Component>. This gives full TypeScript inference on argTypes and the component’s own prop types, catching errors when you reference a prop that does not exist.
Choosing your stories
Each story should represent a distinct, meaningful state of the component — not just every combination of props. A useful starting set for most components:
- Primary / Default: The most common usage, as it appears in the app.
- Disabled: The state when the component is non-interactive.
- Loading / Pending: Relevant for async components.
- Error / Invalid: The error state, for form inputs and similar.
- Empty: For list components, the zero-items case.
Avoid stories that only change a colour or a label when the variant prop does not change behaviour — those are better covered by Controls, which lets you explore prop combinations interactively without separate named exports.
Organising the sidebar
The title string uses slashes to create a folder hierarchy. A consistent convention keeps a large component library navigable:
Components/Button— general UI primitivesForms/TextField— form-specific componentsLayouts/PageShell— layout wrappersFeatures/PaymentCard— product-specific components
Put only the props that change per story in each story’s args; shared defaults belong in meta.args so they apply across every story without repetition.