Create Storybook story files (.stories.tsx) for F0 React components. Use when adding or updating stories, snapshot stories, argTypes, decorators, play functions, or internal stories. For MDX documentation files, use the global factorial-f0-component-documentation skill instead.
Scanned 6/5/2026
Install via CLI
openskills install factorialco/f0---
name: f0-storybook-stories
description: Create Storybook story files (.stories.tsx) for F0 React components. Use when adding or updating stories, snapshot stories, argTypes, decorators, play functions, or internal stories. For MDX documentation files, use the global factorial-f0-component-documentation skill instead.
---
# F0 Storybook Stories
Creates `.stories.tsx` files for F0 components. For MDX docs (the Docs tab), use the global `factorial-f0-component-documentation` skill instead — that's a separate file.
## Quick Reference
| Need | See |
| --------------------------------------------------------------------- | ------------------------------------- |
| Meta definition, tags, argTypes, Snapshot, decorators, play functions | `references/story-patterns.md` |
| Play functions, portal queries, step(), a11y configuration | Load the `f0-storybook-testing` skill |
## Key Rules
### Imports
```tsx
import type { Meta, StoryObj } from "@storybook/react-vite"
import { fn, expect, within } from "storybook/test"
import { withSnapshot, withSkipA11y } from "@/lib/storybook-utils/parameters"
// Icons: "@/icons/app" | "@/icons/modules" | "@/icons/ai"
```
### Meta
Always `satisfies Meta<typeof Component>` (not `as Meta`). Exception: components with complex generics use plain `Meta`.
```tsx
const meta = {
title: "ComponentName",
component: F0Example,
tags: ["stable", "!autodocs"], // or "experimental" | "internal" | "deprecated". Always include "!autodocs" when a manual MDX page exists.
// ...
} satisfies Meta<typeof F0Example>
export default meta
type Story = StoryObj<typeof meta>
```
### Snapshots are OFF by default
**Why**: Chromatic charges per snapshot and visual-diff reviews get noisy when
the same component produces many captures. The policy is **one consolidated
snapshot per component** — a single image that shows every meaningful variant
in a flex layout — so a visual change produces exactly one diff to review.
**Rule**: every component has exactly **one** story exported as `Snapshot`
that opts in with `withSnapshot({})`. Do **not** scatter `withSnapshot()`
across multiple stories; consolidate all variants into the `Snapshot` story.
```tsx
export const Snapshot: Story = {
parameters: withSnapshot({}), // opt in
}
// With a11y skip: withSkipA11y(withSnapshot({}))
```
### Tags
| Tag | When | Sidebar |
| ---------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"stable"` | Production-ready | ✅ toolbar only |
| `"experimental"` | Still stabilizing | 🚧 sidebar + toolbar |
| `"internal"` | Not public | 🔒 sidebar + toolbar |
| `"deprecated"` | Avoid using | ⛔ sidebar + toolbar |
| `"!autodocs"` | Manual MDX page exists | Prevents duplicate autogenerated Docs page |
| `"no-sidebar"` | Hide story from sidebar | Hidden from Storybook navigation. **Never apply to the first exported (primary) story when an attached MDX exists** — it propagates to the `--documentation` entry and hides the whole Documentation page. |
> **`internal` is not a maturity level.** Use it for components in `src/ui/` (Radix wrappers) and for stories you want hidden from the public Storybook sidebar but visible locally. The three F0 maturity levels are `stable`, `experimental`, and `deprecated` — see [Component Maturity](/?path=/docs/docs-components-maturity--docs).
> **Promotion and deprecation:** when a component is promoted from `experimental/` to stable, change the tag from `"experimental"` to `"stable"`. When deprecating, change to `"deprecated"` and add `@deprecated` + `@removeIn` + `@migration` JSDoc tags on the export. See the `f0-component-promotion` skill and the [Deprecation & Removal policy](/?path=/docs/docs-development-release-and-versioning--docs).
> **Note:** Storybook applies `tags: ["autodocs"]` globally via `.storybook/preview.tsx`. To opt out of autodocs when a manual MDX page exists, add `"!autodocs"` to the meta tags. Removing `"autodocs"` alone is not enough.
### Manual MDX requires a visible first story
When a component has a manual MDX file (`F0Example.mdx` with `<Meta of={Stories} />`), Storybook attaches the MDX docs entry to the **first exported story** and inherits its tags onto the docs entry.
**Consequence**: if the first exported story has `tags: ["no-sidebar"]`, the Documentation page also gets `no-sidebar` and disappears from the sidebar — the MDX is still served but unreachable through navigation.
**Rule**: with manual MDX, the **first exported story must NOT include `no-sidebar`**. Conventionally that first story is `Default`, and it must remain visible in the sidebar. Hidden helper stories (`no-sidebar`) referenced by `<Canvas of={...} />` come after.
### ArgTypes for union props
Reference the component's exported const array — don't hardcode the options:
```tsx
import { buttonSizes, buttonVariants } from "../F0Example"
argTypes: {
size: {
control: "select",
options: buttonSizes,
table: { type: { summary: buttonSizes.join(" | ") } },
},
}
```
### Render-only stories must include `args`
Stories that use a custom `render` function without inheriting args from the meta
**must** include an explicit `args` property to satisfy the `Story` type constraint.
Omitting `args` causes a TypeScript error when the meta has required props.
```tsx
// WRONG — TS error: Property 'args' is missing
export const Variants: Story = {
tags: ["!dev"],
render: () => <div>...</div>,
}
// CORRECT — provide required args even if render ignores them
export const Variants: Story = {
tags: ["!dev"],
args: { items: [], onClick: () => {} }, // satisfy required props from Meta
render: () => <div>...</div>,
}
```
No comments yet. Be the first to comment!