Stories are the fundamental building blocks of Storybook. They capture the different states of your React Native components, making them easy to develop, test, and document in isolation.
Component Story Format (CSF)
Storybook for React Native uses Component Story Format (CSF), a standard format for writing component examples. CSF is a modern, ESM-based standard that provides excellent TypeScript support and makes stories portable across different testing tools.
Basic Story Structure
Every story file exports a default export (meta) and named exports (stories). Here’s a simple example:
import type { Meta , StoryObj } from '@storybook/react-native' ;
import { MyButton } from './Button' ;
const meta = {
component: MyButton ,
} satisfies Meta < typeof MyButton >;
export default meta ;
type Story = StoryObj < typeof meta >;
export const Basic : Story = {
args: {
text: 'Hello World' ,
color: 'purple' ,
},
};
TypeScript Support
Storybook provides full TypeScript support through the Meta and StoryObj types:
import type { Meta , StoryObj } from '@storybook/react-native' ;
import { ActionButton } from './Actions' ;
const meta = {
component: ActionButton ,
parameters: {
notes: `
# Button
This is a button component.
You use it like this:
\`\`\` tsx
<Button
text="Press me!"
onPress={() => console.log('pressed')}
/>
\`\`\`
` ,
},
} satisfies Meta < typeof ActionButton >;
export default meta ;
The satisfies keyword ensures your meta configuration is type-safe while preserving type inference for the component props.
Working with Args
Args are inputs to your component that Storybook uses to render it. They make stories dynamic and interactive.
Defining Args
You can define args at the meta level (shared across all stories) or at the story level:
import type { Meta , StoryObj } from '@storybook/react-native' ;
import { fn } from 'storybook/test' ;
import { Button } from './Button' ;
const meta = {
component: Button ,
args: {
onPress: fn (), // Shared across all stories
},
} satisfies Meta < typeof Button >;
export default meta ;
type Story = StoryObj < typeof meta >;
export const Primary : Story = {
args: {
title: 'Sign In' ,
},
};
export const Secondary : Story = {
args: {
title: 'Create Account' ,
variant: 'secondary' ,
},
};
export const Loading : Story = {
args: {
title: 'Sign In' ,
loading: true ,
},
};
export const Disabled : Story = {
args: {
title: 'Sign In' ,
disabled: true ,
},
};
Complex Args
Args support complex data types including objects, arrays, and dates:
import { Meta , StoryObj } from '@storybook/react-native' ;
import { ControlExample } from './ControlExample' ;
const meta = {
component: ControlExample ,
args: {
name: 'Storyteller' ,
age: 70 ,
fruit: 'apple' ,
dollars: 12.5 ,
backgroundColor: '#eaeaea' ,
items: [ 'Laptop' , 'Book' , 'Whiskey' ],
customStyles: {
borderWidth: 3 ,
borderColor: '#000' ,
padding: 10 ,
},
nice: true ,
birthday: new Date ( 2017 , 0 , 20 ),
},
} satisfies Meta < typeof ControlExample >;
export default meta ;
type ControlExampleStory = StoryObj < typeof meta >;
export const Example : ControlExampleStory = {};
ArgTypes
ArgTypes allow you to customize how args are displayed and controlled in the Controls addon. They also provide validation and constraints.
Control Types
You can specify different control types for different arg types:
Select
Radio
Range
Color/Date
Complex
argTypes : {
fruit : {
options : [ 'apple' , 'banana' , 'cherry' ] as const ,
control : {
type : 'select' ,
labels : {
apple : 'Apple' ,
banana : 'Banana' ,
cherry : 'Cherry' ,
} as const ,
},
},
}
argTypes : {
otherFruit : {
options : [ 'kiwi' , 'guava' , 'watermelon' ] as const ,
control : {
type : 'radio' ,
labels : {
kiwi : 'Kiwi' ,
guava : 'Guava' ,
watermelon : 'Watermelon' ,
} as const ,
},
},
}
argTypes : {
age : {
step : 5 ,
min : 0 ,
max : 90 ,
range : true ,
},
dollars : {
min : 0 ,
max : 100 ,
},
}
argTypes : {
backgroundColor : {
control : { type : 'color' },
},
birthday : {
control : { type : 'date' },
},
}
argTypes : {
items : {
control : {
type : 'array' ,
},
},
customStyles : {
control : { type : 'object' },
},
}
Story Configuration
Configuring Stories Path
You define which files Storybook should load in your main.ts configuration:
// .rnstorybook/main.ts
import type { StorybookConfig } from '@storybook/react-native' ;
const main : StorybookConfig = {
stories: [
'../components/**/*.stories.?(ts|tsx|js|jsx)' ,
'../other_components/**/*.stories.?(ts|tsx|js|jsx)' ,
{
directory: '../../../packages/react-native-ui' ,
titlePrefix: 'react-native-ui' ,
files: '**/*.stories.?(ts|tsx|js|jsx)' ,
},
],
addons: [
'@storybook/addon-ondevice-controls' ,
'@storybook/addon-ondevice-actions' ,
'@storybook/addon-ondevice-notes' ,
],
framework: '@storybook/react-native' ,
};
export default main ;
The stories array accepts both glob patterns as strings and configuration objects with a directory, files, and optional titlePrefix.
Story Title and Organization
By default, story titles are inferred from the file path. You can customize this with the title property:
import type { Meta , StoryObj } from '@storybook/react-native' ;
import { ChatMessage } from './ChatComponents' ;
const meta = {
title: 'Examples/Chat/Message' , // Custom hierarchical title
component: ChatMessage ,
argTypes: {
isOwn: { control: 'boolean' },
showAvatar: { control: 'boolean' },
status: {
control: 'select' ,
options: [ 'sent' , 'delivered' , 'read' ],
},
},
} satisfies Meta < typeof ChatMessage >;
export default meta ;
type Story = StoryObj < typeof meta >;
export const MessageFirst : Story = {
args: {
message: 'Hey! How are you doing today? 👋' ,
senderName: 'Sarah Chen' ,
isOwn: false ,
timestamp: '2:34 PM' ,
showAvatar: true ,
},
};
export const MessageSecond : Story = {
args: {
message: "I'm doing great, thanks for asking!" ,
senderName: 'Me' ,
isOwn: true ,
timestamp: '2:35 PM' ,
status: 'read' ,
},
};
Mock Functions
Use the fn() function from storybook/test to create mock callbacks that can be tracked by the Actions addon:
import type { Meta , StoryObj } from '@storybook/react-native' ;
import { fn } from 'storybook/test' ;
import { ActionButton } from './Actions' ;
const meta = {
component: ActionButton ,
} satisfies Meta < typeof ActionButton >;
export default meta ;
type Story = StoryObj < typeof meta >;
export const Basic : Story = {
args: {
text: 'Press me!' ,
onPress: fn (), // Creates a trackable mock function
},
};
The fn() function automatically logs interactions to the Actions panel, making it easy to debug component behavior.
Best Practices
One component per file : Keep each story file focused on a single component
Use TypeScript : Leverage Meta and StoryObj types for type safety
Meaningful story names : Use descriptive names like Primary, Loading, Disabled instead of Story1, Story2
Document with parameters : Add notes and documentation using parameters
Reuse args : Define common args at the meta level to avoid repetition
Use fn() for callbacks : Always mock event handlers with fn() for better debugging
Next Steps
Decorators & Parameters Learn how to wrap stories and configure their behavior
Addons Enhance your stories with the addon ecosystem