Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/satsigner/satsigner/llms.txt

Use this file to discover all available pages before exploring further.

Overview

SatSigner integrates Storybook for React Native, enabling isolated component development, visual testing, and interactive documentation. Storybook runs directly in the mobile app, allowing you to develop and test components on real devices and simulators.

What is Storybook?

Storybook is a development environment for UI components. It allows you to:
  • Develop components in isolation from the main app
  • Visualize different component states without complex navigation
  • Test components interactively with on-device controls
  • Document components with examples and usage notes
  • Speed up development by focusing on one component at a time

Installation

Storybook dependencies are included in the project. From the repository root:
# Install dependencies
yarn install

# Navigate to mobile app
cd apps/mobile

Storybook Configuration

Main Configuration

Configuration is located in apps/mobile/.storybook/main.ts:
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-actions',
    '@storybook/addon-ondevice-backgrounds',
    '@storybook/addon-ondevice-notes'
  ]
}

export default main

Story Location

Stories are automatically discovered in:
  • apps/mobile/components/**/*.stories.ts
  • apps/mobile/components/**/*.stories.tsx
  • apps/mobile/components/**/*.stories.js
  • apps/mobile/components/**/*.stories.jsx

Available Addons

Storybook includes four on-device addons:

1. Controls Addon

Interactively edit component props in real-time:
  • Adjust numbers with sliders
  • Toggle booleans
  • Edit text inputs
  • Select from options

2. Actions Addon

Log and inspect component event handlers:
  • See when callbacks fire
  • View event payloads
  • Debug user interactions

3. Backgrounds Addon

Test components against different backgrounds:
  • Light/dark themes
  • Custom colors
  • Transparent backgrounds

4. Notes Addon

Add markdown documentation to stories:
  • Usage instructions
  • Design guidelines
  • Component specifications

Preview Configuration

Global story settings in apps/mobile/.storybook/preview.tsx:
import type { Preview } from '@storybook/react'

const preview: Preview = {
  parameters: {
    layout: 'centered',
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/
      }
    }
  }
}

export default preview

Storage Configuration

Storybook state persists using AsyncStorage (apps/mobile/.storybook/index.tsx):
import AsyncStorage from '@react-native-async-storage/async-storage'
import { view } from './storybook.requires'

const StorybookUIRoot = view.getStorybookUI({
  storage: {
    getItem: AsyncStorage.getItem,
    setItem: AsyncStorage.setItem
  }
})

export default StorybookUIRoot
This preserves:
  • Selected story
  • Addon panel state
  • Control values

Running Storybook

Android

Build and run with Storybook enabled:
cd apps/mobile
yarn sb:android
This sets EXPO_PUBLIC_STORYBOOK_ENABLED=true and launches the app with Storybook UI.

iOS

Build and run with Storybook enabled:
cd apps/mobile
yarn sb:ios

Alternative: Environment Variable

Manually enable Storybook:
export EXPO_PUBLIC_STORYBOOK_ENABLED=true
yarn android
# or
yarn ios

Switching Between App and Storybook

Storybook mode is controlled by the EXPO_PUBLIC_STORYBOOK_ENABLED environment variable:
  • Enabled: App shows Storybook UI
  • Disabled/Unset: App runs normally
Restart the app to switch modes.

Writing Stories

Basic Story Structure

Create a file next to your component: MyButton.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { MyButton } from './MyButton'

const meta = {
  title: 'Components/MyButton',
  component: MyButton,
  argTypes: {
    label: {
      control: 'text',
      description: 'Button text label'
    },
    onPress: { action: 'pressed' },
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'danger']
    },
    disabled: {
      control: 'boolean'
    }
  }
} satisfies Meta<typeof MyButton>

export default meta

type Story = StoryObj<typeof meta>

export const Primary: Story = {
  args: {
    label: 'Press Me',
    variant: 'primary',
    disabled: false
  }
}

export const Secondary: Story = {
  args: {
    label: 'Cancel',
    variant: 'secondary'
  }
}

export const Disabled: Story = {
  args: {
    label: 'Disabled',
    disabled: true
  }
}

Story Naming Convention

  • File: ComponentName.stories.tsx next to component file
  • Title: Use / for categories: 'Components/Button', 'Forms/Input'
  • Exports: Each named export is a story variant

Control Types

Available control types for props:
argTypes: {
  // Text input
  text: { control: 'text' },
  
  // Number slider
  count: { 
    control: { type: 'range', min: 0, max: 100, step: 1 }
  },
  
  // Boolean toggle
  enabled: { control: 'boolean' },
  
  // Select dropdown
  size: {
    control: 'select',
    options: ['small', 'medium', 'large']
  },
  
  // Radio buttons
  theme: {
    control: 'radio',
    options: ['light', 'dark']
  },
  
  // Color picker
  backgroundColor: { control: 'color' },
  
  // Date picker
  date: { control: 'date' },
  
  // Action logger
  onPress: { action: 'pressed' }
}

Adding Documentation

Include notes with your stories:
export const Example: Story = {
  args: {
    label: 'Example'
  },
  parameters: {
    notes: `
## Usage

This button should be used for primary actions.

### Guidelines
- Use clear, action-oriented labels
- Limit to one primary button per screen
- Disable during async operations
    `
  }
}

Development Workflow

Typical Workflow

  1. Create component file: components/MyComponent.tsx
  2. Create story file: components/MyComponent.stories.tsx
  3. Run Storybook: yarn sb:ios or yarn sb:android
  4. Develop in isolation:
    • View component in Storybook
    • Adjust props with controls
    • Test different states
    • Add more story variants
  5. Generate stories: yarn storybook-generate (updates story index)
  6. Integrate into app: Use component in actual screens

Story Generation

After adding or modifying stories, regenerate the story index:
yarn storybook-generate
This command (sb-rn-get-stories) scans for story files and updates the Storybook registry.

Hot Reloading

Changes to components and stories hot reload automatically:
  • Edit component code
  • Save file
  • Storybook refreshes immediately
  • No need to rebuild

Best Practices

1. One Component Per Story File

Each component should have its own .stories.tsx file:
components/
  Button/
    Button.tsx
    Button.stories.tsx
  Input/
    Input.tsx
    Input.stories.tsx

2. Cover All States

Create stories for every important state:
export const Default: Story = { ... }
export const Loading: Story = { ... }
export const Error: Story = { ... }
export const Empty: Story = { ... }
export const Disabled: Story = { ... }

3. Use Descriptive Titles

Organize stories with clear hierarchies:
// Good
title: 'Wallet/Transaction/TransactionCard'

// Avoid
title: 'TransactionCard'

4. Document Complex Props

Add descriptions to argTypes:
argTypes: {
  onConfirm: {
    action: 'confirmed',
    description: 'Callback fired when transaction is confirmed'
  },
  amount: {
    control: 'number',
    description: 'Transaction amount in satoshis'
  }
}

5. Mock Dependencies

Isolate components by mocking external dependencies:
import { mockWalletService } from '@/tests/mocks'

export const WithBalance: Story = {
  args: {
    walletService: mockWalletService({ balance: 100000 })
  }
}

Troubleshooting

Storybook Won’t Launch

Verify environment variable is set:
echo $EXPO_PUBLIC_STORYBOOK_ENABLED
# Should output: true
If not set, use the sb:android or sb:ios scripts.

Stories Not Appearing

Regenerate story index:
yarn storybook-generate
Verify story files match the pattern in main.ts:
  • Located in components/ directory
  • Named *.stories.tsx or *.stories.ts

Controls Not Working

Ensure controls addon is configured:
  1. Check addons array in .storybook/main.ts
  2. Verify @storybook/addon-ondevice-controls is listed
  3. Check argTypes are properly defined

App Crashes in Storybook Mode

Component may depend on app context:
  1. Wrap story in required providers:
    import { ThemeProvider } from '@/providers'
    
    export const Example: Story = {
      decorators: [
        (Story) => (
          <ThemeProvider>
            <Story />
          </ThemeProvider>
        )
      ]
    }
    
  2. Mock required contexts or services

Changes Not Reflecting

  1. Clear Metro cache: yarn start --reset-cache
  2. Regenerate stories: yarn storybook-generate
  3. Rebuild app: yarn sb:android or yarn sb:ios

Available Scripts

From apps/mobile/package.json:
{
  "sb:android": "cross-env EXPO_PUBLIC_STORYBOOK_ENABLED=true expo run:android",
  "sb:ios": "cross-env EXPO_PUBLIC_STORYBOOK_ENABLED=true expo run:ios",
  "storybook-generate": "sb-rn-get-stories"
}

Dependencies

Storybook packages in devDependencies:
{
  "@storybook/addon-ondevice-actions": "8.6.2",
  "@storybook/addon-ondevice-backgrounds": "^8.6.2",
  "@storybook/addon-ondevice-controls": "8.6.2",
  "@storybook/addon-ondevice-notes": "^8.6.2",
  "@storybook/manager-api": "^8.6.10",
  "@storybook/react-native": "8.6.2",
  "@storybook/theming": "^8.6.10",
  "@react-native-async-storage/async-storage": "^2.2.0"
}

Next Steps

Additional Resources