Skip to content

Implement react-dev: Opinionated React development #13

@vredchenko

Description

@vredchenko

Summary

The react-dev plugin enforces opinionated React conventions: functional components only, TypeScript throughout, Vitest + Testing Library for tests, and Tailwind CSS for styling. It provides component/hook scaffolding that follows these patterns consistently. This is valuable because Claude Code generates inconsistent React code without explicit guidance — sometimes class components, sometimes inline styles, sometimes different test frameworks.

Original Intent

Plugin to guide React web development, preferred dependencies and conventions.

Commands

/react-dev:component

Purpose: Generate a new React component following project conventions.

Behavior:

  1. Ask user for:
    • Component name (PascalCase)
    • Component type: page, layout, UI component, form, modal/dialog
    • Location hint (or auto-detect from existing component paths)
  2. Detect project conventions by scanning existing components:
    • Directory structure: components/<Name>/ (directory) vs components/<Name>.tsx (flat)
    • Styling: Tailwind, CSS Modules, styled-components, or plain CSS
    • State management: React context, Zustand, Jotai, Redux
    • Export style: named vs default
  3. Determine target path:
    • If src/components/ exists → use it
    • If app/ (Next.js App Router) → use app/ for pages, components/ for UI
    • If pages/ (Next.js Pages Router) → use pages/ for pages, components/ for UI
  4. Generate component file:
    // src/components/UserProfile/UserProfile.tsx
    
    interface UserProfileProps {
      userId: string;
      onEdit?: () => void;
    }
    
    export function UserProfile({ userId, onEdit }: UserProfileProps) {
      return (
        <div className="...">
          {/* component content */}
        </div>
      );
    }
    Conventions enforced:
    • Always functional components (never class)
    • Always TypeScript with explicit Props interface
    • Always named export (not default, unless Next.js page)
    • Props destructured in parameter
    • No React.FC — use plain function with typed props
  5. Generate associated files based on project patterns:
    • Test file: UserProfile.test.tsx with Testing Library
    • Story file: UserProfile.stories.tsx (if Storybook detected)
    • Index file: index.ts barrel export (if directory-based structure)
  6. Test file template:
    import { render, screen } from "@testing-library/react";
    import { describe, test, expect } from "vitest";
    import { UserProfile } from "./UserProfile";
    
    describe("UserProfile", () => {
      test("renders without crashing", () => {
        render(<UserProfile userId="123" />);
        // Add assertions based on component content
      });
    });
  7. Output: list of created files

Edge cases:

  • Component already exists → warn and offer to overwrite or pick new name
  • No React detected in project → error with suggestion to install React first
  • Next.js vs plain React vs Remix → adjust conventions accordingly

/react-dev:hook

Purpose: Generate a custom React hook following best practices.

Behavior:

  1. Ask user for:
    • Hook name (must start with use)
    • Purpose description
    • What it returns (data, loading state, error, functions)
  2. Detect where hooks live:
    • src/hooks/ (most common)
    • src/lib/hooks/ (Next.js convention)
    • Colocated with feature (src/features/<feature>/hooks/)
  3. Generate hook file:
    // src/hooks/useDebounce.ts
    
    import { useState, useEffect } from "react";
    
    interface UseDebounceOptions {
      delay?: number;
    }
    
    export function useDebounce<T>(value: T, options: UseDebounceOptions = {}) {
      const { delay = 300 } = options;
      const [debouncedValue, setDebouncedValue] = useState(value);
    
      useEffect(() => {
        const timer = setTimeout(() => setDebouncedValue(value), delay);
        return () => clearTimeout(timer);
      }, [value, delay]);
    
      return debouncedValue;
    }
    Conventions enforced:
    • Name starts with use
    • Typed with generics where appropriate
    • Options object pattern for configuration (not positional args)
    • JSDoc with @example usage
  4. Generate test file:
    import { renderHook, act } from "@testing-library/react";
    import { describe, test, expect, vi } from "vitest";
    import { useDebounce } from "./useDebounce";
    
    describe("useDebounce", () => {
      test("returns initial value immediately", () => {
        const { result } = renderHook(() => useDebounce("hello"));
        expect(result.current).toBe("hello");
      });
      
      test("debounces value changes", async () => {
        vi.useFakeTimers();
        // ...
      });
    });
  5. Output: list of created files

Common hook patterns to suggest (based on purpose):

Purpose Pattern Key imports
API data fetching useQuery wrapper useState, useEffect
Form state useForm useState, useCallback
Local storage useLocalStorage useState, useEffect
Media query useMediaQuery useState, useEffect
Intersection observer useInView useRef, useEffect
Keyboard shortcut useHotkey useEffect
Previous value usePrevious useRef, useEffect

Edge cases:

  • Hook name doesn't start with use → auto-prefix and inform user
  • Complex return type → suggest returning an object (not tuple) for readability

/react-dev:test

Purpose: Run React component tests and identify coverage gaps.

Behavior:

  1. Detect test framework:
    • vitest in devDependencies → bunx vitest run or npx vitest run
    • jest in devDependencies → bunx jest or npx jest
    • Neither → suggest installing vitest
  2. Run tests:
    bunx vitest run --reporter=verbose
  3. Analyze results:
    • Show pass/fail summary
    • For failures: show component name, test name, assertion error, file:line
  4. Run coverage:
    bunx vitest run --coverage
  5. Identify untested components:
    • Scan src/components/**/*.tsx and src/hooks/**/*.ts
    • Check for corresponding *.test.tsx / *.test.ts
    • List untested files:
      Untested Components:
      - src/components/Header/Header.tsx
      - src/components/Sidebar/Sidebar.tsx
      
      Untested Hooks:
      - src/hooks/useAuth.ts
      
  6. Offer to generate test stubs for untested components

Testing best practices to include in command doc:

  • Query by role, label, or text (not test-id) — accessible selectors
  • userEvent over fireEvent for realistic interactions
  • Test behavior, not implementation details
  • Don't test implementation details (internal state, hooks directly)

Edge cases:

  • No tests exist → offer to scaffold test files for all components
  • Jest + Enzyme → suggest migration to Vitest + Testing Library
  • Tests reference DOM elements that don't exist → suggest checking component render

Hooks

None — this plugin operates through commands only.

File Manifest

File Est. Lines Purpose
commands/component.md 100-120 Generate React component
commands/hook.md 90-110 Generate React hook
commands/test.md 80-100 Run tests and check coverage
README.md 170-210 Full plugin documentation
.claude-plugin/plugin.json 15-20 Plugin manifest

README Outline

  1. Overview — Opinionated React: functional, TypeScript, Vitest, Tailwind

  2. Quick Start — Installation + generating first component

  3. Commands — Table with all 3 commands

  4. Conventions

    Aspect Convention Rationale
    Components Functional only Hooks supersede class lifecycle
    Language TypeScript Type safety, better DX
    Styling Tailwind CSS Utility-first, no CSS file sprawl
    Testing Vitest + Testing Library Fast, accessible queries
    Exports Named (not default) Better refactoring, grep-able
    Props Interface, not type Extensible, better error messages
    State Zustand or Context Simple, no boilerplate
  5. Component Patterns — Page, Layout, UI, Form, Modal templates

  6. Hook Patterns — Common custom hook patterns with examples

  7. Testing Guide — Query priority, userEvent, what to test

  8. Recommended Libraries

    Need Package Notes
    Routing react-router / Next.js Framework-dependent
    Forms react-hook-form Uncontrolled, performant
    Validation zod Runtime + TypeScript inference
    Data fetching @tanstack/react-query Caching, dedup, background refresh
    Animation framer-motion Declarative, layout animations
    Icons lucide-react Tree-shakeable, consistent
    UI components shadcn/ui Copy-paste, not dependency

Prerequisites

  • React 18+ project with TypeScript
  • vitest + @testing-library/react (plugin will suggest installation if missing)

Quality Checklist

  • Each command .md is 60+ lines with concrete steps
  • README is 100+ lines with examples and reference tables
  • Convention table explains rationale for each choice
  • Component and hook templates are production-ready
  • Plugin provides clear value beyond Claude's defaults (consistent conventions + testing)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions