React Native Web
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.
Requirements
Getting started
Installation
Install the framework package and its peer dependencies:
npm install storybook-react-native-web-rsbuild react-native-web @rsbuild/plugin-react -D
yarn add storybook-react-native-web-rsbuild react-native-web @rsbuild/plugin-react -D
pnpm add storybook-react-native-web-rsbuild react-native-web @rsbuild/plugin-react -D
bun add storybook-react-native-web-rsbuild react-native-web @rsbuild/plugin-react -D
deno add npm:storybook-react-native-web-rsbuild npm:react-native-web npm:@rsbuild/plugin-react -D
Create 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()],
})
Configure .storybook/main.ts
import 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 config
Features
The framework automatically handles:
- Alias resolution:
react-native → react-native-web
- Web extensions: Prioritizes
.web.tsx, .web.ts, .web.js files
- Global definitions: Sets up
__DEV__, EXPO_OS, and other React Native globals
- Package transpilation: Transpiles
react-native, @react-native, expo, and @expo packages
- React docgen: Full support for props documentation via react-docgen
Framework Options
interface 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[]
}
Example: Transpiling additional packages
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',
],
},
},
}
Writing Stories
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',
},
})
NativeWind Support
This framework supports NativeWind for using Tailwind CSS with React Native components.
Installation
npm install nativewind react-native-css-interop react-native-safe-area-context tailwindcss postcss autoprefixer -D
yarn add nativewind react-native-css-interop react-native-safe-area-context tailwindcss postcss autoprefixer -D
pnpm add nativewind react-native-css-interop react-native-safe-area-context tailwindcss postcss autoprefixer -D
bun add nativewind react-native-css-interop react-native-safe-area-context tailwindcss postcss autoprefixer -D
deno add npm:nativewind npm:react-native-css-interop npm:react-native-safe-area-context npm:tailwindcss npm:postcss npm:autoprefixer -D
Tip
Installing react-native-safe-area-context is recommended to avoid build warnings from react-native-css-interop.
Configuration
- Configure Tailwind CSS - Create
tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
presets: [require('nativewind/preset')],
theme: {
extend: {},
},
plugins: [],
}
- Create global CSS - Create
src/global.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
- Import CSS in preview - Update
.storybook/preview.tsx:
import '../src/global.css'
const preview = {
// your preview config
}
export default preview
- Configure JSX import source - Update
.storybook/main.ts:
const config: StorybookConfig = {
framework: {
name: 'storybook-react-native-web-rsbuild',
options: {
pluginOptions: {
jsxImportSource: 'nativewind',
},
},
},
}
Usage
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>
)
}
React Native Reanimated
This framework provides built-in support for React Native Reanimated on web.
Installation
npm install react-native-reanimated react-native-worklets
yarn add react-native-reanimated react-native-worklets
pnpm add react-native-reanimated react-native-worklets
bun add react-native-reanimated react-native-worklets
deno add npm:react-native-reanimated npm:react-native-worklets
What the framework handles
The framework automatically:
- Defines
_WORKLET and _frameTimestamp globals required by Reanimated
- Transforms Reanimated's webUtils for ESM compatibility
- Handles module resolution in pnpm/monorepo environments
Usage
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'
export function FadeInView({ children }) {
return (
<Animated.View entering={FadeIn.duration(500)} exiting={FadeOut}>
{children}
</Animated.View>
)
}
Expo Support
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'
Next Steps
Troubleshooting
"React is not defined" error
Make sure you have @rsbuild/plugin-react installed and configured in rsbuild.config.ts. This plugin enables the automatic JSX runtime.
Module not found errors for React Native packages
Add the problematic package to modulesToTranspile in your framework options.
TypeScript errors for react-native imports
Install @types/react-native as a dev dependency, or create a react-native.d.ts file:
declare module 'react-native' {
export * from 'react-native-web'
}
Flow syntax errors in React Native packages
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.
- Install the required packages:
pnpm add -D @babel/core babel-loader @babel/preset-flow @babel/preset-react
- Add a custom Rsbuild configuration in
.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.