jpskill.com
🛠️ 開発・MCP コミュニティ 🔴 エンジニア向け 👤 エンジニア・AI開発者

🛠️ Afrexai React Production

afrexai-react-production

本番環境で利用できる高品質なReactアプリケーションを、

⏱ ボイラープレート実装 半日 → 30分

📺 まず動画で見る(YouTube)

▶ 【衝撃】最強のAIエージェント「Claude Code」の最新機能・使い方・プログラミングをAIで効率化する超実践術を解説! ↗

※ jpskill.com 編集部が参考用に選んだ動画です。動画の内容と Skill の挙動は厳密には一致しないことがあります。

📜 元の英語説明(参考)

Complete methodology for building production-grade React applications with architecture decisions, component design, state management, performance optimization, testing, and deployment.

🇯🇵 日本人クリエイター向け解説

一言でいうと

本番環境で利用できる高品質なReactアプリケーションを、

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o afrexai-react-production.zip https://jpskill.com/download/4298.zip && unzip -o afrexai-react-production.zip && rm afrexai-react-production.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/4298.zip -OutFile "$d\afrexai-react-production.zip"; Expand-Archive "$d\afrexai-react-production.zip" -DestinationPath $d -Force; ri "$d\afrexai-react-production.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して afrexai-react-production.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → afrexai-react-production フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-17
取得日時
2026-05-18
同梱ファイル
2

💬 こう話しかけるだけ — サンプルプロンプト

  • Afrexai React Production を使って、最小構成のサンプルコードを示して
  • Afrexai React Production の主な使い方と注意点を教えて
  • Afrexai React Production を既存プロジェクトに組み込む方法を教えて

これをClaude Code に貼るだけで、このSkillが自動発動します。

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

[Skill 名] afrexai-react-production

Reactプロダクションエンジニアリング

プロダクションレベルのReactアプリケーションを構築するための完全な方法論です。アーキテクチャの決定、コンポーネント設計、状態管理、パフォーマンス最適化、テスト、デプロイメントを網羅しています。単なるAPIリファレンスではなく、決定フレームワーク、テンプレート、スコアリングシステムを備えたエンジニアリング方法論です。

フェーズ1: アーキテクチャ評価

クイックヘルスチェック (スコア /16)

  • [ ] コンポーネントツリーの深さ < 6レベル (+2)
  • [ ] 2レベルを超えるプロップドリリングがない (+2)
  • [ ] バンドルサイズ < 200KB (gzip圧縮後) (+2)
  • [ ] 4G環境でのLCP < 2.5秒 (+2)
  • [ ] ビジネスロジックのテストカバレッジ > 70% (+2)
  • [ ] プロダクションコードに any 型がゼロ (+2)
  • [ ] 直接的なDOM操作がない (+2)
  • [ ] 一貫したエラーバウンダリ (+2)

アーキテクチャ概要

project:
  name: ""
  type: "" # spa | ssr | hybrid | static
  framework: "" # next | remix | vite-spa | astro
  scale: "" # small (<20 routes) | medium (20-100) | large (100+)
  team_size: "" # solo | small (2-5) | medium (6-15) | large (15+)
current_state:
  react_version: "" # 18 | 19
  typescript: true
  router: "" # react-router | next-app | tanstack-router
  state_management: "" # useState | zustand | jotai | redux | tanstack-query
  styling: "" # tailwind | css-modules | styled-components | vanilla-extract
  testing: "" # vitest | jest | playwright | cypress
  ci_cd: "" # github-actions | gitlab-ci | vercel
pain_points: []
goals: []

フレームワーク選択決定マトリックス

要因 Vite SPA Next.js Remix Astro
SEOの必要性 ✅ 最適 ✅ 良好 ✅ 最適
ダッシュボード/アプリ ✅ 最適 ✅ 良好 ✅ 良好
コンテンツ重視 ✅ 良好 ✅ 良好 ✅ 最適
チームの習熟度 ✅ 簡単 ⚠️ 学習曲線 ⚠️ Web標準 ⚠️ アイランド
デプロイ どこでも Vercelが最適 どこでも どこでも
バンドルサイズ 制御可能 フレームワークのオーバーヘッド 小さい 最小限のJS

決定ルール:

  1. SEO不要なダッシュボード/社内ツール → Vite SPA
  2. マーケティング + アプリのハイブリッド → Next.js
  3. コンテンツファーストで一部インタラクティブ性が必要 → Astro
  4. Web標準ファースト、ネストされたレイアウト → Remix
  5. ほとんどのSaaS製品のデフォルト → Next.js

フェーズ2: プロジェクト構造と規約

推奨される機能ベースの構造

src/
├── app/                    # ルート/ページ (フレームワーク固有)
├── features/               # 機能モジュール (THE コアパターン)
│   ├── auth/
│   │   ├── components/     # 機能固有のコンポーネント
│   │   ├── hooks/          # 機能固有のフック
│   │   ├── api/            # API呼び出しと型
│   │   ├── utils/          # 機能ユーティリティ
│   │   ├── types.ts        # 機能の型
│   │   └── index.ts        # パブリックAPI (バレルエクスポート)
│   ├── dashboard/
│   └── settings/
├── shared/                 # 機能横断的な共有コード
│   ├── components/         # 汎用UIコンポーネント
│   │   ├── ui/             # プリミティブ (Button, Input, Card)
│   │   └── layout/         # レイアウトコンポーネント
│   ├── hooks/              # 汎用フック
│   ├── lib/                # ユーティリティ、定数
│   └── types/              # グローバルな型
├── providers/              # コンテキストプロバイダー
└── styles/                 # グローバルスタイル

7つの構造ルール

  1. 機能の分離 — features/ は他の features/ から直接インポートしない。shared/ またはイベントを使用する
  2. バレルエクスポート — すべての機能はパブリックAPIを定義する index.ts を持つ
  3. コロケーション — テスト、ストーリー、スタイルはコンポーネントの隣に配置する
  4. 最大ファイルサイズ — 300行。これより大きい場合は分割する
  5. 最大コンポーネントサイズ — JSX 50行。これより大きい場合は抽出する
  6. 循環依存なし — eslint-plugin-import で強制する
  7. 型のコロケーション — 機能の型は機能内に、共有の型は shared/types に配置する

命名規約

Components:     PascalCase.tsx       (UserProfile.tsx)
Hooks:          useCamelCase.ts      (useAuth.ts)
Utilities:      camelCase.ts         (formatCurrency.ts)
Types:          PascalCase.ts        (User.ts) or types.ts
Constants:      SCREAMING_SNAKE.ts   (API_ENDPOINTS.ts)
Test files:     *.test.tsx           (UserProfile.test.tsx)
Story files:    *.stories.tsx        (Button.stories.tsx)

フェーズ3: コンポーネント設計パターン

コンポーネントの構造テンプレート

// 1. インポート (グループ化: react → サードパーティ → 内部 → 型 → スタイル)
import { useState, useCallback, memo } from 'react'
import { clsx } from 'clsx'
import { Button } from '@/shared/components/ui'
import type { User } from '../types'

// 2. 型 (再利用のためにエクスポート)
export interface UserCardProps {
  user: User
  onEdit?: (id: string) => void
  variant?: 'compact' | 'full'
  className?: string
}

// 3. コンポーネント (名前付きエクスポート、デフォルトではない)
export const UserCard = memo(function UserCard({
  user,
  onEdit,
  variant = 'full',
  className,
}: UserCardProps) {
  // 4. フックを最初に
  const [isExpanded, setIsExpanded] = useState(false)

  // 5. 派生状態 (派生状態に useEffect は使わない!)
  const displayName = `${user.firstName} ${user.lastName}`

  // 6. ハンドラ (渡される参照には useCallback を使用)
  const handleEdit = useCallback(() => {
    onEdit?.(user.id)
  }, [onEdit, user.id])

  // 7. エッジケースの早期リターン
  if (!user) return null

  // 8. JSX (最大50行)
  return (
    <div className={clsx('rounded-lg border p-4', className)}>
      <h3>{displayName}</h3>
      {variant === 'full' && <p>{user.bio}</p>}
      {onEdit && <Button onClick={handleEdit}>Edit</Button>}
    </div>
  )
})

コンポーネント構成パターン

1. 複合コンポーネント (関連するUIグループ向け)

// 使用法: <Tabs><Tabs.List><Tabs.Tab>A</Tabs.Tab></Tabs.List><Tabs.Panel>...</Tabs.Panel></Tabs>
const TabsContext = createContext<TabsContextType | null>(null)

export function Tabs({ children, defaultValue }: TabsProps) {
  const [activeTab, setActiveTab] = useState(defaultValue)
  return (
    <TabsContext.Provider value={{ activeTab, setActiveTab }}>
      {children}
    </TabsContext.Provider>
  )
}
Tabs.List = TabsList
Ta
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

React Production Engineering

Complete methodology for building production-grade React applications. Covers architecture decisions, component design, state management, performance optimization, testing, and deployment — not just API reference, but engineering methodology with decision frameworks, templates, and scoring systems.

Phase 1: Architecture Assessment

Quick Health Check (score /16)

  • [ ] Component tree depth < 6 levels (+2)
  • [ ] No prop drilling past 2 levels (+2)
  • [ ] Bundle size < 200KB gzipped (+2)
  • [ ] LCP < 2.5s on 4G (+2)
  • [ ] Test coverage > 70% on business logic (+2)
  • [ ] Zero any types in production code (+2)
  • [ ] No direct DOM manipulation (+2)
  • [ ] Consistent error boundaries (+2)

Architecture Brief

project:
  name: ""
  type: "" # spa | ssr | hybrid | static
  framework: "" # next | remix | vite-spa | astro
  scale: "" # small (<20 routes) | medium (20-100) | large (100+)
  team_size: "" # solo | small (2-5) | medium (6-15) | large (15+)
current_state:
  react_version: "" # 18 | 19
  typescript: true
  router: "" # react-router | next-app | tanstack-router
  state_management: "" # useState | zustand | jotai | redux | tanstack-query
  styling: "" # tailwind | css-modules | styled-components | vanilla-extract
  testing: "" # vitest | jest | playwright | cypress
  ci_cd: "" # github-actions | gitlab-ci | vercel
pain_points: []
goals: []

Framework Selection Decision Matrix

Factor Vite SPA Next.js Remix Astro
SEO needed ✅ Best ✅ Good ✅ Best
Dashboard/app ✅ Best ✅ Good ✅ Good
Content-heavy ✅ Good ✅ Good ✅ Best
Team familiarity ✅ Simple ⚠️ Learning curve ⚠️ Web standards ⚠️ Islands
Deployment Anywhere Vercel optimal Anywhere Anywhere
Bundle size You control Framework overhead Smaller Minimal JS

Decision rules:

  1. Dashboard/internal tool with no SEO → Vite SPA
  2. Marketing + app hybrid → Next.js
  3. Content-first with some interactivity → Astro
  4. Web-standards-first, nested layouts → Remix
  5. Default for most SaaS products → Next.js

Phase 2: Project Structure & Conventions

Recommended Feature-Based Structure

src/
├── app/                    # Routes/pages (framework-specific)
├── features/               # Feature modules (THE core pattern)
│   ├── auth/
│   │   ├── components/     # Feature-specific components
│   │   ├── hooks/          # Feature-specific hooks
│   │   ├── api/            # API calls & types
│   │   ├── utils/          # Feature utilities
│   │   ├── types.ts        # Feature types
│   │   └── index.ts        # Public API (barrel export)
│   ├── dashboard/
│   └── settings/
├── shared/                 # Cross-feature shared code
│   ├── components/         # Generic UI components
│   │   ├── ui/             # Primitives (Button, Input, Card)
│   │   └── layout/         # Layout components
│   ├── hooks/              # Generic hooks
│   ├── lib/                # Utilities, constants
│   └── types/              # Global types
├── providers/              # Context providers
└── styles/                 # Global styles

7 Structure Rules

  1. Feature isolation — features/ never import from other features directly; use shared/ or events
  2. Barrel exports — every feature has index.ts that defines its public API
  3. Colocation — tests, stories, and styles live next to their component
  4. Max file size — 300 lines. If bigger, split
  5. Max component size — 50 lines of JSX. If bigger, extract
  6. No circular deps — enforce with eslint-plugin-import
  7. Types colocated — feature types in feature, shared types in shared/types

Naming Conventions

Components:     PascalCase.tsx       (UserProfile.tsx)
Hooks:          useCamelCase.ts      (useAuth.ts)
Utilities:      camelCase.ts         (formatCurrency.ts)
Types:          PascalCase.ts        (User.ts) or types.ts
Constants:      SCREAMING_SNAKE.ts   (API_ENDPOINTS.ts)
Test files:     *.test.tsx           (UserProfile.test.tsx)
Story files:    *.stories.tsx        (Button.stories.tsx)

Phase 3: Component Design Patterns

Component Anatomy Template

// 1. Imports (grouped: react → third-party → internal → types → styles)
import { useState, useCallback, memo } from 'react'
import { clsx } from 'clsx'
import { Button } from '@/shared/components/ui'
import type { User } from '../types'

// 2. Types (exported for reuse)
export interface UserCardProps {
  user: User
  onEdit?: (id: string) => void
  variant?: 'compact' | 'full'
  className?: string
}

// 3. Component (named export, not default)
export const UserCard = memo(function UserCard({
  user,
  onEdit,
  variant = 'full',
  className,
}: UserCardProps) {
  // 4. Hooks first
  const [isExpanded, setIsExpanded] = useState(false)

  // 5. Derived state (no useEffect for derived!)
  const displayName = `${user.firstName} ${user.lastName}`

  // 6. Handlers (useCallback for passed-down refs)
  const handleEdit = useCallback(() => {
    onEdit?.(user.id)
  }, [onEdit, user.id])

  // 7. Early returns for edge cases
  if (!user) return null

  // 8. JSX (max 50 lines)
  return (
    <div className={clsx('rounded-lg border p-4', className)}>
      <h3>{displayName}</h3>
      {variant === 'full' && <p>{user.bio}</p>}
      {onEdit && <Button onClick={handleEdit}>Edit</Button>}
    </div>
  )
})

Component Composition Patterns

1. Compound Components (for related UI groups)

// Usage: <Tabs><Tabs.List><Tabs.Tab>A</Tabs.Tab></Tabs.List><Tabs.Panel>...</Tabs.Panel></Tabs>
const TabsContext = createContext<TabsContextType | null>(null)

export function Tabs({ children, defaultValue }: TabsProps) {
  const [activeTab, setActiveTab] = useState(defaultValue)
  return (
    <TabsContext.Provider value={{ activeTab, setActiveTab }}>
      {children}
    </TabsContext.Provider>
  )
}
Tabs.List = TabsList
Tabs.Tab = TabsTab
Tabs.Panel = TabsPanel

2. Render Props (for flexible rendering logic)

export function DataList<T>({ items, renderItem, renderEmpty }: DataListProps<T>) {
  if (items.length === 0) return renderEmpty?.() ?? <EmptyState />
  return <ul>{items.map((item, i) => <li key={i}>{renderItem(item)}</li>)}</ul>
}

3. Higher-Order Components (for cross-cutting concerns — use sparingly)

export function withAuth<P>(Component: ComponentType<P>) {
  return function AuthenticatedComponent(props: P) {
    const { user, isLoading } = useAuth()
    if (isLoading) return <Spinner />
    if (!user) return <Navigate to="/login" />
    return <Component {...props} />
  }
}

10 Component Rules

  1. One component per file — always
  2. Named exports — never default exports (refactoring safety)
  3. Props interface — always explicit, always exported
  4. No business logic in components — extract to hooks
  5. No inline styles — use Tailwind classes or CSS modules
  6. No string refs — useRef only
  7. No index as key — use stable identifiers
  8. Memo strategically — not everywhere, only for expensive renders
  9. Children over props — prefer composition over configuration
  10. Accessible by default — semantic HTML, ARIA when needed

Phase 4: State Management Decision Framework

State Type Decision Tree

Is it server data (from API)?
├─ YES → TanStack Query (or SWR) — NEVER Redux/Zustand for server state
│
└─ NO → Is it shared across features?
    ├─ YES → Is it complex with many actions?
    │   ├─ YES → Zustand (or Redux Toolkit if team knows it)
    │   └─ NO → Jotai (atomic) or Zustand (simple store)
    │
    └─ NO → Is it shared within a feature?
        ├─ YES → Context + useReducer (or Zustand feature store)
        └─ NO → useState / useReducer (component-local)

State Management Comparison

Tool Best For Bundle Learning Team Size
useState Component-local 0 KB None Any
useReducer Complex local state 0 KB Low Any
Context Feature-scoped, low-frequency 0 KB Low Any
Zustand Global client state 1.1 KB Low Any
Jotai Atomic derived state 3.4 KB Medium Small-Med
TanStack Query Server state 12 KB Medium Any
Redux Toolkit Complex global + middleware 11 KB High Large

Server State with TanStack Query

// api/users.ts — query key factory pattern
export const userKeys = {
  all: ['users'] as const,
  lists: () => [...userKeys.all, 'list'] as const,
  list: (filters: Filters) => [...userKeys.lists(), filters] as const,
  details: () => [...userKeys.all, 'detail'] as const,
  detail: (id: string) => [...userKeys.details(), id] as const,
}

// hooks/useUsers.ts
export function useUsers(filters: Filters) {
  return useQuery({
    queryKey: userKeys.list(filters),
    queryFn: () => fetchUsers(filters),
    staleTime: 5 * 60 * 1000, // 5 min
    placeholderData: keepPreviousData,
  })
}

export function useUpdateUser() {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: updateUser,
    onMutate: async (newUser) => {
      // Optimistic update
      await queryClient.cancelQueries({ queryKey: userKeys.detail(newUser.id) })
      const previous = queryClient.getQueryData(userKeys.detail(newUser.id))
      queryClient.setQueryData(userKeys.detail(newUser.id), newUser)
      return { previous }
    },
    onError: (err, newUser, context) => {
      queryClient.setQueryData(userKeys.detail(newUser.id), context?.previous)
    },
    onSettled: (data, err, variables) => {
      queryClient.invalidateQueries({ queryKey: userKeys.detail(variables.id) })
      queryClient.invalidateQueries({ queryKey: userKeys.lists() })
    },
  })
}

Client State with Zustand

// stores/useUIStore.ts — thin, focused stores
interface UIStore {
  sidebarOpen: boolean
  theme: 'light' | 'dark' | 'system'
  toggleSidebar: () => void
  setTheme: (theme: UIStore['theme']) => void
}

export const useUIStore = create<UIStore>()(
  persist(
    (set) => ({
      sidebarOpen: true,
      theme: 'system',
      toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
      setTheme: (theme) => set({ theme }),
    }),
    { name: 'ui-preferences' }
  )
)

// Usage: const theme = useUIStore((s) => s.theme) — always use selectors!

5 State Management Rules

  1. Server state ≠ client state — never mix them in the same store
  2. Smallest scope possible — useState > Context > Zustand > Redux
  3. No useEffect for derived state — use useMemo or compute inline
  4. Selectors alwaysuseStore(s => s.field) not useStore()
  5. URL is state — search params, filters, pagination → URL, not React state

Phase 5: Hooks Engineering

Custom Hook Template

// hooks/useDebounce.ts
export function useDebounce<T>(value: T, delayMs: number = 300): T {
  const [debouncedValue, setDebouncedValue] = useState(value)

  useEffect(() => {
    const timer = setTimeout(() => setDebouncedValue(value), delayMs)
    return () => clearTimeout(timer)
  }, [value, delayMs])

  return debouncedValue
}

Essential Custom Hooks Library

Hook Purpose When to Use
useDebounce Debounce value changes Search inputs, resize
useMediaQuery Responsive breakpoints Conditional rendering
useLocalStorage Persistent local state Preferences, drafts
useIntersection Viewport detection Lazy load, infinite scroll
usePrevious Track previous value Animations, comparisons
useClickOutside Detect outside clicks Dropdowns, modals
useEventListener Safe event binding Keyboard, scroll, resize
useToggle Boolean state toggle Modals, accordions

Hook Rules (beyond React's rules)

  1. One concern per hookuseUserSearch not useEverything
  2. Return tuple or object — tuple for 1-2 values, object for 3+
  3. Accept options objectuseDebounce(value, { delay: 300 }) scales better
  4. Handle cleanup — every subscription/timer needs cleanup in useEffect return
  5. No hooks in conditions — extract conditional logic into the hook body
  6. Test hooks independently — use renderHook from testing-library

Phase 6: TypeScript Integration

Strict Configuration

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "exactOptionalPropertyTypes": true,
    "forceConsistentCasingInFileNames": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Essential Type Patterns

// 1. Discriminated unions for state machines
type AsyncState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error }

// 2. Polymorphic components
type ButtonProps<C extends ElementType = 'button'> = {
  as?: C
  variant?: 'primary' | 'secondary'
} & ComponentPropsWithoutRef<C>

export function Button<C extends ElementType = 'button'>({
  as,
  variant = 'primary',
  ...props
}: ButtonProps<C>) {
  const Component = as || 'button'
  return <Component {...props} />
}

// 3. Branded types for IDs
type UserId = string & { __brand: 'UserId' }
type PostId = string & { __brand: 'PostId' }

// 4. Zod for runtime validation
const userSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  role: z.enum(['admin', 'user', 'viewer']),
})
type User = z.infer<typeof userSchema>

5 TypeScript Rules

  1. Zero any — use unknown and narrow, or generics
  2. Zod at boundaries — validate all external data (API, forms, URL params)
  3. Discriminated unions over optional fields{ status: 'success'; data: T } not { data?: T; error?: Error }
  4. Branded types for IDs — prevent userId being passed where postId expected
  5. Satisfies over asconfig satisfies Config preserves inference; as Config lies

Phase 7: Performance Optimization

Performance Budget

Metric Target Measurement
First Contentful Paint < 1.8s Lighthouse
Largest Contentful Paint < 2.5s Lighthouse
Interaction to Next Paint < 200ms Lighthouse
Cumulative Layout Shift < 0.1 Lighthouse
Bundle size (gzipped) < 200 KB webpack-bundle-analyzer
JS execution (main thread) < 3s Chrome DevTools

Optimization Priority Stack

Priority Technique Impact Effort
P0 Code splitting (route-based) 🔴 High Low
P0 Image optimization (next/image, srcset) 🔴 High Low
P1 Tree shaking (named imports) 🟡 Medium Low
P1 Virtualization for long lists 🟡 Medium Medium
P1 Debounce expensive operations 🟡 Medium Low
P2 React.memo on expensive components 🟢 Low-Med Low
P2 useMemo/useCallback for expensive calculations 🟢 Low-Med Low
P3 Web Workers for heavy computation 🟢 Low High

Code Splitting Patterns

// 1. Route-based (automatic with Next.js, manual with React Router)
const Dashboard = lazy(() => import('./features/dashboard'))
const Settings = lazy(() => import('./features/settings'))

// 2. Component-based (heavy components)
const Chart = lazy(() => import('./components/Chart'))
const MarkdownEditor = lazy(() =>
  import('./components/MarkdownEditor').then(m => ({ default: m.MarkdownEditor }))
)

// 3. Library-based (heavy third-party)
const { PDFViewer } = await import('@react-pdf/renderer')

React Compiler (React 19+)

// With React Compiler enabled, manual memo/useMemo/useCallback become unnecessary
// The compiler auto-memoizes. Remove manual optimizations:
// ❌ const memoized = useMemo(() => expensiveCalc(data), [data])
// ✅ const memoized = expensiveCalc(data)  // compiler handles it

// Enable in babel config:
// plugins: [['babel-plugin-react-compiler', {}]]

Rendering Performance Rules

  1. Never create components inside components — define at module level
  2. Never create objects/arrays in JSXstyle={{ color: 'red' }} rerenders always
  3. Children as props prevent rerender<Layout><ExpensiveChild /></Layout>
  4. Key must be stable and unique — not index, not Math.random()
  5. Avoid context value churn — memoize provider value or split contexts
  6. Profile before optimizing — React DevTools Profiler, not guesswork

Phase 8: Error Handling & Resilience

Error Boundary Architecture

// Three levels of error boundaries:
// 1. App-level (catches everything, shows full-page error)
// 2. Feature-level (isolates feature failures)
// 3. Component-level (for risky widgets — charts, third-party)

// Modern error boundary with react-error-boundary
import { ErrorBoundary, FallbackProps } from 'react-error-boundary'

function FeatureErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
  return (
    <div role="alert" className="rounded-lg border-red-200 bg-red-50 p-4">
      <h3>Something went wrong</h3>
      <pre className="text-sm text-red-600">{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  )
}

// Usage:
<ErrorBoundary FallbackComponent={FeatureErrorFallback} onReset={() => queryClient.clear()}>
  <DashboardFeature />
</ErrorBoundary>

Error Handling Checklist

  • [ ] App-level error boundary wrapping entire app
  • [ ] Feature-level boundaries for each major feature
  • [ ] API errors handled in TanStack Query's onError / error states
  • [ ] Form validation errors shown inline (not alerts)
  • [ ] 404 page for unknown routes
  • [ ] Offline detection and graceful degradation
  • [ ] Error reporting to monitoring (Sentry, etc.)
  • [ ] User-friendly error messages (no stack traces in production)

Phase 9: Forms & Validation

Form Library Decision

Library Best For Bundle Renders
React Hook Form Most forms 9 KB Minimal (uncontrolled)
Formik Simple forms 13 KB Every keystroke
TanStack Form Type-safe complex 5 KB Controlled
Native 1-2 field forms 0 KB You control

Default recommendation: React Hook Form + Zod

Form Pattern

const schema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'Min 8 characters'),
  role: z.enum(['admin', 'user']),
})
type FormData = z.infer<typeof schema>

export function LoginForm({ onSubmit }: { onSubmit: (data: FormData) => void }) {
  const form = useForm<FormData>({
    resolver: zodResolver(schema),
    defaultValues: { email: '', password: '', role: 'user' },
  })

  return (
    <form onSubmit={form.handleSubmit(onSubmit)} noValidate>
      <label htmlFor="email">Email</label>
      <input id="email" type="email" {...form.register('email')} aria-invalid={!!form.formState.errors.email} />
      {form.formState.errors.email && (
        <p role="alert">{form.formState.errors.email.message}</p>
      )}
      {/* ... more fields */}
      <button type="submit" disabled={form.formState.isSubmitting}>
        {form.formState.isSubmitting ? 'Signing in...' : 'Sign in'}
      </button>
    </form>
  )
}

Phase 10: Testing Strategy

Test Pyramid for React

Level Tool Coverage Target What to Test
Unit Vitest 80% business logic Hooks, utilities, reducers
Component Testing Library Key user flows Rendering, interactions, a11y
Integration Testing Library Feature flows Multi-component workflows
E2E Playwright Critical paths Auth, checkout, core flows
Visual Chromatic/Percy UI components Regression detection

Testing Patterns

// Component test (Testing Library philosophy: test behavior, not implementation)
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

describe('UserCard', () => {
  it('calls onEdit when edit button clicked', async () => {
    const user = userEvent.setup()
    const onEdit = vi.fn()
    render(<UserCard user={mockUser} onEdit={onEdit} />)

    await user.click(screen.getByRole('button', { name: /edit/i }))
    expect(onEdit).toHaveBeenCalledWith(mockUser.id)
  })

  it('does not render edit button when onEdit not provided', () => {
    render(<UserCard user={mockUser} />)
    expect(screen.queryByRole('button', { name: /edit/i })).not.toBeInTheDocument()
  })
})

7 Testing Rules

  1. Test behavior, not implementation — never test state directly or useEffect
  2. Use accessible queriesgetByRole > getByTestId > getByText
  3. User events over fireEventuserEvent.click simulates real interaction
  4. One assertion per concept — not one per test, but focused assertions
  5. Mock at boundaries — API calls, not internal functions
  6. No snapshot tests — they break on every change and test nothing meaningful
  7. Arrange-Act-Assert — clear structure in every test

Phase 11: Accessibility (a11y)

10-Point Accessibility Checklist

  1. Semantic HTML<button> not <div onClick>, <nav> not <div class="nav">
  2. Keyboard navigation — every interactive element reachable via Tab, operable via Enter/Space
  3. Focus management — visible focus indicator, logical tab order, focus trap in modals
  4. Alt text — every <img> has descriptive alt (or alt="" if decorative)
  5. Color contrast — 4.5:1 for normal text, 3:1 for large text (WCAG AA)
  6. ARIA labelsaria-label for icon-only buttons, aria-describedby for hints
  7. Live regionsaria-live="polite" for dynamic content (toasts, form errors)
  8. Reduced motion — respect prefers-reduced-motion for animations
  9. Screen reader testing — test with VoiceOver (Mac) or NVDA (Windows)
  10. Automated scanning — axe-core in CI (vitest-axe or @axe-core/playwright)

Phase 12: Production Deployment Checklist

Mandatory (P0)

  • [ ] TypeScript strict mode, zero errors
  • [ ] All tests passing
  • [ ] Bundle analyzed, no unexpected large dependencies
  • [ ] Error boundaries at app and feature level
  • [ ] Environment variables validated at build time
  • [ ] Security headers configured (CSP, HSTS, X-Frame-Options)
  • [ ] SEO meta tags (title, description, OG tags)
  • [ ] Analytics/error monitoring integrated
  • [ ] Performance budget met (LCP < 2.5s)

Recommended (P1)

  • [ ] Storybook for component library
  • [ ] Visual regression tests
  • [ ] a11y automated checks in CI
  • [ ] Feature flags for risky features
  • [ ] Preview deployments for PRs
  • [ ] Bundle size CI check (fail if +10%)

Recommended Stack (2025+)

Layer Recommendation Alternative
Framework Next.js 15 Remix, Vite SPA
Language TypeScript (strict)
Styling Tailwind CSS v4 CSS Modules
Components shadcn/ui Radix, Headless UI
State (server) TanStack Query v5 SWR
State (client) Zustand Jotai
Forms React Hook Form + Zod TanStack Form
Testing Vitest + Testing Library Jest
E2E Playwright Cypress
Linting Biome ESLint + Prettier
Auth Auth.js (NextAuth) Clerk, Lucia
Database Drizzle ORM Prisma
Deployment Vercel Cloudflare, Fly.io
Monitoring Sentry Datadog

Quality Scoring (0-100)

Dimension Weight What to Score
Architecture 20% Structure, separation, patterns
Type safety 15% Strict TS, zero any, Zod boundaries
Performance 15% Core Web Vitals, bundle size
Testing 15% Coverage, quality, pyramid
Accessibility 10% WCAG AA, keyboard, screen reader
State management 10% Right tool, no prop drilling
Error handling 10% Boundaries, user-friendly, monitoring
Developer experience 5% Linting, formatting, CI speed

Grading: 90+ World-class | 75-89 Production-ready | 60-74 Needs work | <60 Tech debt crisis


10 Common Mistakes

# Mistake Fix
1 useEffect for derived state Compute inline or useMemo
2 Prop drilling 5+ levels deep Context, Zustand, or composition
3 Fetching in useEffect TanStack Query or framework loaders
4 Default exports everywhere Named exports for refactoring safety
5 Testing implementation details Test behavior with Testing Library
6 Giant components (500+ lines) Extract hooks and sub-components
7 No error boundaries Add at app, feature, and widget level
8 Redux for server state TanStack Query for API data
9 Ignoring a11y until the end Build accessible from day 1
10 No TypeScript strict mode Enable strict, fix all errors

Natural Language Commands

  • "Set up a new React project" → Phase 1-2 architecture + structure
  • "Review my component" → Phase 3 rules + quality scoring
  • "Help me choose state management" → Phase 4 decision tree
  • "Optimize performance" → Phase 7 priority stack + profiling
  • "Add error handling" → Phase 8 error boundary architecture
  • "Build a form" → Phase 9 React Hook Form + Zod pattern
  • "Write tests for this component" → Phase 10 testing patterns
  • "Check accessibility" → Phase 11 checklist
  • "Prepare for production" → Phase 12 deployment checklist
  • "Audit my React app" → Full quality scoring across all phases
  • "Migrate from class components" → Modern patterns + hooks
  • "Upgrade to React 19" → Compiler, Server Components, Actions

⚡ Level Up Your React Development

This skill gives you the methodology. For industry-specific implementation patterns, grab an AfrexAI Context Pack ($47):

  • SaaS Context Pack — SaaS-specific React patterns, billing UI, dashboard architecture
  • Fintech Context Pack — Financial UI patterns, real-time data, compliance
  • Healthcare Context Pack — HIPAA-compliant UI, patient data handling

👉 Browse all 10 packs: https://afrexai-cto.github.io/context-packs/

🔗 More Free Skills by AfrexAI

  • afrexai-nextjs-production — Next.js production engineering
  • afrexai-vibe-coding — AI-assisted development methodology
  • afrexai-technical-seo — SEO for React SPAs and SSR
  • afrexai-test-automation-engineering — Complete testing strategy
  • afrexai-ui-design-system — Design system architecture

同梱ファイル

※ ZIPに含まれるファイル一覧。`SKILL.md` 本体に加え、参考資料・サンプル・スクリプトが入っている場合があります。