Storybook for React Native Web & Rsbuild enables you to develop and document React Native components that run on the web using react-native-web.
This framework extends storybook-react-rsbuild to provide full React Native Web compatibility, including alias resolution, web-specific extensions, and transpilation of React Native packages.
| Package | Version |
|---|---|
react | ≥ 16.8 |
react-native-web | ≥ 0.19.0 |
@rsbuild/core | ≥ 1.5.0 |
@rsbuild/plugin-react | ≥ 1.0.0 |
storybook | ≥ 10.1.0 |
Install the framework package and its peer dependencies:
npm install storybook-react-native-web-rsbuild react-native-web @rsbuild/plugin-react -Drsbuild.config.tsCreate or update your Rsbuild configuration to include the React plugin:
import { defineConfig } from '@rsbuild/core'
import { pluginReact } from '@rsbuild/plugin-react'
export default defineConfig({
plugins: [pluginReact()],
}).storybook/main.tsimport type { StorybookConfig } from 'storybook-react-native-web-rsbuild'
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
framework: {
name: 'storybook-react-native-web-rsbuild',
options: {},
},
}
export default configThe framework automatically handles:
react-native → react-native-web.web.tsx, .web.ts, .web.js files__DEV__, EXPO_OS, and other React Native globalsreact-native, @react-native, expo, and @expo packagesinterface FrameworkOptions {
/**
* Additional node_modules that need to be transpiled.
* Packages starting with `react-native`, `@react-native`, `expo`, and `@expo`
* are included by default.
*/
modulesToTranspile?: string[]
/**
* Options passed to the underlying rsbuild-plugin-react-native-web plugin.
*/
pluginOptions?: PluginReactNativeWebOptions
}
interface PluginReactNativeWebOptions {
/**
* Additional node_modules that need to be transpiled.
* @example ['my-react-native-library']
*/
modulesToTranspile?: string[]
/**
* The JSX runtime to use.
* @default 'automatic'
*/
jsxRuntime?: 'automatic' | 'classic'
/**
* The source for JSX imports when using the automatic runtime.
* @default 'react'
* @example 'nativewind' for NativeWind v4+
*/
jsxImportSource?: string
/**
* Modules that should not be tree-shaken.
* @default ['react-native-css-interop', 'expo-modules-core']
*/
noTreeshakeModules?: string[]
}Some React Native libraries ship untranspiled code. Add them to modulesToTranspile:
const config: StorybookConfig = {
framework: {
name: 'storybook-react-native-web-rsbuild',
options: {
modulesToTranspile: [
'react-native-reanimated',
'@react-navigation/native',
],
},
},
}Write stories using standard React Native components:
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './Button'
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
}
export default meta
type Story = StoryObj<typeof Button>
export const Primary: Story = {
args: {
label: 'Press me',
variant: 'primary',
},
}// Button.tsx
import { Text, TouchableOpacity, StyleSheet } from 'react-native'
export function Button({ label, onPress, variant = 'primary' }) {
return (
<TouchableOpacity onPress={onPress} style={[styles.button, styles[variant]]}>
<Text style={styles.text}>{label}</Text>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
button: {
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 8,
},
primary: {
backgroundColor: '#007AFF',
},
text: {
color: 'white',
fontWeight: 'bold',
},
})This framework supports NativeWind for using Tailwind CSS with React Native components.
npm install nativewind react-native-css-interop react-native-safe-area-context tailwindcss postcss autoprefixer -D
Installing react-native-safe-area-context is recommended to avoid build warnings from react-native-css-interop.
tailwind.config.js:/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
presets: [require('nativewind/preset')],
theme: {
extend: {},
},
plugins: [],
}src/global.css:@tailwind base;
@tailwind components;
@tailwind utilities;.storybook/preview.tsx:import '../src/global.css'
const preview = {
// your preview config
}
export default preview.storybook/main.ts:const config: StorybookConfig = {
framework: {
name: 'storybook-react-native-web-rsbuild',
options: {
pluginOptions: {
jsxImportSource: 'nativewind',
},
},
},
}import { View, Text } from 'react-native'
export function Card({ title, children }) {
return (
<View className="p-4 bg-white rounded-lg shadow-md">
<Text className="text-xl font-bold text-gray-800">{title}</Text>
{children}
</View>
)
}This framework provides built-in support for React Native Reanimated on web.
npm install react-native-reanimated react-native-workletsThe framework automatically:
_WORKLET and _frameTimestamp globals required by Reanimatedimport Animated, { FadeIn, FadeOut } from 'react-native-reanimated'
export function FadeInView({ children }) {
return (
<Animated.View entering={FadeIn.duration(500)} exiting={FadeOut}>
{children}
</Animated.View>
)
}This framework works with Expo projects. Ensure your metro.config.js or bundler configuration is not conflicting with Rsbuild.
For Expo-specific globals, the plugin automatically defines:
EXPO_OS → 'web'process.env.EXPO_OS → 'web'Make sure you have @rsbuild/plugin-react installed and configured in rsbuild.config.ts. This plugin enables the automatic JSX runtime.
Add the problematic package to modulesToTranspile in your framework options.
react-native importsInstall @types/react-native as a dev dependency, or create a react-native.d.ts file:
declare module 'react-native' {
export * from 'react-native-web'
}Some older React Native packages may contain Flow type annotations that are not stripped before publishing. If you encounter syntax errors related to Flow types (e.g., Unexpected token : in type annotations), you can use Babel to strip Flow types.
pnpm add -D @babel/core babel-loader @babel/preset-flow @babel/preset-react.storybook/main.ts:const config: StorybookConfig = {
framework: {
name: 'storybook-react-native-web-rsbuild',
options: {},
},
rsbuildFinal: async (config) => {
config.tools ??= {}
config.tools.bundlerChain = (chain) => {
// Add babel-loader for packages with Flow syntax
chain.module
.rule('flow')
.test(/\.jsx?$/)
.include.add(/node_modules\/(react-native|@react-native)/)
.end()
.use('babel')
.loader('babel-loader')
.options({
presets: ['@babel/preset-flow', '@babel/preset-react'],
})
}
return config
},
}This configuration uses Babel to process React Native packages and strip Flow type annotations before they are handled by SWC.