Skip to main content
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:
argTypes: {
  fruit: {
    options: ['apple', 'banana', 'cherry'] as const,
    control: {
      type: 'select',
      labels: {
        apple: 'Apple',
        banana: 'Banana',
        cherry: 'Cherry',
      } as const,
    },
  },
}

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

  1. One component per file: Keep each story file focused on a single component
  2. Use TypeScript: Leverage Meta and StoryObj types for type safety
  3. Meaningful story names: Use descriptive names like Primary, Loading, Disabled instead of Story1, Story2
  4. Document with parameters: Add notes and documentation using parameters
  5. Reuse args: Define common args at the meta level to avoid repetition
  6. 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

Build docs developers (and LLMs) love