Skip to main content
Configures Metro bundler to work with Storybook in React Native. This function wraps a Metro configuration to enable Storybook usage.

Signature

function withStorybook(
  config: MetroConfig,
  options?: WithStorybookOptions
): MetroConfig

Parameters

config
MetroConfig
required
The Metro bundler configuration to be modified. This should be a valid Metro config object that includes resolver, transformer, and other Metro-specific options.
options
WithStorybookOptions
Options to customize the Storybook configuration. See Metro Options for detailed options.

Returns

config
MetroConfig
The modified Metro configuration with Storybook support enabled.

Usage

Basic Usage (Expo)

For basic usage with all defaults:
// 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);

With Custom Options

Configure Storybook with custom options:
// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withStorybook } = require('@storybook/react-native/metro/withStorybook');
const path = require('path');

const projectRoot = __dirname;
const config = getDefaultConfig(projectRoot);

module.exports = withStorybook(config, {
  configPath: path.resolve(projectRoot, './.rnstorybook'),
  websockets: { port: 7007, host: 'localhost' },
  useJs: false,
  docTools: true,
  liteMode: false,
});

Conditional Storybook (Production)

Disable Storybook in production builds:
// 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",
});

React Native CLI

// metro.config.js
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { withStorybook } = require('@storybook/react-native/metro/withStorybook');

const defaultConfig = getDefaultConfig(__dirname);
const config = {};

const finalConfig = mergeConfig(defaultConfig, config);

module.exports = withStorybook(finalConfig, {
  enabled: process.env.STORYBOOK_ENABLED === 'true',
  configPath: path.resolve(__dirname, './.rnstorybook'),
  websockets: {
    port: 7007,
    host: 'localhost',
  },
});

WebSocket Configuration

Enable WebSocket server for syncing stories across devices:
module.exports = withStorybook(config, {
  websockets: {
    port: 7007,
    host: 'localhost',
  },
});

// Or use 'auto' to automatically detect LAN IP
module.exports = withStorybook(config, {
  websockets: 'auto',
});

Behavior

When enabled: true (default):
  • Enables unstable_allowRequireContext in Metro transformer
  • Configures resolver for proper package exports and Storybook modules
  • Automatically generates storybook.requires.ts/js file
  • Optionally starts WebSocket server for remote story control
When enabled: false:
  • Removes all Storybook modules from bundle by resolving them as empty
  • Replaces config folder index file with a stub
  • Reduces bundle size by excluding Storybook dependencies

Build docs developers (and LLMs) love