Storybook for React Native
With Storybook for React Native you can design and develop individual React Native components without running your app.
This readme is for the 7.6.10 version, you can find the 6.5 docs here.
If you are migrating from 6.5 to 7.6 you can find the migration guide here
For more information about storybook visit: storybook.js.org
NOTE: @storybook/react-native
requires atleast 7.6.10, if you install other storybook core packages they should be ^7.6.10
or newer.
If you want to help out or are just curious then check out the project board to see the open issues.
Pictured is from the template mentioned in getting started
Table of contents
Getting Started
New project
There is some project boilerplate with @storybook/react-native
and @storybook/addons-react-native-web
both already configured with a simple example.
For expo you can use this template with the following command
npx create-expo-app --template expo-template-storybook AwesomeStorybook
For react native cli you can use this template
npx react-native init MyApp --template react-native-template-storybook
Existing project
Run init to setup your project with all the dependencies and configuration files:
npx storybook@latest init
The only thing left to do is return Storybook's UI in your app entry point (such as App.tsx
) like this:
export { default } from './.storybook';
If you want to be able to swap easily between storybook and your app, have a look at this blog post
If you want to add everything yourself check out the the manual guide here.
Additional steps: Update your metro config
We 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.
Expo
First create metro config file if you don't have it yet.
npx expo customize metro.config.js
Then set transformer.unstable_allowRequireContext
to true and add the generate call here.
const path = require('path');
const { getDefaultConfig } = require('expo/metro-config');
const { generate } = require('@storybook/react-native/scripts/generate');
generate({
configPath: path.resolve(__dirname, './.storybook'),
});
const config = getDefaultConfig(__dirname);
config.transformer.unstable_allowRequireContext = true;
module.exports = config;
React native
const path = require('path');
const { generate } = require('@storybook/react-native/scripts/generate');
generate({
configPath: path.resolve(__dirname, './.storybook'),
});
module.exports = {
transformer: {
unstable_allowRequireContext: true,
},
};
Writing stories
In storybook we use a syntax called CSF that looks like this:
import type { Meta, StoryObj } from '@storybook/react';
import { MyButton } from './Button';
const meta = {
component: MyButton,
} satisfies Meta<typeof MyButton>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Basic: Story = {
args: {
text: 'Hello World',
color: 'purple',
},
};
You should configure the path to your story files in the main.ts
config file from the .storybook
folder.
import { StorybookConfig } from '@storybook/react-native';
const main: StorybookConfig = {
stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
addons: [],
};
export default main;
Decorators and Parameters
For stories you can add decorators and parameters on the default export or on a specifc story.
import type { Meta } from '@storybook/react';
import { Button } from './Button';
const meta = {
title: 'Button',
component: Button,
decorators: [
(Story) => (
<View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
<Story />
</View>
),
],
parameters: {
backgrounds: {
values: [
{ name: 'red', value: '#f00' },
{ name: 'green', value: '#0f0' },
{ name: 'blue', value: '#00f' },
],
},
},
} satisfies Meta<typeof Button>;
export default meta;
For global decorators and parameters, you can add them to preview.tsx
inside your .storybook
folder.
import type { Preview } from '@storybook/react';
import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';
const preview: Preview = {
decorators: [
withBackgrounds,
(Story) => (
<View style={{ flex: 1, color: 'blue' }}>
<Story />
</View>
),
],
parameters: {
backgrounds: {
default: 'plain',
values: [
{ name: 'plain', value: 'white' },
{ name: 'warm', value: 'hotpink' },
{ name: 'cool', value: 'deepskyblue' },
],
},
},
};
export default preview;
Addons
The cli will install some basic addons for you such as controls and actions.
Ondevice addons are addons that can render with the device ui that you see on the phone.
Currently the addons available are:
Install each one you want to use and add them to the main.ts
addons list as follows:
import { StorybookConfig } from '@storybook/react-native';
const main: StorybookConfig = {
addons: [
'@storybook/addon-ondevice-notes',
'@storybook/addon-ondevice-controls',
'@storybook/addon-ondevice-backgrounds',
'@storybook/addon-ondevice-actions',
],
};
export default main;
Using the addons in your story
For details of each ondevice addon you can see the readme:
Hide/Show storybook
Storybook 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.
You can also create a separate app just for storybook that also works as a package for your visual components.
Some have opted to toggle the storybook component by using a custom option in the react native developer menu.
getStorybookUI options
You can pass these parameters to getStorybookUI call in your storybook entry point:
{
tabOpen: Number (0)
-- which tab should be open. -1 Sidebar, 0 Canvas, 1 Addons
initialSelection: string | Object (undefined)
-- initialize storybook with a specific story. eg: `mybutton--largebutton` or `{ kind: 'MyButton', name: 'LargeButton' }`
shouldDisableKeyboardAvoidingView: Boolean (false)
-- Disable KeyboardAvoidingView wrapping Storybook's view
keyboardAvoidingViewVerticalOffset: Number (0)
-- With shouldDisableKeyboardAvoidingView=true, this will set the keyboardverticaloffset (https://facebook.github.io/react-native/docs/keyboardavoidingview#keyboardverticaloffset) value for KeyboardAvoidingView wrapping Storybook's view
storage: Object (undefined)
-- {getItem: (key: string) => Promise<string | null>;setItem: (key: string, value: string) => Promise<void>;}
-- Custom storage to be used instead of AsyncStorage
shouldPersistSelection: Boolean (true)
-- Stores last selected story in your devices storage.
}
Contributing
We welcome contributions to Storybook!
- 📥 Pull requests and 🌟 Stars are always welcome.
- Read our contributing guide to get started,
or find us on Discord and look for the react-native channel.
Looking for a first issue to tackle?
- We tag issues with Good First Issue when we think they are well suited for people who are new to the codebase or OSS in general.
- Talk to us, we'll find something to suits your skills and learning interest.
Examples
Here are some example projects to help you get started