Skip to content

Storybook Guidelines

This document provides comprehensive guidelines for creating well-structured, consistent, and helpful Storybook stories for UI components.

Table of Contents

Story Structure

Every Storybook story file should follow this consistent structure:

import React from 'react';
import type { Meta, StoryObj } from '@storybook/nextjs';
// Import required icons from lucide-react
// Import component from relative path

import { YourComponent } from './YourComponent';

const meta: Meta<typeof YourComponent> = {
  title: 'Components/YourComponent', // No category subfolder in title
  component: YourComponent,
  parameters: {
    layout: 'padded', // or 'centered', 'fullscreen'
    docs: {
      description: {
        component: `Brief description of the component.

## Features

- **Feature 1**: Description of the feature
- **Feature 2**: Description of the feature
- **Feature 3**: Description of the feature
// ... 8-12 features total

## Usage

\`\`\`tsx
import { YourComponent } from '@/components/ui/YourComponent';

// Basic usage example
<YourComponent
  prop1="value"
  prop2={true}
/>

// Advanced usage example
<YourComponent
  prop1="value"
  prop2={true}
  onAction={(data) => console.log(data)}
>
  {children}
</YourComponent>
\`\`\``,
      },
    },
  },
  tags: ['autodocs'],
  argTypes: {
    // Define controls for interactive props
    prop1: {
      control: 'text',
      description: 'Description of prop1',
      table: {
        type: { summary: 'string' },
        defaultValue: { summary: 'default' },
      },
    },
    // Hide non-essential props from controls
    internalProp: { table: { disable: true } },
  },
};

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

// Define your stories
export const Default: Story = {
  args: {
    // Default props
  },
};

Documentation Pattern

Component Description

The component description should include:

  1. Brief Overview (1-2 sentences)
  2. What the component does
  3. When to use it

  4. Features Section

  5. 8-12 bullet points
  6. Each feature should be bold with a colon
  7. Cover:

    • Core functionality
    • Visual variants
    • Interaction patterns
    • Accessibility features
    • Integration capabilities
    • Customization options
  8. Usage Section

  9. Import statement
  10. Basic usage example
  11. Common variations
  12. Advanced usage (if applicable)
  13. Integration examples (for complex components)

Example Documentation

description: {
  component: `A flexible button component that supports multiple variants, sizes, and states. Use it for triggering actions and navigation throughout your application.

## Features

- **Multiple Variants**: Default, destructive, outline, secondary, ghost, and link styles
- **Flexible Sizing**: Small, default, and large sizes for different contexts
- **Loading State**: Built-in loading spinner with disabled interaction
- **Icon Support**: Leading and trailing icons with automatic spacing
- **Full Accessibility**: ARIA attributes, keyboard navigation, and focus management
- **Asynchronous Actions**: Handles async onClick handlers with loading state
- **Link Support**: Can render as anchor tag with href prop
- **Custom Styling**: Accepts className for additional customization
- **Disabled State**: Visual and interaction feedback for disabled buttons
- **Keyboard Shortcuts**: Support for keyboard shortcuts with tooltips
- **Animation Effects**: Smooth transitions and hover effects
- **Theme Aware**: Automatically adapts to light/dark themes

## Usage

\`\`\`tsx
import { Button } from '@/components/ui/Button';
import { SendIcon } from 'lucide-react';

// Basic button
<Button onClick={() => console.log('clicked')}>
  Click me
</Button>

// Button with icon
<Button>
  <SendIcon className="mr-2 h-4 w-4" />
  Send Message
</Button>

// Loading state
<Button loading>
  Processing...
</Button>

// As a link
<Button asChild>
  <a href="/dashboard">Go to Dashboard</a>
</Button>
\`\`\``,
}

Best Practices

1. Story Naming

  • Use descriptive names that indicate the story's purpose
  • Common story names:
  • Default - Basic component usage
  • WithIcons - Component with icon examples
  • AllVariants - Shows all visual variants
  • Interactive - Demonstrates interactions
  • Controlled - Controlled component example
  • WithValidation - Form components with validation
  • Loading - Loading states
  • Error - Error states
  • Disabled - Disabled states

2. ArgTypes Configuration

argTypes: {
  // Text controls
  label: {
    control: 'text',
    description: 'Button label text',
  },

  // Select controls
  variant: {
    control: 'select',
    options: ['default', 'destructive', 'outline'],
    description: 'Visual style variant',
  },

  // Boolean controls
  disabled: {
    control: 'boolean',
    description: 'Disables the button',
  },

  // Action logging
  onClick: {
    action: 'clicked',
    description: 'Called when button is clicked',
  },

  // Hide internal props
  internalState: {
    table: { disable: true },
  },
}

3. Parameters

parameters: {
  // Layout options
  layout: 'centered', // 'padded' | 'fullscreen'

  // Viewport configuration
  viewport: {
    defaultViewport: 'mobile1',
  },

  // Backgrounds
  backgrounds: {
    default: 'light',
  },

  // Story-specific docs
  docs: {
    description: {
      story: 'This story demonstrates...',
    },
  },
}

4. Decorators

Use decorators for:

  • Providing context/providers
  • Adding layout wrappers
  • Setting up mock data
decorators: [
  (Story) => (
    <div className="min-h-[400px] flex items-center justify-center">
      <Story />
    </div>
  ),
]

Code Examples

Simple Component Story

// Badge.stories.tsx
const meta: Meta<typeof Badge> = {
  title: 'Components/Badge',
  component: Badge,
  parameters: {
    layout: 'centered',
    docs: {
      description: {
        component: `Badges are used to highlight important information. They provide visual indicators for status, counts, or categories.

## Features

- **Multiple Variants**: Default, secondary, destructive, and outline styles
- **Flexible Content**: Can contain text, numbers, or icons
- **Accessible**: Proper ARIA attributes for screen readers
- **Theme Integration**: Adapts to light/dark themes automatically

## Usage

\`\`\`tsx
import { Badge } from '@/components/ui/Badge';

// Basic usage
<Badge>New</Badge>

// With variants
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="outline">Outline</Badge>
\`\`\``,
      },
    },
  },
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['default', 'secondary', 'destructive', 'outline'],
    },
    children: {
      control: 'text',
    },
  },
};

export const Default: Story = {
  args: {
    children: 'Badge',
  },
};

export const AllVariants: Story = {
  render: () => (
    <div className="flex gap-2">
      <Badge>Default</Badge>
      <Badge variant="secondary">Secondary</Badge>
      <Badge variant="destructive">Destructive</Badge>
      <Badge variant="outline">Outline</Badge>
    </div>
  ),
};

Complex Component Story

// DataTable.stories.tsx
const meta: Meta<typeof DataTable<User>> = {
  title: 'Components/DataTable',
  component: DataTable,
  parameters: {
    layout: 'padded',
    docs: {
      description: {
        component: `Comprehensive data table with sorting, filtering...`,
      },
    },
  },
  tags: ['autodocs'],
  argTypes: {
    // Complex type documentation
    columns: {
      description: 'Column definitions',
      table: {
        type: {
          summary: 'ColumnDef<TData>[]',
          detail: `{
  id?: string,
  header?: string,
  accessorKey?: keyof TData,
  // ... full type definition
}`,
        },
      },
    },
  },
};

// Interactive story with state
export const Interactive: Story = {
  render: function Render(args) {
    const [data, setData] = useState(initialData);

    return (
      <DataTable
        {...args}
        data={data}
        onUpdate={(newData) => setData(newData)}
      />
    );
  },
};

Common Patterns

1. Render Functions for State

Use render functions when stories need internal state:

export const Controlled: Story = {
  render: function Render() {
    const [value, setValue] = useState('');

    return (
      <Input
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
    );
  },
};

2. Multiple Examples in One Story

export const Sizes: Story = {
  render: () => (
    <div className="space-y-4">
      <Button size="sm">Small</Button>
      <Button>Default</Button>
      <Button size="lg">Large</Button>
    </div>
  ),
  parameters: {
    docs: {
      description: {
        story: 'Available button sizes for different UI contexts.',
      },
    },
  },
};

3. With Mock Data

const mockUsers = [
  { id: 1, name: 'John Doe', email: 'john@example.com' },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com' },
];

export const WithData: Story = {
  args: {
    data: mockUsers,
  },
};

4. Async Actions

export const AsyncAction: Story = {
  args: {
    onClick: async () => {
      await sleep(2000);
      console.log('Action completed');
    },
  },
};

Current Project Conventions

Based on the existing stories in this project:

Import Pattern

import React from 'react'; // Always import React
import type { Meta, StoryObj } from '@storybook/nextjs';
import { IconName } from 'lucide-react'; // Icons from lucide-react

import { Component } from './Component'; // Relative imports

Title Convention

  • Use flat hierarchy: 'Components/ComponentName'
  • Don't use nested categories in the title

Layout Options

  • 'centered' - For small, focused components (buttons, badges, inputs)
  • 'padded' - For larger components that need space (cards, tables, alerts)
  • 'fullscreen' - For full-page layouts

Documentation Style

Each component description should follow this exact format:

  1. Opening sentence describing what the component does
  2. Optional second sentence about when to use it
  3. Features section with 4-12 bullet points
  4. Usage section with practical code examples
  5. Optional Accessibility section for complex components

Story Naming Patterns

  • Default - Basic component with minimal props
  • Secondary, Destructive, Outline - Variant examples
  • Small, Large - Size variations
  • Disabled - Disabled state
  • Loading - Loading state
  • AllVariants - Shows all variants in one story
  • AllSizes - Shows all sizes in one story
  • WithIcon - Component with icon examples
  • Interactive - Demonstrates user interactions

Farm Cove Specific

Some stories include a FarmCoveThemeShowcase story that demonstrates:

  • Brand colors (Primary Teal, Success, Warning, etc.)
  • Farm Cove specific UI patterns
  • Film production context examples
  • WCAG compliance notes

Summary

Good Storybook stories should:

  1. Be Comprehensive: Cover all major use cases and variants
  2. Be Documented: Include features list and usage examples
  3. Be Interactive: Use controls for props that users might want to adjust
  4. Be Realistic: Use realistic mock data and scenarios
  5. Be Organized: Follow consistent naming and structure
  6. Be Helpful: Include code examples that developers can copy
  7. Be Accessible: Demonstrate accessibility features
  8. Be Visual: Show all visual states and variants

Remember: Storybook stories are both documentation and development tools. They should help developers understand how to use components and serve as a visual regression testing tool.