Claude Code से Storybook विकास: UI कैटलॉग, टेस्ट और CI
Claude Code से Storybook सेटअप, typed stories, interaction tests, CI और Chromatic जैसी visual review बनाएं।
Storybook सिर्फ स्क्रीनशॉट गैलरी नहीं, UI contract है
Storybook UI components को मुख्य app से अलग करके develop और review करने का तरीका देता है। शुरुआती लोगों के लिए यह button, form या card को अकेले खोलने वाला component catalog है। लेकिन real project में इसका काम ज्यादा महत्वपूर्ण होता है: props, states, interactions, accessibility, design review और CI checks को एक जगह दर्ज करना।
अगर आप Claude Code से सिर्फ “Storybook जोड़ दो” कहेंगे, तो वह कुछ Button.stories.tsx files बनाकर रुक सकता है। उपयोगी setup के लिए scope साफ होना चाहिए: कौन से components शामिल होंगे, कौन से states अनिवार्य हैं, play function क्या verify करेगा, और PR में Chromatic या वैसी ही visual regression check कैसे चलेगी।
यह guide React + TypeScript + Vite पर आधारित है और Storybook 8/9+ में modern लगने वाली writing style इस्तेमाल करती है: .storybook/main.ts, CSF में Meta/StoryObj, satisfies, @storybook/test interaction tests, @storybook/addon-vitest, और Chromatic-ready CI। आधिकारिक references हैं main configuration, TypeScript stories, interaction testing, Vitest addon, और Chromatic for Storybook।
अगर आप design system भी बना रहे हैं, तो Claude Code design system और Claude Code accessibility भी देखें। Storybook तभी ताकतवर बनता है जब वह सिर्फ UI दिखाने के बजाय design और user flow की quality को बचाता है।
पहले इन 3 practical cases को जोड़ें
सारी UI को एक साथ Storybook में डालने की जरूरत नहीं है। पहले उन हिस्सों पर ध्यान दें जो बार-बार बदलते हैं और signup, purchase, inquiry या content conversion के करीब हैं।
| Case | Storybook में states | Claude Code को देने वाला काम |
|---|---|---|
| Design system Button | primary, secondary, loading, disabled, सभी sizes | Props types, stories, controls, autodocs |
| Free signup form | empty, invalid input, submitting, success | play, mocks, validation checks |
| Article या product CTA | default, highlighted, mobile, long copy | Responsive check, visual diff, internal links |
इन तीन examples से designer states review कर सकता है, developer props contract समझ सकता है, और site owner revenue-related CTA टूटने से पहले देख सकता है।
Vite based setup रखें
Storybook के Vitest addon के लिए Vite based framework सबसे साफ रास्ता है। React + Vite में @storybook/react-vite इस्तेमाल करें; Next.js में @storybook/nextjs-vite देखें।
npm create vite@latest storybook-claude-demo -- --template react-ts
cd storybook-claude-demo
npm install
npm create storybook@latest
npx storybook add @storybook/addon-a11y
npx storybook add @storybook/addon-vitest
npm install -D chromatic @vitest/browser-playwright playwright
npx playwright install chromium
{
"scripts": {
"dev": "vite",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"test-storybook": "vitest --project=storybook --run",
"chromatic": "chromatic"
}
}
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
framework: '@storybook/react-vite',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: ['@storybook/addon-docs', '@storybook/addon-a11y', '@storybook/addon-vitest'],
docs: {
defaultName: 'Docs',
},
staticDirs: ['../public'],
};
export default config;
// .storybook/preview.ts
import type { Preview } from '@storybook/react-vite';
const preview: Preview = {
tags: ['autodocs'],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
a11y: {
test: 'todo',
},
},
};
export default preview;
Example 1: Button की typed story
Storybook के लिए अलग नकली props न बनाएं। Story वही contract दिखाए जो production component में है।
// src/components/Button.tsx
import type { ButtonHTMLAttributes, CSSProperties, ReactNode } from 'react';
type ButtonVariant = 'primary' | 'secondary' | 'outline';
type ButtonSize = 'sm' | 'md' | 'lg';
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
children: ReactNode;
variant?: ButtonVariant;
size?: ButtonSize;
loading?: boolean;
};
const variantStyle: Record<ButtonVariant, CSSProperties> = {
primary: { background: '#2563eb', color: '#ffffff', borderColor: '#2563eb' },
secondary: { background: '#0f172a', color: '#ffffff', borderColor: '#0f172a' },
outline: { background: '#ffffff', color: '#0f172a', borderColor: '#94a3b8' },
};
const sizeStyle: Record<ButtonSize, CSSProperties> = {
sm: { minHeight: 32, padding: '0 12px', fontSize: 14 },
md: { minHeight: 40, padding: '0 16px', fontSize: 15 },
lg: { minHeight: 48, padding: '0 20px', fontSize: 16 },
};
export function Button({
children,
variant = 'primary',
size = 'md',
loading = false,
disabled,
style,
...props
}: ButtonProps) {
return (
<button
{...props}
disabled={disabled || loading}
aria-busy={loading || undefined}
style={{
...variantStyle[variant],
...sizeStyle[size],
borderWidth: 1,
borderStyle: 'solid',
borderRadius: 6,
fontWeight: 700,
cursor: disabled || loading ? 'not-allowed' : 'pointer',
opacity: disabled ? 0.55 : 1,
...style,
}}
>
{loading ? 'Sending...' : children}
</button>
);
}
// src/components/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Button } from './Button';
const meta = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
args: {
children: 'Start free trial',
variant: 'primary',
size: 'md',
},
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'outline'],
},
size: {
control: 'inline-radio',
options: ['sm', 'md', 'lg'],
},
loading: { control: 'boolean' },
disabled: { control: 'boolean' },
},
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary = {} satisfies Story;
export const Secondary = {
args: {
variant: 'secondary',
children: 'View pricing',
},
} satisfies Story;
export const AllSizes = {
render: () => (
<div style={{ display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>
),
} satisfies Story;
export const Loading = {
args: {
loading: true,
},
} satisfies Story;
Example 2: Signup form का interaction test
play function story render होने के बाद user actions चलाता है। इससे सिर्फ UI दिखना नहीं, बल्कि input, error और submit भी verify होते हैं।
// src/components/SignupForm.tsx
import type { FormEvent } from 'react';
import { useState } from 'react';
import { Button } from './Button';
export type SignupFormData = {
name: string;
email: string;
plan: 'free' | 'team';
};
type SignupFormProps = {
plan?: SignupFormData['plan'];
onSubmit?: (data: SignupFormData) => Promise<void> | void;
};
export function SignupForm({ plan = 'free', onSubmit }: SignupFormProps) {
const [message, setMessage] = useState('');
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const form = new FormData(event.currentTarget);
const data: SignupFormData = {
name: String(form.get('name') ?? ''),
email: String(form.get('email') ?? ''),
plan,
};
if (!data.email.includes('@')) {
setMessage('Please enter a valid email address.');
return;
}
await onSubmit?.(data);
setMessage('Thanks, we will send the setup guide.');
}
return (
<form onSubmit={handleSubmit} style={{ display: 'grid', gap: 12, maxWidth: 360 }}>
<label>
Name
<input name="name" required style={{ display: 'block', width: '100%', minHeight: 36 }} />
</label>
<label>
Email
<input name="email" type="email" required style={{ display: 'block', width: '100%', minHeight: 36 }} />
</label>
<Button type="submit">Get the guide</Button>
{message ? <p role="status">{message}</p> : null}
</form>
);
}
// src/components/SignupForm.stories.tsx
import type { Meta, StoryObj } from '@storybook/react-vite';
import { expect, fn, userEvent, within } from '@storybook/test';
import { SignupForm } from './SignupForm';
const meta = {
title: 'Components/SignupForm',
component: SignupForm,
args: {
plan: 'team',
onSubmit: fn(),
},
} satisfies Meta<typeof SignupForm>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Empty = {} satisfies Story;
export const InvalidEmail = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.type(canvas.getByLabelText('Name'), 'Masa');
await userEvent.type(canvas.getByLabelText('Email'), 'masa.example.com');
await userEvent.click(canvas.getByRole('button', { name: 'Get the guide' }));
await expect(canvas.getByRole('status')).toHaveTextContent('valid email');
},
} satisfies Story;
export const SuccessfulSubmit = {
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
await userEvent.type(canvas.getByLabelText('Name'), 'Masa');
await userEvent.type(canvas.getByLabelText('Email'), 'masa@example.com');
await userEvent.click(canvas.getByRole('button', { name: 'Get the guide' }));
await expect(args.onSubmit).toHaveBeenCalledWith({
name: 'Masa',
email: 'masa@example.com',
plan: 'team',
});
await expect(canvas.getByRole('status')).toHaveTextContent('setup guide');
},
} satisfies Story;
Example 3: Vitest और CI visual check
Vitest addon stories को browser component tests में बदलता है। जिन stories में play है, उनमें interactions और assertions भी चलते हैं।
// .storybook/vitest.setup.ts
import { setProjectAnnotations } from '@storybook/react-vite';
import * as previewAnnotations from './preview';
setProjectAnnotations([previewAnnotations]);
// vitest.config.ts
import { defineConfig, defineProject, mergeConfig } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import viteConfig from './vite.config';
const dirname = path.dirname(fileURLToPath(import.meta.url));
export default mergeConfig(
viteConfig,
defineConfig({
test: {
projects: [
defineProject({
extends: true,
plugins: [
storybookTest({
configDir: path.join(dirname, '.storybook'),
storybookScript: 'npm run storybook -- --ci',
storybookUrl: 'http://localhost:6006',
tags: {
include: ['test'],
exclude: ['experimental'],
},
}),
],
test: {
name: 'storybook',
browser: {
enabled: true,
provider: playwright({}),
headless: true,
instances: [{ browser: 'chromium' }],
},
setupFiles: ['./.storybook/vitest.setup.ts'],
},
}),
],
},
}),
);
# .github/workflows/storybook.yml
name: storybook
on:
pull_request:
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npm run test-storybook
- run: npm run build-storybook
- name: Publish to Chromatic
if: ${{ secrets.CHROMATIC_PROJECT_TOKEN != '' }}
run: npx chromatic --project-token=${{ secrets.CHROMATIC_PROJECT_TOKEN }}
Claude Code prompt template
React + TypeScript + Vite project में Storybook setup करें।
Scope:
- src/components/Button.tsx
- src/components/SignupForm.tsx
Requirements:
- .storybook/main.ts में @storybook/react-vite इस्तेमाल करें
- CSF को Meta/StoryObj और satisfies के साथ type-safe बनाएं
- Button के लिए primary, secondary, outline, loading, disabled, all sizes stories जोड़ें
- SignupForm के लिए invalid email और successful submit play functions लिखें
- @storybook/test से within, userEvent, expect, fn इस्तेमाल करें
- Vitest addon configure करें ताकि test-storybook CI में चले
- GitHub Actions में build-storybook और Chromatic-style visual check जोड़ें
Do not:
- Pseudocode न लिखें
- Real component में नहीं मौजूद props न जोड़ें
- Secrets code में hard-code न करें
- Scope के बाहर files format न करें
Report:
- Changed files
- Commands run
- Remaining risks
आम गलतियां
पहली गलती है Storybook-only props बनाना। दूसरी है सिर्फ success path test करना; invalid input, loading, disabled और duplicate submit छूट जाते हैं। तीसरी है dates, random values, animations और remote images को stabilize न करना, जिससे Chromatic diff noisy हो जाता है। चौथी है @storybook/react, @storybook/react-vite और @storybook/nextjs-vite को मिलाना। पांचवीं है CI में Chromium install करना भूलना। छठी है revenue-related CTA, product card और inquiry form को बाद में छोड़ देना।
CTA और actual result
Technical media या product site में Storybook monetization flow को भी बचाता है। Free guide form, product CTA, training inquiry form और internal link cards stories बन सकते हैं, interaction test से चल सकते हैं, और PR में visually review हो सकते हैं।
Reusable Claude Code prompts चाहिए तो free cheat sheet से शुरू करें। Templates चाहिए तो products and templates देखें। Real repository में Storybook, CI, Chromatic, accessibility और CTA review साथ चाहिए तो Claude Code training and consulting बेहतर रास्ता है।
छोटे React project में test करने पर सबसे ज्यादा फायदा Button variants से नहीं, SignupForm की failing story से मिला। InvalidEmail और SuccessfulSubmit अलग करने और CI में test-storybook तथा build-storybook चलाने से Claude Code को दोबारा correction मांगने की जरूरत कम हुई। Visual review भी पूरे site से शुरू करने के बजाय CTA और forms पर केंद्रित करने से आसान रही।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code Obsidian to CLAUDE.md workflow: context बार-बार न समझाएं
Obsidian notes को CLAUDE.md operating notes में बदलकर Claude Code sessions को resume करना आसान बनाएं.
Claude Code Revenue CTA Routing: article से PDF, Gumroad और consultation तक
Reader intent के आधार पर free PDF, Gumroad products और consultation तक CTA route करने वाला workflow.
Claude Code टीम हैंडऑफ नियम: review proof, permissions, rollback और revenue path
Claude Code टीम काम के लिए evidence, permission rules, rollback, free PDF, Gumroad और consultation path वाला handoff.