Claude Codeはフロントエンド開発と非常に相性がよいツールです。コンポーネントの生成・カスタムHooksの設計・アクセシビリティの自動チェック・パフォーマンス最適化の提案まで、開発サイクルの各フェーズで活用できます。
この記事ではReactとNext.js(App Router)を対象に、Claude Codeを使ったフロントエンド開発の実践パターンを解説します。テストの書き方はテスト自動化・TDD完全ガイドを、デバッグ手法はデバッグ完全ガイドを参照してください。
CLAUDE.mdにフロントエンド規約を設定する
フロントエンドプロジェクトはコンポーネントのファイル構造・命名規則・スタイリング方針などチームごとの規約が多岐にわたります。CLAUDE.mdに規約を記載しておくと、毎回の指示が不要になります。
## Tech Stack
- Next.js 15 (App Router)
- React 19
- TypeScript (strict mode)
- Tailwind CSS v4
- Zustand (global state)
- TanStack Query v5 (server state / data fetching)
- Zod (validation)
## Component Rules
- Use Server Components by default; add "use client" only when needed
(event handlers, useState, useEffect, browser APIs)
- Component files: PascalCase (UserCard.tsx)
- One component per file
- Export as named export, not default export
- Props type: inline interface named `ComponentNameProps`
## File Structure
src/
app/ ← Next.js App Router pages and layouts
components/
ui/ ← Reusable primitive components (Button, Input, etc.)
features/ ← Feature-specific components
hooks/ ← Custom hooks
lib/ ← Utilities, helpers
types/ ← Shared TypeScript types
## Styling
- Use Tailwind CSS utility classes
- Never use inline styles
- For complex animations, use CSS Modules alongside Tailwind
- Dark mode: use Tailwind dark: variant (not class-based)
## Testing
- Vitest + React Testing Library
- Run: npm test
Server ComponentsとClient Componentsの使い分け
Next.js App Routerでは、コンポーネントはデフォルトでServer Componentsです。"use client"が必要な場合をClaude Codeに正確に判断させるには、判断基準を事前に伝えることが重要です。
| Server Component | Client Component(”use client”が必要) |
|---|---|
| データフェッチ(直接DBアクセス・fetch) | onClick・onChange などのイベントハンドラー |
| バックエンドリソースへのアクセス | useState・useReducer などの状態管理 |
| APIキーなどの機密情報へのアクセス | useEffect・useLayoutEffect |
| 大きな依存関係のバンドルサイズ削減 | ブラウザ専用API(localStorage・navigator) |
| SEOが重要な静的コンテンツ | アニメーション・ドラッグなどのインタラクション |
claude "Create a ProductCard component for a Next.js 15 App Router project. The component needs to: - Display product image, name, price, and rating - Have an 'Add to Cart' button that calls addToCart(productId) from our Zustand cart store - Show a skeleton loading state while data is loading Decide whether this should be a Server Component or Client Component, and explain your reasoning. Follow the conventions in CLAUDE.md."
// products/ProductCard.tsx
// "use client" が必要な理由: Zustandストアへのアクセス(ブラウザ側の状態)
"use client";
import Image from "next/image";
import { useCartStore } from "@/store/cart";
interface ProductCardProps {
id: string;
name: string;
price: number;
imageUrl: string;
rating: number;
}
export function ProductCard({ id, name, price, imageUrl, rating }: ProductCardProps) {
const addToCart = useCartStore((state) => state.addToCart);
return (
<div className="rounded-lg border border-gray-200 p-4 shadow-sm">
<Image src={imageUrl} alt={name} width={200} height={200} className="rounded-md" />
<h3 className="mt-2 text-lg font-semibold">{name}</h3>
<p className="text-2xl font-bold text-blue-600">¥{price.toLocaleString()}</p>
<button
onClick={() => addToCart(id)}
className="mt-3 w-full rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
>
カートに追加
</button>
</div>
);
}
カスタムHooksの設計と生成
ロジックをコンポーネントから分離するカスタムHooksは、テスタビリティと再利用性を高める重要なパターンです。Claude Codeは「何をしたいか」を伝えるだけで適切なHook設計を提案します。
claude "Create a custom hook useInfiniteScroll that: - Observes a target element (ref) using IntersectionObserver - Calls a provided callback function when the element enters the viewport - Handles cleanup on unmount - Returns isIntersecting boolean Also create a useDebounce hook that: - Takes a value and delay (ms) - Returns the debounced value - Cleans up the timer on unmount or value change Write unit tests for both hooks using Vitest + React Testing Library."
// hooks/useDebounce.ts
import { useState, useEffect } from "react";
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
TanStack QueryのカスタムHooksを生成する
claude "Create TanStack Query hooks for the products API (src/api/products.ts). Create: 1. useProducts(filters) - GET /api/products with optional filters 2. useProduct(id) - GET /api/products/:id, disabled if id is undefined 3. useCreateProduct() - POST /api/products mutation with optimistic update 4. useUpdateProduct() - PUT /api/products/:id mutation 5. useDeleteProduct() - DELETE /api/products/:id with list cache invalidation Use query keys as: ['products', filters], ['products', id] For mutations, invalidate the products list on success."
Server Actions の実装
Next.js App RouterのServer Actionsは、クライアントからサーバー側の処理を直接呼び出せる機能です。フォームの送信・データの更新に使いやすく、Claude Codeはバリデーション・エラーハンドリング・再検証まで一括で生成できます。
claude "Create a Server Action for creating a new user profile in Next.js 15.
Requirements:
- Validate input with Zod (name: string min 2, email: valid email,
bio: string optional max 500 chars)
- Save to database using Prisma (User model)
- Revalidate the path /profile after success
- Return a typed result: { success: true, userId: string }
or { success: false, errors: ZodError['errors'] }
- Handle Prisma unique constraint error for duplicate email
Use 'use server' directive. Export as createUserProfile."
// app/actions/user.ts
"use server";
import { z } from "zod";
import { revalidatePath } from "next/cache";
import { prisma } from "@/lib/prisma";
import { Prisma } from "@prisma/client";
const CreateUserSchema = z.object({
name: z.string().min(2, "名前は2文字以上で入力してください"),
email: z.string().email("有効なメールアドレスを入力してください"),
bio: z.string().max(500, "自己紹介は500文字以内で入力してください").optional(),
});
type CreateUserResult =
| { success: true; userId: string }
| { success: false; errors: z.ZodError["errors"] | string };
export async function createUserProfile(
formData: FormData
): Promise<CreateUserResult> {
const parsed = CreateUserSchema.safeParse({
name: formData.get("name"),
email: formData.get("email"),
bio: formData.get("bio") || undefined,
});
if (!parsed.success) {
return { success: false, errors: parsed.error.errors };
}
try {
const user = await prisma.user.create({ data: parsed.data });
revalidatePath("/profile");
return { success: true, userId: user.id };
} catch (error) {
if (
error instanceof Prisma.PrismaClientKnownRequestError &&
error.code === "P2002"
) {
return { success: false, errors: "このメールアドレスはすでに使用されています" };
}
throw error;
}
}
Tailwind CSSスタイリングの効率化
デザインからTailwindコンポーネントを生成する
スクリーンショットや設計仕様を渡すと、Tailwind CSSのクラスを使ったコンポーネントを生成できます。ChromeとClaude Codeを組み合わせると実装→確認→修正のサイクルが短縮されます。
claude "Create a pricing card component with these specifications: - 3 tiers: Free, Pro ($19/mo), Enterprise (custom) - Pro tier is highlighted with a blue gradient background - Each tier has: name, price, feature list (5 items), CTA button - Responsive: stacked on mobile, 3 columns on desktop (md:grid-cols-3) - Use Tailwind CSS v4 only (no custom CSS) - CTA buttons: Free=outlined, Pro=filled blue, Enterprise=outlined dark"
既存コンポーネントのTailwindリファクタリング
claude "Convert all inline styles in src/components/ to Tailwind CSS classes.
Rules:
- Never use style={{}} props
- Map CSS properties to the closest Tailwind equivalent
- For values not in the default Tailwind scale, use arbitrary values: w-[320px]
- Preserve all existing className strings
- After conversion, visually verify nothing broke by checking
that the Tailwind classes produce equivalent styles"
shadcn/uiコンポーネントとの連携
claude "Add a data table component for displaying the orders list. Use shadcn/ui's Table component (already installed). Requirements: - Columns: Order ID, Customer, Status (badge), Amount, Date, Actions - Sortable columns: Date, Amount - Status badge colors: pending=yellow, processing=blue, shipped=green, cancelled=red - Actions column: View, Cancel (only for pending orders) - Pagination: 20 rows per page - Loading state with skeleton rows Data type: Order[] from src/types/order.ts"
アクセシビリティの自動チェックと改善
アクセシビリティ(a11y)対応は見落とされがちですが、Claude Codeはコンポーネントを読んでWCAG違反を指摘し修正できます。後から直すより、生成時に一緒に対応させるのが効率的です。
claude "Audit the accessibility of all components in src/components/ui/. Check for these WCAG 2.1 AA issues: 1. Missing or incorrect aria-label on interactive elements 2. Missing alt text on images 3. Form inputs without associated labels 4. Color contrast issues (describe the class combinations, not exact hex values) 5. Keyboard navigation issues (missing tabIndex, missing onKeyDown handlers) 6. Missing role attributes on custom interactive elements 7. Missing aria-expanded, aria-controls for disclosure patterns For each issue found, show the current code and the corrected version."
claude "Create an accessible modal dialog component that: - Traps focus within the modal when open - Returns focus to the trigger element when closed - Can be closed with Escape key - Has role='dialog' and aria-modal='true' - Has aria-labelledby pointing to the modal title - Prevents scrolling of the background content - Accessible announce to screen readers when opened (use aria-live if needed)"
パフォーマンス最適化
不要な再レンダリングの検出と修正
Reactの不要な再レンダリングはパフォーマンス問題の主要な原因です。Claude Codeはコンポーネントツリーを読んで最適化箇所を特定できます。
claude "Analyze the rendering performance of src/components/features/ProductList.tsx and its child components. Look for: 1. Components that re-render even when their props haven't changed (candidates for React.memo) 2. Expensive computations not wrapped in useMemo 3. Callback functions re-created on every render (candidates for useCallback) 4. Context consumers that re-render due to unrelated state changes 5. Large lists that should use virtualization (react-virtual or similar) For each finding, explain why it causes a performance issue and show the fix."
Next.js特有の最適化
claude "Review the Next.js performance of this app and suggest improvements: 1. Check all <Image> usage in src/ — are width/height set? Is priority set for above-the-fold images? Are remote domains configured in next.config.ts? 2. Check for large client-side imports that could be Server Components 3. Check for missing Suspense boundaries around dynamic content 4. Check if any Server Components are unnecessarily marked 'use client' 5. Check if dynamic() with ssr:false is used appropriately for browser-only components 6. Check next.config.ts for missing optimizations (compress, images.formats, etc.)"
Bundle Analyzerでの依存関係調査
claude "The production bundle is too large (>500KB initial JS). Help me identify and reduce it. 1. First, set up @next/bundle-analyzer: - Install the package - Add configuration to next.config.ts 2. After analysis (I'll run it and share the output), look for: - Libraries imported entirely but only using small parts (moment.js, lodash) - Duplicate dependencies - Components/pages that could use dynamic imports 3. Suggest specific optimizations with expected size savings"
コンポーネントテストの自動生成
React Testing Libraryを使ったコンポーネントテストもClaude Codeが自動生成できます。ユーザー操作を模したテストを「コンポーネントと同時に生成する」習慣をつけるとテストカバレッジを維持しやすくなります。
claude "Create the LoginForm component and its tests simultaneously. Component requirements: - Email and password inputs with validation - Submit button that calls onSubmit(email, password) prop - Show loading spinner during submission (isLoading prop) - Show error message when error prop is set - Disable the form during loading Tests (Vitest + React Testing Library): - Renders all form elements - Shows validation error for invalid email - Shows validation error for empty password - Calls onSubmit with correct values on valid submit - Shows loading spinner when isLoading=true - Disables submit button when isLoading=true - Displays the error message when error prop is set"
よくある質問
QServer ComponentsとClient Componentsの境界線を毎回Claude Codeが正しく判断してくれません
ACLAUDE.mdに判断基準を明記するのが最も効果的です。「useStateやイベントハンドラーを使う場合は”use client”を追加。それ以外はデフォルトでServer Componentとする」のように具体的なルールを書いてください。個別のコンポーネント生成時に「Server ComponentかClient Componentかを明示して判断根拠も説明して」と付け加えると確認しながら進められます。
QTailwind CSSのクラスが長くなりすぎて管理しにくくなります
AClaude Codeに「cn()ヘルパー(clsx + tailwind-merge)を使ってクラスを整理して」と依頼すると、条件付きクラスや長いクラス列を読みやすく整理してくれます。また「このコンポーネントのスタイルをTailwind v4のCSS変数として抽出してtheme()で参照するようにして」と頼むと、デザイントークン化も対応してくれます。
QNext.js App RouterでのData Fetchingのパターンが多すぎて迷います
A「このデータはどこでフェッチすべきか判断して」と、データの性質(リアルタイム性・認証要否・キャッシュ戦略)を説明しながら依頼すると、Claude Codeが最適なパターンを提案します。具体的には:静的コンテンツはServer Component + fetch with cache→Server Component + TanStack Query→リアルタイムはWebSocket/Server-Sent Eventsという判断軸をCLAUDE.mdに書いておくと一貫性が保たれます。
Qアクセシビリティの修正をしたら見た目が崩れてしまいました
Aアクセシビリティ属性の追加(aria-label・role等)は見た目に影響しないはずです。見た目が崩れた場合はHTMLの構造変更(labelとinputの関連付けでDOMが変わった等)が原因の可能性があります。「アクセシビリティを改善したが見た目が崩れた。スタイルを変更せずにa11yのみ修正して」と依頼し直してください。
Qスクリーンショットを見せてデザイン再現を頼むことはできますか?
Aできます。Claude Codeは画像を読み取ってTailwindのコンポーネントを生成できます。ただし色・フォントサイズ・間隔の正確な数値は近い値になることがあります。FigmaのMCPサーバーを連携させると、Figmaのデザインデータから直接コンポーネントを生成できるため精度が上がります。MCPサーバーの設定はMCP完全ガイドを参照してください。

