From dev-conventions
This skill should be used when the user is working with React components, JSX/TSX files, React hooks, TanStack Query, React Hook Form, Zustand, or files like ".tsx", "vite.config.*", "next.config.*". Provides React/TypeScript frontend development conventions including component design, state management, data fetching, forms, testing, and performance.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dev-conventions:react-conventionsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
| Layer | Technology |
| Layer | Technology |
|---|---|
| Runtime | React 18+ |
| Language | TypeScript (strict) |
| Styling | Tailwind CSS |
| Data Fetching | TanStack Query |
| Forms | React Hook Form + Zod |
| Routing | React Router v7 |
| State | Zustand (global client), React context (theme/auth) |
| Testing | Vitest + React Testing Library |
| Build | Vite or Next.js |
| Package Manager | pnpm |
React.ButtonHTMLAttributes<HTMLButtonElement>)variant: 'primary' | 'secondary') — avoid boolean prop proliferation<Card><CardHeader /><CardContent /></Card> over <Card title="..." />children: React.ReactNode for flexible contentuseState (or useReducer for complex transitions)useContextNever store server data in Zustand. TanStack Query handles caching, refetching, and invalidation.
useStore(s => s.field)persist middleware only when needed (preferences)set after await, not inside store creationuseMemo if expensive) — don't store derivable valuesuseQuery: queryKey includes all dependencies (filters, pagination, id)useMutation: onSuccess → queryClient.invalidateQueries()onMutate → set cache, onError → rollback, onSettled → invalidateisLoading for initial load, isFetching for background refetchqueryClient.prefetchQuery()useEffect for server data — use useQuery or route loaderuseForm<T>({ resolver: zodResolver(schema), defaultValues })type FormData = z.infer<typeof schema>formState.errors.field?.message with aria-invaliddisabled={isSubmitting}setError('fieldName', { message }) or setError('root', { message })useMutation (TanStack Query) with mutateAsync in onSubmitfallback UI with retry buttoncomponentDidCatch callbackgetByRole > getByLabelText > getByText > getByTestId (last resort)findBy* or waitFor(() => expect(...))userEvent (click, type), not fireEventvi.mock on API moduleshould {expected} when {condition}React.memo: only for expensive components with stable propsuseMemo: only for expensive computations with correct depsuseCallback: only when passing stable callback to memoized childlazy(() => import('./Page')) + <Suspense> for routes and heavy componentsaria-invalid, aria-describedby for form errorsgetByTestId as first choice (use accessible queries)memo/useMemo/useCallback everywhere without measurementuseEffect for server dataFor detailed patterns, consult:
references/patterns.md — Compound components, TanStack Query advanced, form patterns, code splitting, Tailwind design tokensnpx claudepluginhub kljajics/claude-conventions --plugin dev-conventionsGuides writing and modifying React components with modern patterns, TypeScript, hooks for state and effects, component composition, and pitfalls to avoid. Triggers on .jsx/.tsx files or React planning.
Provides modern React patterns for component design, hooks, composition, state management, performance, error handling, and TypeScript best practices. Useful for production-ready apps.
Guides modern React patterns: hooks, composition, performance, TypeScript best practices, state management selection, and React 19 features.