Skip to main content
This guide covers setting up Storybook manually without using the CLI. This is useful if you want full control over the setup process or if the CLI doesn’t work for your specific project configuration.
You can use any package manager (npm, yarn, or pnpm) throughout this guide.

Installation

1

Install Dependencies

Install all required Storybook dependencies:
npm install storybook @storybook/react-native @react-native-async-storage/async-storage react-dom react-native-safe-area-context react-native-reanimated react-native-gesture-handler @gorhom/bottom-sheet react-native-svg
If you’re working with React Native CLI or development clients, install iOS pods after installing dependencies:
cd ios && pod install && cd ..
2

Create Configuration Files

Create a .rnstorybook folder with the required configuration files:
mkdir .rnstorybook
touch .rnstorybook/main.ts .rnstorybook/preview.tsx .rnstorybook/index.tsx
3

Configure main.ts

In main.ts, configure the location of your stories:
.rnstorybook/main.ts
import type { StorybookConfig } from '@storybook/react-native';

const main: StorybookConfig = {
  stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
  addons: [],
};

export default main;
4

Configure preview.tsx

In preview.tsx, set up any global decorators or parameters:
.rnstorybook/preview.tsx
import type { Preview } from '@storybook/react-native';

const preview: Preview = {
  parameters: {},
  decorators: [],
};

export default preview;
5

Create Storybook UI Export

In index.tsx, export the Storybook UI component:
.rnstorybook/index.tsx
import { view } from './storybook.requires';
import AsyncStorage from '@react-native-async-storage/async-storage';

const StorybookUIRoot = view.getStorybookUI({
  storage: {
    getItem: AsyncStorage.getItem,
    setItem: AsyncStorage.setItem,
  },
});

export default StorybookUIRoot;
6

Configure Metro

Update your metro config to use the withStorybook wrapper function.
If you don’t have a metro config, generate one first:
npx expo customize metro.config.js
Then update it:
metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withStorybook } = require('@storybook/react-native/metro/withStorybook');

const defaultConfig = getDefaultConfig(__dirname);

module.exports = withStorybook(defaultConfig);
7

Add Story Generation Script

Add a script to your package.json for manual story generation:
package.json
{
  "scripts": {
    "storybook-generate": "sb-rn-get-stories"
  }
}
The withStorybook function automatically generates the storybook.requires.ts file when you run your app, but this script is useful for manual generation.
8

Render Storybook

Update your app to render the Storybook component. The simplest approach is to export Storybook from your app entry point:
App.tsx
import StorybookUI from './.rnstorybook';
export default StorybookUI;
For conditional rendering based on environment variables:
App.tsx
import { View, Text } from 'react-native';
import Constants from 'expo-constants';

function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.tsx to start working on your app!</Text>
    </View>
  );
}

let AppEntryPoint = App;

if (Constants.expoConfig?.extra?.storybookEnabled === 'true') {
  AppEntryPoint = require('./.rnstorybook').default;
}

export default AppEntryPoint;
9

Run Storybook

Run your app as normal:
npm run start
npm run ios     # or npm run android
If you’re using an environment variable to enable Storybook, add convenience scripts:
package.json
{
  "scripts": {
    "storybook": "STORYBOOK_ENABLED='true' expo start",
    "storybook:ios": "STORYBOOK_ENABLED='true' expo start --ios",
    "storybook:android": "STORYBOOK_ENABLED='true' expo start --android"
  }
}

Next Steps

Writing Stories

Learn how to write your first story

Metro Configuration

Explore advanced Metro config options

Addons

Add functionality with Storybook addons

Build docs developers (and LLMs) love