BelajarKoding Logobelajarkoding

Platform belajar web development Indonesia. Artikel, cheat sheets, roadmap, dan code challenges untuk developer Indonesia.

Navigasi

  • Artikel
  • Cheat Sheets
  • Roadmap
  • Challenges
  • Pricing
  • Search

Produk Lain

  • JagoHermes
  • KelasClaude
  • KilatKoding
  • BelajarVibeCoding
  • JualanKoding

Support

  • Privacy Policy
  • Terms of Service
  • Email

© 2026 BelajarKoding. All rights reserved.

Galih PratamaBagian dari ekosistem Galih Pratama
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade

Daftar Isi

InstalasiSetup StorybookRun StorybookStories (CSF 3.0)Format DasarRender Function (Dynamic Stories)Args dan ArgTypesArgs: Component PropsArgTypes: Control ConfigurationDecoratorsComponent-level DecoratorsGlobal Decorators (preview.ts)ParametersConfiguration (main.ts)AddonsEssential AddonsTestingPlay Functions (Interaction Tests)Storybook Test RunnerComponent Testing dengan Vitest/JestOrganizationStory HierarchyMultiple Components per FilePage Stories (Full Pages)Composition (Microfrontends)Deploy StorybookChromatic (Visual Testing)Glossary
StorybookUITestingComponents

Storybook Cheat Sheet

Referensi cepat Storybook untuk component-driven development. Stories format, args, decorators, addons, testing, dan composition. Perfect buat developer yang bangun design system.

TypeScript9 min read1.725 kata
Silakan login atau daftar untuk membaca cheat sheet ini.

#Instalasi

#Setup Storybook

bash
# Init di project yang udah ada (React, Vue, Svelte, Angular, dll)
npx storybook@latest init
 
# Install manual
npm install storybook @storybook/react @storybook/react-vite --dev
npx storybook init

Setelah init, Storybook bakal bikin:

text
.storybook/
  main.ts          # Konfigurasi Storybook
  preview.ts       # Global decorators dan parameters
src/stories/        # Contoh stories

#Run Storybook

bash
# Dev mode (hot reload)
npm run storybook
 
# Build static storybook (untuk deploy)
npm run build-storybook
 
# Dengan custom port
npx storybook dev -p 6007
 
# CI mode (tanpa auto-open browser)
npx storybook dev --ci -p 6007

#Stories (CSF 3.0)

#Format Dasar

tsx
// src/components/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'danger', 'ghost'],
    },
    size: {
      control: 'select',
      options: ['sm', 'md', 'lg'],
    },
    disabled: { control: 'boolean' },
    onClick: { action: 'clicked' },
  },
};
 
export default meta;
type Story = StoryObj<typeof meta>;
 
// Default story
export const Primary: Story = {
  args: {
    variant: 'primary',
    size: 'md',
    children: 'Click Me',
  },
};
 
// Dengan args berbeda
export const Secondary: Story = {
  args: {
    ...Primary.args,
    variant: 'secondary',
    children: 'Secondary',
  },
};
 
export const Disabled: Story = {
  args: {
    ...Primary.args,
    disabled: true,
    children: 'Disabled',
  },
};
 
// Large size
export const Large: Story = {
  args: {
    ...Primary.args,
    size: 'lg',
    children: 'Large Button',
  },
};

#Render Function (Dynamic Stories)

tsx
export const Template: Story = {
  render: (args) => ({
    component: Button,
    props: args,
  }),
};
 
// Story dengan custom render
export const WithIcon: Story = {
  render: () => (
    <Button variant="primary">
      <svg width="16" height="16" fill="currentColor">
        <path d="M8 0a8 8 0 100 16A8 8 0 008 0z" />
      </svg>
      Save
    </Button>
  ),
};
 
// Multiple stories dari satu template
const Template: StoryFn<typeof Button> = (args) => <Button {...args} />;
 
export const Primary = Template.bind({});
Primary.args = { variant: 'primary', children: 'Primary' };
 
export const Secondary = Template.bind({});
Secondary.args = { variant: 'secondary', children: 'Secondary' };

#Args dan ArgTypes

#Args: Component Props

tsx
export const Playground: Story = {
  args: {
    variant: 'primary',
    size: 'md',
    children: 'Playground',
    disabled: false,
    loading: false,
  },
};
 
// Args composition
const baseArgs = {
  children: 'Button',
  size: 'md' as const,
};
 
export const Primary: Story = { args: { ...baseArgs, variant: 'primary' } };
export const Danger: Story = { args: { ...baseArgs, variant: 'danger' } };

#ArgTypes: Control Configuration

tsx
const meta = {
  argTypes: {
    // Text input
    label: { control: 'text' },
 
    // Number input
    count: { control: 'number', min: 0, max: 100, step: 5 },
 
    // Boolean
    show: { control: 'boolean' },
 
    // Select dropdown
    color: {
      control: 'select',
      options: ['red', 'green', 'blue', 'yellow'],
    },
 
    // Radio buttons
    layout: {
      control: 'radio',
      options: ['horizontal', 'vertical'],
    },
 
    // Inline radio
    align: {
      control: 'inline-radio',
      options: ['left', 'center', 'right'],
    },
 
    // Range slider
    opacity: {
      control: 'range',
      min: 0,
      max: 1,
      step: 0.1,
    },
 
    // Object/JSON
    style: { control: 'object' },
 
    // Date picker
    date: { control: 'date' },
 
    // Color picker
    backgroundColor: { control: 'color' },
 
    // File upload
    image: { control: 'file', accept: 'image/*' },
 
    // Custom control
    customProp: {
      control: {
        type: 'select',
        labels: { 0: 'Small', 1: 'Medium', 2: 'Large' },
      },
      options: [0, 1, 2],
      mapping: { 0: 'sm', 1: 'md', 2: 'lg' },
    },
  },
};

#Decorators

#Component-level Decorators

Wrap story dengan markup atau provider tambahan.

tsx
// Global decorator untuk semua stories
const meta = {
  decorators: [
    (Story) => (
      <div style={{ padding: '20px', background: '#f0f0f0' }}>
        <Story />
      </div>
    ),
  ],
};
 
// Per-story decorator
export const DarkMode: Story = {
  decorators: [
    (Story) => (
      <div className="dark-theme">
        <Story />
      </div>
    ),
  ],
};
 
// Provider decorator (Redux, Theme, Router, dll)
const meta = {
  decorators: [
    (Story) => (
      <ThemeProvider theme={darkTheme}>
        <BrowserRouter>
          <Story />
        </BrowserRouter>
      </ThemeProvider>
    ),
  ],
};

#Global Decorators (preview.ts)

tsx
// .storybook/preview.ts
import type { Preview } from '@storybook/react';
 
const preview: Preview = {
  decorators: [
    (Story) => (
      <div style={{ fontFamily: 'Inter, sans-serif' }}>
        <Story />
      </div>
    ),
  ],
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
    backgrounds: {
      default: 'light',
      values: [
        { name: 'light', value: '#ffffff' },
        { name: 'dark', value: '#1a1a1a' },
        { name: 'brand', value: '#6366f1' },
      ],
    },
  },
};
 
export default preview;

#Parameters

tsx
// Per-story parameters
export const Canvas: Story = {
  parameters: {
    layout: 'fullscreen',
    backgrounds: { default: 'dark' },
    viewport: { defaultViewport: 'mobile1' },
  },
};
 
// Common parameters
const meta = {
  parameters: {
    layout: 'centered',     // 'centered' | 'fullscreen' | 'padded'
    docs: {
      description: {
        component: 'Reusable button component with variants.',
      },
    },
    design: {
      type: 'figma',
      url: 'https://www.figma.com/file/abc123/Button',
    },
  },
};

#Configuration (main.ts)

typescript
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
 
const config: StorybookConfig = {
  stories: [
    '../src/**/*.mdx',
    '../src/**/*.stories.@(ts|tsx)',
  ],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/addon-a11y',
    '@storybook/addon-coverage',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',     // 'tag' | true | false
    defaultName: 'Documentation',
  },
  staticDirs: ['../public'],
  typescript: {
    check: false,
    reactDocgen: 'react-docgen-typescript',
  },
  viteFinal: async (config) => {
    // Custom Vite config
    config.resolve.alias = {
      ...config.resolve.alias,
      '@': '/src',
    };
    return config;
  },
};
 
export default config;

#Addons

#Essential Addons

tsx
// Actions (event logging)
import type { Meta } from '@storybook/react';
 
const meta: Meta = {
  parameters: {
    actions: {
      argTypesRegex: '^on[A-Z].*',  // auto-detect onClick, onChange, dll
    },
  },
};
 
// Manual action
import { action } from '@storybook/addon-actions';
 
export const Demo: Story = {
  args: {
    onClick: action('button-clicked'),
    onChange: action('text-changed'),
  },
};
 
// Viewport addon (responsive testing)
const meta = {
  parameters: {
    viewport: {
      viewports: {
        iPhone: { name: 'iPhone', styles: { width: '375px', height: '812px' } },
        iPad: { name: 'iPad', styles: { width: '768px', height: '1024px' } },
        desktop: { name: 'Desktop', styles: { width: '1920px', height: '1080px' } },
      },
      defaultViewport: 'iPhone',
    },
  },
};
 
// Measure & Outline (CSS debugging)
// Aktif otomatis dengan addon-essentials
// Tampil di toolbar addon
 
// Accessibility (a11y) testing
const meta = {
  parameters: {
    a11y: {
      element: '#storybook-root',
      config: {
        rules: [
          { id: 'color-contrast', enabled: true },
        ],
      },
      options: {},
      manual: false,
    },
  },
};

#Testing

#Play Functions (Interaction Tests)

tsx
import { expect } from '@storybook/test';
import { within, userEvent } from '@storybook/test';
 
export const LoginForm: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
 
    // Interact dengan component
    await userEvent.type(canvas.getByPlaceholderText('Email'), 'test@test.com');
    await userEvent.type(canvas.getByPlaceholderText('Password'), 'password123');
    await userEvent.click(canvas.getByRole('button', { name: 'Login' }));
 
    // Assert
    await expect(canvas.getByText('Welcome!')).toBeVisible();
  },
};
 
// Async play dengan delay
export const WithDelay: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    await userEvent.click(canvas.getByText('Load'));
    await new Promise(r => setTimeout(r, 500));
    await expect(canvas.getByText('Loaded')).toBeInTheDocument();
  },
};

#Storybook Test Runner

bash
# Install
npm install @storybook/test-runner --dev
 
# Run tests (headless browser)
npm run test-storybook
 
# Dengan specific stories
npx test-storybook --url "http://localhost:6007" \
  --storyFilter "Components/Button/*"
 
# Watch mode
npx test-storybook --watch
 
# Generate coverage
npx test-storybook --coverage

#Component Testing dengan Vitest/Jest

tsx
// Import story sebagai test case
import { composeStories } from '@storybook/react';
import { render, screen } from '@testing-library/react';
import * as stories from './Button.stories';
 
const { Primary, Disabled } = composeStories(stories);
 
test('renders primary button', () => {
  render(<Primary />);
  expect(screen.getByText('Click Me')).toBeInTheDocument();
});
 
test('disabled button is disabled', () => {
  render(<Disabled />);
  expect(screen.getByRole('button')).toBeDisabled();
});
 
// Run play functions
test('runs play function', async () => {
  const { Primary } = composeStories(stories);
  const { container } = render(<Primary />);
  await Primary.play?.({ canvasElement: container });
});

#Organization

#Story Hierarchy

tsx
// Title format dengan / untuk grouping
const meta = {
  title: 'Design System/Forms/Input',    // Group > Subgroup > Component
  component: Input,
};
 
// Atau dot notation
const meta = {
  title: 'Components.Input',  // Flat hierarchy dengan dot separator
  component: Input,
};

#Multiple Components per File

tsx
// Group related components
export default {
  title: 'Components/Form Controls',
} as Meta;
 
export const TextInput = {
  render: () => <TextInput />,
};
 
export const Select = {
  render: () => <Select />,
};
 
export const Checkbox = {
  render: () => <Checkbox />,
};

#Page Stories (Full Pages)

tsx
export default {
  title: 'Pages/Login',
  component: LoginPage,
  parameters: {
    layout: 'fullscreen',
    viewport: { defaultViewport: 'desktop' },
  },
} as Meta<typeof LoginPage>;
 
export const Default: Story = {};
 
export const WithError: Story = {
  args: {
    error: 'Invalid credentials',
  },
};

#Composition (Microfrontends)

typescript
// .storybook/main.ts
// Combine multiple Storybooks dari berbagai repo
const config = {
  refs: {
    designSystem: {
      title: 'Design System',
      url: 'https://design-system.example.com/storybook',
    },
    adminPanel: {
      title: 'Admin Panel',
      url: 'https://admin.example.com/storybook',
    },
  },
};

#Deploy Storybook

bash
# Build static files
npm run build-storybook
# Output: ./storybook-static/
 
# Deploy ke Vercel
npx vercel ./storybook-static --prod
 
# Deploy ke Netlify
npx netlify deploy --dir=./storybook-static --prod
 
# Deploy ke Chromatic (visual regression)
npx chromatic --project-token=YOUR_TOKEN
 
# Deploy ke GitHub Pages
# .github/workflows/storybook.yml

#Chromatic (Visual Testing)

bash
# Install
npm install chromatic --dev
 
# Publish storybook + run visual tests
npx chromatic
 
# CI integration
# GitHub Actions:
# - name: Publish to Chromatic
#   run: npx chromatic --project-token=${{ secrets.CHROMATIC_TOKEN }}

#Glossary

  • Story: Snapshot dari component dalam state tertentu. Unit terkecil di Storybook.
  • CSF (Component Story Format): Format standar nulis stories sebagai ES modules. Versi 3 adalah terbaru.
  • Args: Input props untuk component dalam story. Bisa di-edit di UI Storybook.
  • ArgTypes: Metadata tentang args: tipe control, options, validasi.
  • Decorator: Wrapper yang bungkus story dengan context tambahan (provider, layout, theme).
  • Play Function: Function yang otomatis interact dengan component untuk testing. Pakai Testing Library.
  • Addon: Plugin yang nambah fungsionalitas Storybook (a11y, coverage, viewport, dll).
  • Canvas: Area render di mana component live. Interaktif.
  • Docs: Halaman dokumentasi auto-generated dari stories dan component source.
  • Composition: Gabung multiple Storybook dari berbagai project jadi satu unified UI.
  • Visual Regression Testing: Bandingkan screenshot component dari commit ke commit. Chromatic, Percy.
  • Coverage: Metric berapa banyak component/code yang tercakup oleh stories.

Baca Cheat Sheet Lengkap

Login atau daftar akun gratis untuk membaca cheat sheet ini.

LoginDaftar Gratis
Share: