Loading...
Loading...
Create and edit React Native Storybook stories using Component Story Format (CSF). Use when writing .stories.tsx files, adding stories to React Native components, configuring Storybook addons (controls, actions, backgrounds, notes), setting up argTypes, decorators, parameters, or working with portable stories for testing. Applies to any task involving @storybook/react-native story authoring.
npx skill4agent add storybookjs/react-native writing-react-native-storybook-stories@storybook/react-nativeimport 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.tsxMetaStoryObj@storybook/react-nativemetasatisfies Meta<typeof Component>StoryObj<typeof meta>argsargTypesparametersrenderdecoratorsexport 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>;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>;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>;stringbooleannumberparameters: {
// 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 |
|---|---|---|
| | 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 |
| | Initial UI visibility |
| | Hide fullscreen toggle |
| | Story container layout |
const meta = {
component: MyComponent,
decorators: [
(Story) => (
<View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
<Story />
</View>
),
],
} satisfies Meta<typeof MyComponent>;.rnstorybook/preview.tsximport { 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;stories: [
'../components/**/*.stories.?(ts|tsx|js|jsx)',
{ directory: '../other_components', files: '**/*.stories.?(ts|tsx|js|jsx)' },
],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();
});composeStoryimport { composeStory } from '@storybook/react';
import meta, { Primary } from './Button.stories';
const PrimaryStory = composeStory(Primary, meta);// setup-portable-stories.ts
import { setProjectAnnotations } from '@storybook/react';
import * as previewAnnotations from '../.rnstorybook/preview';
setProjectAnnotations(previewAnnotations);| Addon | Package | Purpose |
|---|---|---|
| Controls | | Edit props interactively |
| Actions | | Log component interactions |
| Backgrounds | | Change story backgrounds |
| Notes | | Add markdown documentation |