UNPKG

14.6 kBMarkdownView Raw
1# Storybook for React Native
2
3> [!IMPORTANT]
4> This readme is for v8, for v7 docs see the [v7.6 docs](https://github.com/storybookjs/react-native/tree/v7.6.20-stable).
5
6With Storybook for React Native you can design and develop individual React Native components without running your app.
7
8If you are migrating from 7.6 to 8.3 you can find the migration guide [here](https://github.com/storybookjs/react-native/blob/next/MIGRATION.md#from-version-76x-to-83x)
9
10For more information about storybook visit: [storybook.js.org](https://storybook.js.org)
11
12> [!NOTE]
13> `@storybook/react-native` requires atleast 8.3.1, if you install other storybook core packages they should be `^8.3.1` or newer.
14
15![picture of storybook](https://github.com/user-attachments/assets/cf98766d-8b90-44ab-b718-94ab16e63205)
16
17## Table of contents
18
19- 🚀 [Getting Started](#getting-started)
20- 📒 [Writing stories](#writing-stories)
21- 🔌 [Addons](#addons)
22- 📱 [Hide/Show Storybook](#hideshow-storybook)
23- ⚙️ [withStorybook wrapper](#withstorybook-wrapper)
24- 🔧 [getStorybookUI](#getstorybookui-options)
25- 🧪 [Using stories in unit tests](#using-stories-in-unit-tests)
26- 🤝 [Contributing](#contributing)
27- ✨ [Examples](#examples)
28
29## Getting Started
30
31### New project
32
33There is some project boilerplate with `@storybook/react-native` and `@storybook/addon-react-native-web` both already configured with a simple example.
34
35For expo you can use this [template](https://github.com/dannyhw/expo-template-storybook) with the following command
36
37```sh
38# With NPM
39npx create-expo-app --template expo-template-storybook AwesomeStorybook
40```
41
42For react native cli you can use this [template](https://github.com/dannyhw/react-native-template-storybook)
43
44```sh
45npx react-native init MyApp --template react-native-template-storybook
46```
47
48### Existing project
49
50Run init to setup your project with all the dependencies and configuration files:
51
52```sh
53npx storybook@latest init
54```
55
56The only thing left to do is return Storybook's UI in your app entry point (such as `App.tsx`) like this:
57
58```tsx
59export { default } from './.storybook';
60```
61
62Then wrap your metro config with the withStorybook function as seen [below](#additional-steps-update-your-metro-config)
63
64If you want to be able to swap easily between storybook and your app, have a look at this [blog post](https://dev.to/dannyhw/how-to-swap-between-react-native-storybook-and-your-app-p3o)
65
66If you want to add everything yourself check out the the manual guide [here](https://github.com/storybookjs/react-native/blob/next/MANUAL_SETUP.md).
67
68#### Additional steps: Update your metro config
69
70We require the unstable_allowRequireContext transformer option to enable dynamic story imports based on the stories glob in `main.ts`. We can also call the storybook generate function from the metro config to automatically generate the `storybook.requires.ts` file when metro runs.
71
72**Expo**
73
74First create metro config file if you don't have it yet.
75
76```sh
77npx expo customize metro.config.js
78```
79
80Then wrap your config in the withStorybook function as seen below.
81
82```js
83// metro.config.js
84const path = require('path');
85const { getDefaultConfig } = require('expo/metro-config');
86const withStorybook = require('@storybook/react-native/metro/withStorybook');
87
88/** @type {import('expo/metro-config').MetroConfig} */
89const config = getDefaultConfig(__dirname);
90
91module.exports = withStorybook(config, {
92 // Set to false to remove storybook specific options
93 // you can also use a env variable to set this
94 enabled: true,
95 // Path to your storybook config
96 configPath: path.resolve(__dirname, './.storybook'),
97
98 // Optional websockets configuration
99 // Starts a websocket server on the specified port and host on metro start
100 // websockets: {
101 // port: 7007,
102 // host: 'localhost',
103 // },
104});
105```
106
107**React native**
108
109```js
110const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
111const path = require('path');
112const withStorybook = require('@storybook/react-native/metro/withStorybook');
113const defaultConfig = getDefaultConfig(__dirname);
114
115/**
116 * Metro configuration
117 * https://reactnative.dev/docs/metro
118 *
119 * @type {import('metro-config').MetroConfig}
120 */
121const config = {};
122// set your own config here 👆
123
124const finalConfig = mergeConfig(defaultConfig, config);
125
126module.exports = withStorybook(finalConfig, {
127 // Set to false to remove storybook specific options
128 // you can also use a env variable to set this
129 enabled: true,
130 // Path to your storybook config
131 configPath: path.resolve(__dirname, './.storybook'),
132
133 // Optional websockets configuration
134 // Starts a websocket server on the specified port and host on metro start
135 // websockets: {
136 // port: 7007,
137 // host: 'localhost',
138 // },
139});
140```
141
142## Writing stories
143
144In storybook we use a syntax called CSF that looks like this:
145
146```tsx
147import type { Meta, StoryObj } from '@storybook/react';
148import { MyButton } from './Button';
149
150const meta = {
151 component: MyButton,
152} satisfies Meta<typeof MyButton>;
153
154export default meta;
155
156type Story = StoryObj<typeof meta>;
157
158export const Basic: Story = {
159 args: {
160 text: 'Hello World',
161 color: 'purple',
162 },
163};
164```
165
166You should configure the path to your story files in the `main.ts` config file from the `.storybook` folder.
167
168```ts
169// .storybook/main.ts
170import { StorybookConfig } from '@storybook/react-native';
171
172const main: StorybookConfig = {
173 stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
174 addons: [],
175};
176
177export default main;
178```
179
180### Decorators and Parameters
181
182For stories you can add decorators and parameters on the default export or on a specifc story.
183
184```tsx
185import type { Meta } from '@storybook/react';
186import { Button } from './Button';
187
188const meta = {
189 title: 'Button',
190 component: Button,
191 decorators: [
192 (Story) => (
193 <View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
194 <Story />
195 </View>
196 ),
197 ],
198 parameters: {
199 backgrounds: {
200 values: [
201 { name: 'red', value: '#f00' },
202 { name: 'green', value: '#0f0' },
203 { name: 'blue', value: '#00f' },
204 ],
205 },
206 },
207} satisfies Meta<typeof Button>;
208
209export default meta;
210```
211
212For global decorators and parameters, you can add them to `preview.tsx` inside your `.storybook` folder.
213
214```tsx
215// .storybook/preview.tsx
216import type { Preview } from '@storybook/react';
217import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';
218
219const preview: Preview = {
220 decorators: [
221 withBackgrounds,
222 (Story) => (
223 <View style={{ flex: 1, color: 'blue' }}>
224 <Story />
225 </View>
226 ),
227 ],
228 parameters: {
229 backgrounds: {
230 default: 'plain',
231 values: [
232 { name: 'plain', value: 'white' },
233 { name: 'warm', value: 'hotpink' },
234 { name: 'cool', value: 'deepskyblue' },
235 ],
236 },
237 },
238};
239
240export default preview;
241```
242
243## Addons
244
245The cli will install some basic addons for you such as controls and actions.
246Ondevice addons are addons that can render with the device ui that you see on the phone.
247
248Currently the addons available are:
249
250- [`@storybook/addon-ondevice-controls`](https://storybook.js.org/addons/@storybook/addon-ondevice-controls): adjust your components props in realtime
251- [`@storybook/addon-ondevice-actions`](https://storybook.js.org/addons/@storybook/addon-ondevice-actions): mock onPress calls with actions that will log information in the actions tab
252- [`@storybook/addon-ondevice-notes`](https://storybook.js.org/addons/@storybook/addon-ondevice-notes): Add some markdown to your stories to help document their usage
253- [`@storybook/addon-ondevice-backgrounds`](https://storybook.js.org/addons/@storybook/addon-ondevice-backgrounds): change the background of storybook to compare the look of your component against different backgrounds
254
255Install each one you want to use and add them to the `main.ts` addons list as follows:
256
257```ts
258// .storybook/main.ts
259import { StorybookConfig } from '@storybook/react-native';
260
261const main: StorybookConfig = {
262 // ... rest of config
263 addons: [
264 '@storybook/addon-ondevice-notes',
265 '@storybook/addon-ondevice-controls',
266 '@storybook/addon-ondevice-backgrounds',
267 '@storybook/addon-ondevice-actions',
268 ],
269};
270
271export default main;
272```
273
274### Using the addons in your story
275
276For details of each ondevice addon you can see the readme:
277
278- [actions](https://github.com/storybookjs/react-native/tree/next/packages/ondevice-actions#readme)
279- [backgrounds](https://github.com/storybookjs/react-native/tree/next/packages/ondevice-backgrounds#readme)
280- [controls](https://github.com/storybookjs/react-native/tree/next/packages/ondevice-controls#readme)
281- [notes](https://github.com/storybookjs/react-native/tree/next/packages/ondevice-notes#readme)
282
283## Hide/Show storybook
284
285Storybook on react native is a normal React Native component that can be used or hidden anywhere in your RN application based on your own logic.
286
287You can also create a separate app just for storybook that also works as a package for your visual components.
288Some have opted to toggle the storybook component by using a custom option in the react native developer menu.
289
290- [Heres an approach for react native cli](https://dev.to/dannyhw/multiple-entry-points-for-react-native-storybook-4dkp)
291- [Heres an article about how you can do it in expo](https://dev.to/dannyhw/how-to-swap-between-react-native-storybook-and-your-app-p3o)
292
293## withStorybook wrapper
294
295`withStorybook` is a wrapper function to extend your [Metro config](https://metrobundler.dev/docs/configuration) for Storybook. It accepts your existing Metro config and an object of options for how Storybook should be started and configured.
296
297```js
298// metro.config.js
299const { getDefaultConfig } = require('expo/metro-config');
300const withStorybook = require('@storybook/react-native/metro/withStorybook');
301
302const defaultConfig = getDefaultConfig(__dirname);
303
304module.exports = withStorybook(defaultConfig, {
305 enabled: true,
306 // See API section below for available options
307});
308```
309
310### Options
311
312#### enabled
313
314Type: `boolean`, default: `true`
315
316Determines whether the options specified are applied to the Metro config. This can be useful for project setups that use Metro both with and without Storybook and need to conditionally apply the options. In this example, it is made conditional using an environment variable:
317
318```js
319// metro.config.js
320const { getDefaultConfig } = require('expo/metro-config');
321const withStorybook = require('@storybook/react-native/metro/withStorybook');
322
323const defaultConfig = getDefaultConfig(__dirname);
324
325module.exports = withStorybook(defaultConfig, {
326 enabled: process.env.WITH_STORYBOOK,
327 // ... other options
328});
329```
330
331#### onDisabledRemoveStorybook
332
333Type: `boolean`, default: `false`
334
335If onDisabledRemoveStorybook `true` and `enabled` is `false`, the storybook package will be removed from the build.
336This is useful if you want to remove storybook from your production build.
337
338#### useJs
339
340Type: `boolean`, default: `false`
341
342Generates the `.storybook/storybook.requires` file in JavaScript instead of TypeScript.
343
344#### configPath
345
346Type: `string`, default: `path.resolve(process.cwd(), './.storybook')`
347
348The location of your Storybook configuration directory, which includes `main.ts` and other project-related files.
349
350### websockets
351
352Type: `{ host: string?, port: number? }`, default: `undefined`
353
354If specified, create a WebSocket server on startup. This allows you to sync up multiple devices to show the same story and [arg](https://storybook.js.org/docs/writing-stories/args) values connected to the story in the UI.
355
356### websockets.host
357
358Type: `string`, default: `'localhost'`
359
360The host on which to run the WebSocket, if specified.
361
362### websockets.port
363
364Type: `number`, default: `7007`
365
366The port on which to run the WebSocket, if specified.
367
368## getStorybookUI options
369
370You can pass these parameters to getStorybookUI call in your storybook entry point:
371
372```
373{
374 initialSelection?: string | Object (undefined)
375 -- initialize storybook with a specific story. eg: `mybutton--largebutton` or `{ kind: 'MyButton', name: 'LargeButton' }`
376 storage?: Object (undefined)
377 -- {getItem: (key: string) => Promise<string | null>;setItem: (key: string, value: string) => Promise<void>;}
378 -- Custom storage to be used instead of AsyncStorage
379 shouldPersistSelection: Boolean (true)
380 -- Stores last selected story in your devices storage.
381 onDeviceUI?: boolean;
382 -- show the ondevice ui
383 enableWebsockets?: boolean;
384 -- enable websockets for the storybook ui
385 query?: string;
386 -- query params for the websocket connection
387 host?: string;
388 -- host for the websocket connection
389 port?: number;
390 -- port for the websocket connection
391 secured?: boolean;
392 -- use secured websockets
393 shouldPersistSelection?: boolean;
394 -- store the last selected story in the device's storage
395 theme: Partial<Theme>;
396 -- theme for the storybook ui
397}
398```
399
400## Using stories in unit tests
401
402Storybook provides testing utilities that allow you to reuse your stories in external test environments, such as Jest. This way you can write unit tests easier and reuse the setup which is already done in Storybook, but in your unit tests. You can find more information about it in the [portable stories section](./PORTABLE_STORIES.md).
403
404## Contributing
405
406We welcome contributions to Storybook!
407
408- 📥 Pull requests and 🌟 Stars are always welcome.
409- Read our [contributing guide](CONTRIBUTING.md) to get started,
410 or find us on [Discord](https://discord.gg/sMFvFsG) and look for the react-native channel.
411
412Looking for a first issue to tackle?
413
414- We tag issues with [Good First Issue](https://github.com/storybookjs/react-native/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) when we think they are well suited for people who are new to the codebase or OSS in general.
415- [Talk to us](https://discord.gg/sMFvFsG), we'll find something to suits your skills and learning interest.
416
417## Examples
418
419Here are some example projects to help you get started
420
421- A mono repo setup by @axeldelafosse https://github.com/axeldelafosse/storybook-rnw-monorepo
422- Expo setup https://github.com/dannyhw/expo-storybook-starter
423- React native cli setup https://github.com/dannyhw/react-native-storybook-starter
424- Adding a separate entry point and dev menu item in native files for RN CLI project: https://github.com/zubko/react-native-storybook-with-dev-menu
425- Want to showcase your own project? open a PR and add it to the list!