Skip to main content

Development Workflows

This guide covers common development workflows and patterns when working with Storybook for React Native.

Project Setup

New Project

Quickly bootstrap a new project with Storybook pre-configured:
npx create-expo-app --template expo-template-storybook AwesomeStorybook
This template includes:
  • Storybook configured in .rnstorybook/
  • Metro config with withStorybook wrapper
  • Example stories
  • On-device addons (controls, actions, backgrounds)

Existing Project

Add Storybook to an existing React Native app:
1

Run the init command

npm create storybook@latest
This automatically installs dependencies and creates:
  • .rnstorybook/ configuration directory
  • main.ts for Storybook configuration
  • preview.tsx for global decorators/parameters
2

Update Metro configuration

Create or update metro.config.js:
// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withStorybook } = require('@storybook/react-native/metro/withStorybook');

const config = getDefaultConfig(__dirname);

module.exports = withStorybook(config, {
  enabled: process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === 'true',
});
3

Configure app entry point

Update your app entry (e.g., App.tsx):
// Direct export (simplest)
export { default } from './.rnstorybook';
Or with conditional rendering:
import StorybookUI from './.rnstorybook';
import { MyApp } from './MyApp';

const isStorybook = process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === 'true';

export default function App() {
  return isStorybook ? <StorybookUI /> : <MyApp />;
}
4

Install reanimated

Storybook UI requires react-native-reanimated:
npx expo install react-native-reanimated
Add the plugin to babel.config.js:
module.exports = {
  plugins: ['react-native-reanimated/plugin'],
};

Writing Stories

Component Story Format (CSF)

Storybook uses CSF, an ES6 module-based standard:
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react-native';
import { Button } from './Button';

const meta = {
  component: Button,
} satisfies Meta<typeof Button>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Primary: Story = {
  args: {
    text: 'Hello World',
    color: 'purple',
  },
};

export const Secondary: Story = {
  args: {
    ...Primary.args,
    color: 'blue',
  },
};

File Organization

Organize stories alongside components:
components/
├─ Button/
│  ├─ Button.tsx
│  ├─ Button.stories.tsx
│  └─ Button.test.tsx
├─ TextInput/
│  ├─ TextInput.tsx
│  ├─ TextInput.stories.tsx
│  └─ TextInput.test.tsx

Story Configuration

Configure story paths in .rnstorybook/main.ts:
import type { StorybookConfig } from '@storybook/react-native';

const main: StorybookConfig = {
  stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
  addons: [
    '@storybook/addon-ondevice-controls',
    '@storybook/addon-ondevice-actions',
    '@storybook/addon-ondevice-backgrounds',
  ],
};

export default main;

Working with Addons

Available On-Device Addons

Adjust component props in real-time:
import type { Meta, StoryObj } from '@storybook/react-native';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  component: Button,
  argTypes: {
    color: {
      control: 'color',
    },
    size: {
      control: { type: 'select' },
      options: ['small', 'medium', 'large'],
    },
  },
};

export default meta;

Decorators and Parameters

Component-Level Decorators

Wrap stories with common layout or context:
import { View } from 'react-native';
import type { Meta } from '@storybook/react-native';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  component: Button,
  decorators: [
    (Story) => (
      <View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
        <Story />
      </View>
    ),
  ],
};

export default meta;

Global Decorators

Apply decorators to all stories in .rnstorybook/preview.tsx:
import type { Preview } from '@storybook/react-native';
import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';
import { View } from 'react-native';

const preview: Preview = {
  decorators: [
    withBackgrounds,
    (Story) => (
      <View style={{ flex: 1, padding: 16 }}>
        <Story />
      </View>
    ),
  ],
  parameters: {
    backgrounds: {
      default: 'plain',
      values: [
        { name: 'plain', value: 'white' },
        { name: 'dark', value: '#333' },
      ],
    },
  },
};

export default preview;

UI Configuration Parameters

Control the Storybook UI behavior per story:
export const FullScreen: Story = {
  args: {
    label: 'Button',
  },
  parameters: {
    // Hide UI initially (fullscreen mode)
    storybookUIVisibility: 'hidden',
    // Remove safe area padding (edge-to-edge)
    noSafeArea: true,
    // Center the component
    layout: 'centered',
    // Hide fullscreen toggle button
    hideFullScreenButton: true,
  },
};
Available layout values: 'padded', 'centered', 'fullscreen'

Environment Switching

Toggle Between App and Storybook

Control via Metro config:
// App.tsx
export { default } from './.rnstorybook';
// metro.config.js
module.exports = withStorybook(config, {
  enabled: process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === 'true',
});
When enabled: false, Metro removes Storybook from the bundle.

Development Tips

Hot Reloading: Stories automatically reload when you save changes. Metro watches your story files and regenerates storybook.requires.ts.
Story Naming: Use descriptive names that become test descriptions when using portable stories in Jest.
Reset Metro Cache: If stories don’t appear, clear the cache:
npx react-native start --reset-cache

Lite Mode (Reduced Bundle Size)

For apps that need smaller bundles or don’t want heavy dependencies:
// .rnstorybook/index.tsx
import { LiteUI } from '@storybook/react-native-ui-lite';
import { view } from './storybook.requires';

const StorybookUIRoot = view.getStorybookUI({
  CustomUIComponent: LiteUI,
});

export default StorybookUIRoot;
// metro.config.js
module.exports = withStorybook(config, {
  liteMode: true,
});
Lite mode removes dependencies like react-native-reanimated.

Feature Flags

Opt into new functionality via main.ts:
import type { StorybookConfig } from '@storybook/react-native';

const main: StorybookConfig = {
  stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
  addons: ['@storybook/addon-ondevice-controls'],
  features: {
    // New backgrounds API with full-screen support (v10.3+)
    ondeviceBackgrounds: true,
  },
};

export default main;
Feature flags become the default in the next major version. Enable them early to future-proof your setup.

Build docs developers (and LLMs) love