Skip to main content

Overview

The Openlane UI library uses Storybook for component development, testing, and documentation. Storybook provides an isolated environment to build and test components independently from your application.

Running Storybook

Storybook is set up in the apps/storybook directory of the monorepo.
1

Navigate to the monorepo root

cd /path/to/openlane-monorepo
2

Install dependencies

pnpm install
3

Start Storybook

cd apps/storybook
pnpm dev
Storybook will start on http://localhost:6006

Storybook Configuration

The Storybook setup uses Vite for fast builds and hot module replacement:
.storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite'

const config: StorybookConfig = {
  stories: [
    '../src/**/*.mdx',
    {
      directory: '../../../packages/ui/src',
      files: '**/*.stories.*',
    },
  ],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-a11y',
    '@storybook/addon-themes',
    '@storybook/addon-docs',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {
      builder: {
        viteConfigPath: 'vite.config.ts',
      },
    },
  },
  docs: {
    defaultName: 'Documentation',
  },
}

Installed Addons

Accessibility

@storybook/addon-a11y - Test components for accessibility issues

Theming

@storybook/addon-themes - Toggle between light and dark themes

Documentation

@storybook/addon-docs - Auto-generate component documentation

Links

@storybook/addon-links - Navigate between stories

Writing Stories

Stories are written using the Component Story Format (CSF) 3.0:

Basic Story Example

Here’s how the Button component stories are structured:
button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react-vite'
import { ArrowRight, InfoIcon } from 'lucide-react'
import { Button, buttonStyles } from './button'

type ButtonVariants = keyof typeof buttonStyles.variants.variant
type ButtonSizes = keyof typeof buttonStyles.variants.size

const variants = Object.keys(buttonStyles.variants.variant) as ButtonVariants[]
const sizes = Object.keys(buttonStyles.variants.size) as ButtonSizes[]

const meta: Meta<typeof Button> = {
  title: 'Actions/Button',
  component: Button,
  parameters: {
    docs: {
      description: {
        component: 'Displays a button with multiple variants and sizes',
      },
    },
  },
  argTypes: {
    variant: {
      description: 'Defines the theme of the button',
      control: 'select',
      options: variants,
    },
    size: {
      description: 'Defines the size of the button',
      control: 'select',
      options: sizes,
    },
    loading: { control: 'boolean' },
    iconAnimated: { control: 'boolean' },
  },
} satisfies Meta<typeof Button>

export default meta
type Story = StoryObj<typeof meta>

export const Primary: Story = {
  args: {
    children: 'Primary Button',
    variant: 'primary',
  },
}

export const Secondary: Story = {
  args: {
    children: 'Secondary Button',
    variant: 'secondary',
  },
}

export const WithIcon: Story = {
  args: {
    children: 'Continue',
    variant: 'primary',
    icon: <ArrowRight />,
    iconAnimated: true,
  },
}

export const Loading: Story = {
  args: {
    children: 'Loading...',
    loading: true,
  },
}

Story Organization

Stories are organized by category:
Actions/
├── Button
├── AlertDialog
├── Dialog
└── ConfirmationDialog

Form/
├── Input
├── Textarea
├── Select
├── Checkbox
└── Switch

Data Display/
├── Table
├── DataTable
├── Badge
└── Tag

Feedback/
├── Alert
├── Toast
└── Tooltip

Interactive Controls

Storybook provides interactive controls for component props:
argTypes: {
  children: {
    control: 'text',
    description: 'Button content',
  },
  placeholder: {
    control: 'text',
  },
}

Testing Components

Visual Testing

Use Storybook to visually test components in isolation:
  1. Test all variants: Create stories for each variant
  2. Test states: Loading, disabled, error states
  3. Test interactions: Click handlers, form submissions
  4. Test responsive behavior: Resize viewport

Accessibility Testing

The @storybook/addon-a11y addon automatically checks for accessibility issues:
1

Open any story

Navigate to a component story in Storybook
2

Check the Accessibility tab

View accessibility violations and passes in the addon panel
3

Fix violations

Address any reported accessibility issues in your component

Dark Mode Testing

Test components in both light and dark modes:
  1. Click the theme toggle in the Storybook toolbar
  2. Switch between light, dark, and side-by-side views
  3. Verify colors and contrast in both themes
.storybook/preview.tsx
import { withThemeByClassName } from '@storybook/addon-themes'

export const decorators = [
  withThemeByClassName({
    themes: {
      light: '',
      dark: 'dark',
    },
    defaultTheme: 'light',
  }),
]

Documentation

Storybook auto-generates documentation from your stories:

Component Description

const meta: Meta<typeof Button> = {
  title: 'Actions/Button',
  component: Button,
  parameters: {
    docs: {
      description: {
        component: 'A button component with multiple variants and states.',
      },
    },
  },
}

Prop Documentation

argTypes: {
  variant: {
    description: 'The visual style variant of the button',
    table: {
      type: { summary: 'primary | secondary | destructive' },
      defaultValue: { summary: 'primary' },
    },
  },
}

Creating New Stories

Follow these steps to create a new story:
1

Create the story file

Create a .stories.tsx file next to your component:
packages/ui/src/my-component/my-component.stories.tsx
2

Define the meta object

import type { Meta, StoryObj } from '@storybook/react-vite'
import { MyComponent } from './my-component'

const meta: Meta<typeof MyComponent> = {
  title: 'Category/MyComponent',
  component: MyComponent,
  argTypes: {
    // Define controls here
  },
} satisfies Meta<typeof MyComponent>

export default meta
type Story = StoryObj<typeof meta>
3

Create story variants

export const Default: Story = {
  args: {
    // Default props
  },
}

export const Variant1: Story = {
  args: {
    // Variant props
  },
}
4

Test in Storybook

Start Storybook and verify your new story appears:
pnpm dev

Best Practices

Cover all important variants and states:
export const Default: Story = { ... }
export const Primary: Story = { ... }
export const Secondary: Story = { ... }
export const Loading: Story = { ... }
export const Disabled: Story = { ... }
export const WithIcon: Story = { ... }
export const LongText: Story = { ... }
Story names should clearly describe what they demonstrate:
// Good
export const WithIconAndLoadingState: Story = { ... }

// Avoid
export const Story1: Story = { ... }
Add descriptions to help users understand the component:
parameters: {
  docs: {
    description: {
      component: 'This component is used for...',
      story: 'This story demonstrates...',
    },
  },
}
Create stories for edge cases:
export const VeryLongText: Story = {
  args: {
    children: 'Very long text that might overflow...',
  },
}

export const EmptyState: Story = {
  args: {
    data: [],
  },
}

Building Storybook

Build a static version of Storybook for deployment:
pnpm build-storybook
The static files will be generated in storybook-static/.

Available Scripts

pnpm dev
# Starts Storybook dev server on port 6006

Troubleshooting

Check that your story file matches the glob pattern in main.ts:
stories: [
  {
    directory: '../../../packages/ui/src',
    files: '**/*.stories.*',  // Must end in .stories.tsx
  },
]
Ensure styles are imported in .storybook/preview.tsx:
import '@repo/ui/styles.css'
Verify the theme addon is configured:
import { withThemeByClassName } from '@storybook/addon-themes'

export const decorators = [
  withThemeByClassName({
    themes: { light: '', dark: 'dark' },
    defaultTheme: 'light',
  }),
]

Component Examples in Storybook

Here are some example components you can explore in Storybook:

Button

10+ variants including primary, secondary, loading states

Input

Variants with icons, prefixes, suffixes

Select

Form select with multiple styles

Dialog

Modal dialogs with different layouts

Table

Sortable data tables

Toast

Toast notifications with variants

Tabs

Tabbed navigation

Badge

Status badges and labels

Switch

Toggle switches

Next Steps

Component Overview

View all available components

Usage Guide

Learn how to use components

Theming

Customize component themes

Installation

Setup the component library

Build docs developers (and LLMs) love