Skip to content

Commit 1d1f8e0

Browse files
authored
Merge pull request storybookjs#31713 from storybookjs/docs-csf-factories-ref-rfc
Docs: Point CSF Factories docs at RFC
2 parents 99cb74a + 8242723 commit 1d1f8e0

3 files changed

Lines changed: 20 additions & 332 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
```shell renderer="common" language="js" packageManager="npm"
2+
npx storybook migrate csf-2-to-3 --glob="**/*.stories.tsx" --parser=tsx
3+
```
4+
5+
```shell renderer="common" language="js" packageManager="pnpm"
6+
pnpm exec storybook migrate csf-2-to-3 --glob="**/*.stories.tsx" --parser=tsx
7+
```
8+
9+
```shell renderer="common" language="js" packageManager="yarn"
10+
yarn exec storybook migrate csf-2-to-3 --glob="**/*.stories.tsx" --parser=tsx
11+
```

docs/api/csf/csf-factories.mdx

Lines changed: 1 addition & 332 deletions
Original file line numberDiff line numberDiff line change
@@ -6,337 +6,6 @@ tab:
66
title: CSF Factories (Experimental)
77
---
88

9-
<If notRenderer="react">
10-
11-
<Callout variant="info">
12-
CSF Factories are currently only supported in [React](?renderer=react) projects.
13-
</Callout>
14-
15-
</If>
16-
17-
<If renderer="react">
18-
19-
<Callout variant="warning" icon="🧪">
20-
This is an experimental feature and (though unlikely) the API may change in future releases. We [welcome feedback](https://github.com/storybookjs/storybook/discussions/30112) and contributions to help improve this feature.
21-
</Callout>
22-
239
CSF Factories are the next evolution of Storybook's Component Story Format (CSF). This new API uses a pattern called factory functions to provide full type safety to your Storybook stories, making it easier to configure addons correctly and unlocking the full potential of Storybook's features.
2410

25-
This reference will provide an overview of the API and a migration guide to upgrade from CSF 3.
26-
27-
## Overview
28-
29-
The CSF Factories API is composed of four main functions to help you write stories. Note how three of the functions operate as factories, each producing the next function in the chain (`definePreview``preview.meta``meta.story`), providing full type safety at each step.
30-
31-
### `defineMain`
32-
33-
With CSF Factories, your [main Storybook config](../main-config/main-config.mdx) is specified by the `defineMain` function. This function is type-safe and will automatically infer types for your project.
34-
35-
```ts title=".storybook/main.js|ts"
36-
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
37-
import { defineMain } from '@storybook/your-framework/node';
38-
39-
export default defineMain({
40-
framework: '@storybook/your-framework',
41-
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
42-
addons: ['@storybook/addon-a11y'],
43-
});
44-
```
45-
46-
### `definePreview`
47-
48-
Similarly, the `definePreview` function specifies your project's story configuration. This function is also type-safe and will infer types throughout your project.
49-
50-
Importantly, by specifying addons here, their types will be available throughout your project, enabling autocompletion and type checking.
51-
52-
You will import the result of this function, `preview`, in your story files to define the component meta.
53-
54-
```ts title=".storybook/preview.js|ts"
55-
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
56-
import { definePreview } from '@storybook/your-framework';
57-
import addonA11y from '@storybook/addon-a11y';
58-
59-
export default definePreview({
60-
// 👇 Add your addons here
61-
addons: [addonA11y()],
62-
parameters: {
63-
// type-safe!
64-
a11y: {
65-
options: { xpath: true },
66-
},
67-
},
68-
});
69-
```
70-
71-
<Callout variant="info">
72-
The preview configuration will be automatically updated to reference the necessary addons when installing an addon via `npx storybook add <addon-name>` or running `storybook dev`.
73-
</Callout>
74-
75-
### `preview.meta`
76-
77-
The `meta` function on the `preview` object is used to define the [metadata for your stories](./index.mdx#default-export). It accepts an object containing the `component`, `title`, `parameters`, and other story properties.
78-
79-
```ts title="Button.stories.js|ts"
80-
// Learn about the # subpath import: https://storybook.js.org/docs/api/csf/csf-factories#subpath-imports
81-
import preview from '#.storybook/preview';
82-
83-
import { Button } from './Button';
84-
85-
const meta = preview.meta({
86-
component: Button,
87-
parameters: {
88-
// type-safe!
89-
layout: 'centered',
90-
}
91-
});
92-
export default meta;
93-
```
94-
95-
### `meta.story`
96-
97-
Finally, the `story` function on the `meta` object defines the stories. This function accepts an object containing the [`name`](../../writing-stories/index.mdx#rename-stories), [`args`](../../writing-stories/args.mdx), [`parameters`](../../writing-stories/parameters.mdx), and other story properties.
98-
99-
```ts title="Button.stories.js|ts"
100-
// ...from above
101-
const meta = preview.meta({ /* ... */ });
102-
103-
export const Primary = meta.story({
104-
args: {
105-
// type-safe!
106-
primary: true,
107-
},
108-
});
109-
```
110-
111-
### Subpath imports
112-
113-
CSF Factories leverages subpath imports to simplify importing constructs from the preview file. While you can still use relative path imports, subpath imports offer a more convenient and maintainable approach:
114-
115-
```ts
116-
// ✅ Subpath imports won't break if you move story files around
117-
import preview from '#.storybook/preview';
118-
119-
// ❌ Relative imports will break if you move story files around
120-
import preview from '../../../.storybook/preview';
121-
```
122-
123-
See the manual migration steps for details about [configuring the necessary subpath imports](#1-add-subpath-import-in-packagejson).
124-
125-
For more details, refer to the [subpath imports documentation](../../writing-stories/mocking-data-and-modules/mocking-modules.mdx#subpath-imports).
126-
127-
## Upgrading from CSF 1, 2, or 3
128-
129-
You can upgrade your project's story files to CSF Factories incrementally or all at once. However, before using CSF Factories in a story file, you must upgrade your `.storybook/main.js|ts` and `.storybook/preview.js|ts` files.
130-
131-
### 1. Add subpath import in `package.json`
132-
133-
To be able to consistently import the preview file from any location in your project, you need to add a subpath import in your `package.json`. For more information, refer to the [subpath imports documentation](../../writing-stories/mocking-data-and-modules/mocking-modules.mdx#subpath-imports).
134-
135-
```json title="package.json"
136-
{
137-
"imports": {
138-
"#*": ["./*", "./*.ts", "./*.tsx"],
139-
},
140-
}
141-
```
142-
143-
### 2. Update your main Storybook config file
144-
145-
Update your `.storybook/main.js|ts` file to use the new [`defineMain`](#definemain) function.
146-
147-
```diff title=".storybook/main.js|ts"
148-
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
149-
+ import { defineMain } from '@storybook/your-framework/node';
150-
- import { StorybookConfig } from '@storybook/your-framework';
151-
152-
+ export default defineMain({
153-
- export const config: StorybookConfig = {
154-
// ...current config
155-
+ });
156-
- };
157-
- export default config;
158-
```
159-
160-
### 3. Update your preview config file
161-
162-
Update your `.storybook/preview.js|ts` file to use the new [`definePreview`](#definepreview) function.
163-
164-
<details>
165-
<summary>Which addons should be specified in `preview`?</summary>
166-
167-
The ability for an addon to provide annotation types (`parameters`, `globals`, etc.) is new and not all addons support it yet.
168-
169-
If an addon provides annotations (i.e. it distributes a `./preview` export), it can be imported in two ways:
170-
171-
1. For official Storybook addons, you import the default export:
172-
`import addonName from '@storybook/addon-name'`
173-
174-
2. For community addons, you should import the entire module and access the addon from there:
175-
`import * as addonName from 'community-addon-name'`
176-
177-
</details>
178-
179-
```diff title=".storybook/preview.js|ts"
180-
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
181-
+ import { definePreview } from '@storybook/your-framework';
182-
- import type { Preview } from '@storybook/your-framework';
183-
// 👇 Import the addons you are using
184-
+ import addonA11y from '@storybook/addon-a11y';
185-
186-
+ export default definePreview({
187-
- export const preview: Preview = {
188-
// ...current config
189-
// 👇 Add your addons here
190-
+ addons: [addonA11y()],
191-
+ });
192-
- };
193-
- export default preview;
194-
```
195-
196-
### 4. Update your story files
197-
198-
Story files have been updated for improved usability. With the new format:
199-
200-
- Import the preview construct from the Storybook preview file
201-
- The meta object is now created via the [`preview.meta`](#previewmeta) function and does not have to be exported as a default export
202-
- Stories are now created from the meta object, via the [`meta.story`](#metastory) function
203-
204-
<Callout variant="info">
205-
The examples below show the changes needed to upgrade a story file from CSF 3 to CSF Factories. You can also upgrade from CSF 1 or 2 using similar steps.
206-
</Callout>
207-
208-
```diff title="Button.stories.js|ts"
209-
// Learn about the # subpath import: https://storybook.js.org/docs/api/csf/csf-factories#subpath-imports
210-
+ import preview from '#.storybook/preview';
211-
- import type { Meta, StoryObj } from '@storybook/your-framework';
212-
213-
import { Button } from './Button';
214-
215-
+ const meta = preview.meta({
216-
- const meta = {
217-
// ...current meta
218-
+ });
219-
- } satisfies Meta<typeof Button>;
220-
- export default meta;
221-
222-
- type Story = StoryObj<typeof meta>;
223-
224-
+ export const Primary = meta.story({
225-
- export const Primary: Story = {
226-
// ...current story
227-
+ });
228-
- };
229-
```
230-
231-
Note that importing or manually applying any type to the meta or stories is no longer necessary. Thanks to the factory function pattern, the types are now inferred automatically.
232-
233-
#### 4.1 Reusing story properties
234-
235-
Previously, story properties such as `Story.args` or `Story.parameters` were accessed directly when reusing them in another story. While accessing them like this is still supported, it is deprecated in CSF Factories.
236-
237-
All of the story properties are now contained within a new property called `composed` and should be accessed from that property instead. For instance, `Story.composed.args` or `Story.composed.parameters`.
238-
239-
```diff title="Button.stories.js|ts"
240-
// ...rest of file
241-
242-
+ export const Primary = meta.story({
243-
- export const Primary: Story = {
244-
args: { primary: true },
245-
+ });
246-
- };
247-
248-
+ export const PrimaryDisabled = meta.story({
249-
- export const PrimaryDisabled: Story = {
250-
args: {
251-
+ ...Primary.composed.args,
252-
- ...Primary.args,
253-
disabled: true,
254-
}
255-
+ });
256-
- };
257-
```
258-
259-
<Callout variant="info">
260-
The property name "composed" was chosen because the values within are composed from the story, its component meta, and the preview configuration.
261-
262-
If you want to access the direct input to the story, you can use `Story.input` instead of `Story.composed`.
263-
</Callout>
264-
265-
### 5. Update your Vitest setup file
266-
267-
If you're using [Storybook's Vitest addon](../../writing-tests/integrations/vitest-addon.mdx), you can remove your Vitest setup file.
268-
269-
If you are using [portable stories in Vitest](../portable-stories/portable-stories-vitest.mdx), you may use a Vitest setup file to configure your stories. This file must be updated to use the new CSF Factories format.
270-
271-
<Callout variant="warning">
272-
Note that this only applies if you use CSF Factories for all your tested stories. If you use a mix of CSF 1, 2, or 3 and CSF Factories, you must maintain two separate setup files.
273-
</Callout>
274-
275-
```diff title="vitest.setup.js|ts"
276-
import { beforeAll } from 'vitest';
277-
// 👇 No longer necessary
278-
- // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
279-
import { setProjectAnnotations } from '@storybook/your-framework';
280-
- import * as addonAnnotations from 'my-addon/preview';
281-
+ import preview from './.storybook/preview';
282-
- import * as previewAnnotations from './.storybook/preview';
283-
284-
// No longer necessary
285-
- const annotations = setProjectAnnotations([previewAnnotations, addonAnnotations]);
286-
287-
// Run Storybook's beforeAll hook
288-
+ beforeAll(preview.composed.beforeAll);
289-
- beforeAll(annotations.beforeAll);
290-
```
291-
292-
### 6. Reusing stories in test files
293-
294-
[Storybook's Vitest addon](../../writing-tests/integrations/vitest-addon.mdx) allows you to test your components directly inside Storybook. All the stories are automatically turned into Vitest tests, making integration seamless in your testing suite.
295-
296-
If you cannot use Storybook Test, you can still reuse the stories in your test files using [portable stories](../portable-stories/portable-stories-vitest.mdx). In prior story formats, you had to compose the stories before rendering them in your test files. With CSF Factories, you can now reuse the stories directly.
297-
298-
```diff title="Button.test.js|ts"
299-
import { test, expect } from 'vitest';
300-
import { screen } from '@testing-library/react';
301-
- // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
302-
import { composeStories } from '@storybook/your-framework';
303-
304-
// Import all stories from the stories file
305-
import * as stories from './Button.stories';
306-
307-
+ const { Primary } = stories;
308-
- const { Primary } = composeStories(stories);
309-
310-
test('renders primary button with default args', async () => {
311-
// The run function will mount the component and run all of Storybook's lifecycle hooks
312-
await Primary.run();
313-
const buttonElement = screen.getByText('Text coming from args in stories file!');
314-
expect(buttonElement).not.toBeNull();
315-
});
316-
```
317-
318-
The `Story` object also provides a `Component` property, enabling you to render the component with any method you choose, such as [Testing Library](https://testing-library.com/). You can also access its composed properties ([`args`](../../writing-stories/args.mdx), [`parameters`](../../writing-stories/parameters.mdx), etc.) via the `composed` property.
319-
320-
Here's an example of how you can reuse a story in a test file by rendering its component:
321-
322-
{/* prettier-ignore-start */}
323-
<CodeSnippets path="portable-stories-csf-factory-render.md" />
324-
{/* prettier-ignore-end */}
325-
326-
## Frequently asked questions (FAQ)
327-
328-
### Will I have to migrate all of my stories to this new format?
329-
330-
Storybook will continue to support CSF 1, [CSF 2](../../../release-6-5/docs/api/stories/csf.mdx), and [CSF 3](./index.mdx) for the foreseeable future. None of these prior formats are deprecated.
331-
332-
While using CSF Factories, you can still use the older formats, as long as they are not mixed in the same file. If you want to migrate your existing files to the new format, refer to [the upgrade section](#upgrading-from-csf-3), above.
333-
334-
### Will this format work with MDX docs pages?
335-
336-
Yes, the [doc blocks](../../writing-docs/doc-blocks.mdx) used to reference stories in MDX files support the CSF Factories format with no changes needed.
337-
338-
### How can I know more about this format and provide feedback?
339-
340-
For more information on this experimental format's original proposal, refer to its [RFC on GitHub](https://github.com/storybookjs/storybook/discussions/30112). We welcome your comments!
341-
342-
</If>
11+
While this feature is experimental, we will be documenting progress in the [RFC](https://github.com/storybookjs/storybook/discussions/30112). We welcome your feedback!

docs/api/csf/index.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ The first option is the recommended solution if you follow the best practice of
173173

174174
## Upgrading from CSF 2 to CSF 3
175175

176+
<Callout variant="info">
177+
178+
Storybook provides a codemod to help you upgrade from CSF 2 to CSF 3. You can run it with the following command:
179+
180+
<CodeSnippets path="migrate-csf-2-to-3.md" />
181+
182+
</Callout>
183+
176184
In CSF 2, the named exports are always functions that instantiate a component, and those functions can be annotated with configuration options. For example:
177185

178186
{/* prettier-ignore-start */}

0 commit comments

Comments
 (0)