Skip to main content
This guide covers setting up Storybook in Expo projects that use Expo Router for file-based navigation.

Installation

1

Run CLI Init

Use the Storybook CLI to initialize Storybook in your project:
npm create storybook -- --type react_native --yes
When prompted, choose recommended and then native.
2

Install Dependencies

Storybook’s default UI depends on react-native-reanimated and react-native-worklets. If they’re not already installed:
npx expo install --fix react-native-reanimated react-native-worklets
Expo handles the babel plugin configuration automatically.
3

Configure Metro

Generate a metro config file if you don’t have one:
npx expo@latest customize metro.config.js
Wrap your config with withStorybook, using EXPO_PUBLIC_STORYBOOK_ENABLED to control inclusion:
metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withStorybook } = require('@storybook/react-native/metro/withStorybook');

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

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

Create Storybook Route

Create a route group for Storybook:
app/(storybook)/index.tsx
export { default } from '../../.rnstorybook';
5

Configure Layout

The layout configuration depends on your project’s navigation structure. Choose the pattern that matches your app:
Move your existing app routes into a route group (e.g., app/(pages)/):
app/
├── _layout.tsx
├── (storybook)/
│   └── index.tsx
└── (pages)/
    └── index.tsx    # existing app entry point moved here
Update the root layout:
app/_layout.tsx
import { Stack } from 'expo-router';

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

export const unstable_settings = {
  initialRouteName: storybookEnabled ? '(storybook)/index' : '(pages)/index',
};

export default function RootLayout() {
  return (
    <Stack screenOptions={{ headerShown: false }}>
      <Stack.Protected guard={storybookEnabled}>
        <Stack.Screen name="(storybook)/index" />
      </Stack.Protected>
      <Stack.Screen name="(pages)/index" />
    </Stack>
  );
}
The unstable_settings ensures your app opens directly to Storybook when EXPO_PUBLIC_STORYBOOK_ENABLED is set.
6

Add NPM Scripts

Add convenience scripts to your package.json:
package.json
{
  "scripts": {
    "storybook": "EXPO_PUBLIC_STORYBOOK_ENABLED='true' expo start",
    "storybook:ios": "EXPO_PUBLIC_STORYBOOK_ENABLED='true' expo start --ios",
    "storybook:android": "EXPO_PUBLIC_STORYBOOK_ENABLED='true' expo start --android"
  }
}
7

Run Storybook

Start your app with Storybook enabled:
npm run storybook

Video Tutorial

Watch this video tutorial for a visual walkthrough of the setup process:

Next Steps

Writing Stories

Learn how to write your first story

Metro Configuration

Explore advanced Metro config options

Build docs developers (and LLMs) love