Author React Native Storybook stories in CSF format for .stories.tsx files, including args, argTypes, decorators, parameters, custom renders, actions, and controls.
From react-native-storybooknpx claudepluginhub transaurus/staging-storybookjs-react-nativeThis skill uses the workspace's default tool permissions.
references/controls.mdGuides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Analyzes BMad project state from catalog CSV, configs, artifacts, and query to recommend next skills or answer questions. Useful for help requests, 'what next', or starting BMad.
Write stories for React Native components using @storybook/react-native v10 and Component Story Format (CSF).
Minimal story file:
import type { Meta, StoryObj } from '@storybook/react-native';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta<typeof MyComponent>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Basic: Story = {
args: {
label: 'Hello',
},
};
ComponentName.stories.tsx colocated with the componentMeta and StoryObj from @storybook/react-nativemeta object with satisfies Meta<typeof Component>StoryObj<typeof meta>args for props, argTypes for control config, parameters for addon configrender for custom render functions, decorators for wrappersexport const Primary: Story = {
args: { variant: 'primary', title: 'Click me' },
};
export const Secondary: Story = {
args: { ...Primary.args, variant: 'secondary' },
};
export const WithScrollView: Story = {
render: (args) => (
<ScrollView>
<MyComponent {...args} />
</ScrollView>
),
};
export const Interactive: Story = {
render: function InteractiveRender() {
const [count, setCount] = useReducer((s) => s + 1, 0);
return <Counter count={count} onPress={setCount} />;
},
};
import { fn } from 'storybook/test';
const meta = {
component: Button,
args: { onPress: fn() },
} satisfies Meta<typeof Button>;
Or via argTypes:
argTypes: { onPress: { action: 'pressed' } },
export const MyStory: Story = {
storyName: 'Custom Display Name',
args: { label: 'Hello' },
};
const meta = {
title: 'NestingExample/Message/Bubble',
component: MyComponent,
} satisfies Meta<typeof MyComponent>;
For the full control type reference, see references/controls.md.
Common patterns:
const meta = {
component: MyComponent,
argTypes: {
// Select dropdown
size: {
options: ['small', 'medium', 'large'],
control: { type: 'select' },
},
// Range slider
opacity: {
control: { type: 'range', min: 0, max: 1, step: 0.1 },
},
// Color picker
color: { control: { type: 'color' } },
// Conditional control (shows only when `advanced` arg is true)
padding: { control: 'number', if: { arg: 'advanced' } },
},
} satisfies Meta<typeof MyComponent>;
Auto-detection: TypeScript prop types are automatically mapped to controls (string -> text, boolean -> boolean, union types -> select, number -> number).
parameters: {
// Markdown docs in the Notes addon tab
notes: `# MyComponent\nUsage: \`<MyComponent label="hi" />\``,
// Background options for Backgrounds addon
backgrounds: {
default: 'dark',
values: [
{ name: 'light', value: 'white' },
{ name: 'dark', value: '#333' },
],
},
},
| Parameter | Type | Description |
|---|---|---|
noSafeArea | boolean | Remove top safe area padding. When using this, the component itself must handle safe areas since Storybook will no longer provide safe area padding. Prefer useSafeAreaInsets() over SafeAreaView — apply insets as paddingTop/paddingBottom on the container, and for scrollable content use contentContainerStyle padding instead of wrapping in SafeAreaView. |
storybookUIVisibility | 'visible' | 'hidden' | Initial UI visibility |
hideFullScreenButton | boolean | Hide fullscreen toggle |
layout | 'padded' | 'centered' | 'fullscreen' | Story container layout |
Parameters can be set at story, meta (component), or global (preview.tsx) level.
Wrap stories in providers, layouts, or context:
const meta = {
component: MyComponent,
decorators: [
(Story) => (
<View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
<Story />
</View>
),
],
} satisfies Meta<typeof MyComponent>;
Global decorators go in .rnstorybook/preview.tsx:
import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';
import type { Preview } from '@storybook/react-native';
const preview: Preview = {
decorators: [withBackgrounds],
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
backgrounds: {
default: 'plain',
values: [
{ name: 'plain', value: 'white' },
{ name: 'dark', value: '#333' },
],
},
},
};
export default preview;
import type { StorybookConfig } from '@storybook/react-native';
const main: StorybookConfig = {
stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
addons: [
'@storybook/addon-ondevice-controls',
'@storybook/addon-ondevice-backgrounds',
'@storybook/addon-ondevice-actions',
'@storybook/addon-ondevice-notes',
],
framework: '@storybook/react-native',
};
export default main;
Story globs also support the object form for multi-directory setups:
stories: [
'../components/**/*.stories.?(ts|tsx|js|jsx)',
{ directory: '../other_components', files: '**/*.stories.?(ts|tsx|js|jsx)' },
],
Reuse stories in Jest tests:
import { render, screen } from '@testing-library/react-native';
import { composeStories } from '@storybook/react';
import * as stories from './Button.stories';
const { Primary, Secondary } = composeStories(stories);
test('renders primary button', () => {
render(<Primary />);
expect(screen.getByText('Click me')).toBeTruthy();
});
// Override args in tests
test('renders with custom props', () => {
render(<Primary title="Custom" />);
expect(screen.getByText('Custom')).toBeTruthy();
});
For single stories use composeStory:
import { composeStory } from '@storybook/react';
import meta, { Primary } from './Button.stories';
const PrimaryStory = composeStory(Primary, meta);
Setup global annotations for tests in a Jest setup file:
// setup-portable-stories.ts
import { setProjectAnnotations } from '@storybook/react';
import * as previewAnnotations from '../.rnstorybook/preview';
setProjectAnnotations(previewAnnotations);
| Addon | Package | Purpose |
|---|---|---|
| Controls | @storybook/addon-ondevice-controls | Edit props interactively |
| Actions | @storybook/addon-ondevice-actions | Log component interactions |
| Backgrounds | @storybook/addon-ondevice-backgrounds | Change story backgrounds |
| Notes | @storybook/addon-ondevice-notes | Add markdown documentation |