React patterns
Skill halflength-ampleness75/claude-code-recipes/skills/react-patterns
Provide ready-to-use Claude Code commands, subagents, hooks, skills, and configs to simplify setup and speed up development.
npx -y skills add halflength-ampleness75/claude-code-recipes --skill react-patternsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.5 KB, as published. Nobody here has run it
React Patterns
Best practices for React development: functional components, hooks, state management, performance, and accessibility.
Component Rules
- Always use functional components — never class components for new code
- One component per file — name the file the same as the component (PascalCase)
- Export components as named exports — default exports only for pages/routes
- Colocate related files — keep
Component.tsx,Component.test.tsx, andComponent.module.csstogether - Props interface above the component — name it
ComponentNameProps
// Good
interface UserCardProps {
user: User;
onSelect: (id: string) => void;
variant?: "compact" | "full";
}
export function UserCard({ user, onSelect, variant = "full" }: UserCardProps) {
return ( /* ... */ );
}
// Bad — default export, inline props, class component
export default class UserCard extends React.Component<{user: any}> { /* ... */ }
Hooks Patterns
Custom Hooks
- Prefix with
use—useAuth,useDebounce,useLocalStorage - Extract shared logic into custom hooks — if two components share stateful logic, extract it
- Return tuples for simple hooks, objects for complex ones
// Simple: return tuple
function useToggle(initial = false): [boolean, () => void] {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue((v) => !v), []);
return [value, toggle];
}
// Complex: return object
function useApi<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
const [loading, setLoading] = useState(true);
// ... fetch logic
return { data, error, loading, refetch };
}
Hook Rules
- Never call hooks conditionally — all hooks must run on every render
- Use
useCallbackfor functions passed to children — prevents unnecessary re-renders - Use
useMemoonly for expensive computations — don't wrap everything - Prefer
useReduceroveruseStatewhen state transitions are complex
useEffect Guidelines
- Always specify dependencies — never use
// eslint-disable-next-line - Return a cleanup function for subscriptions, timers, and listeners
- Avoid setting state in useEffect when you can derive it — computed values don't need effects
// Bad — unnecessary effect
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
// Good — derived value
const fullName = `${firstName} ${lastName}`;
State Management Tiers
Use the simplest tier that solves the problem:
| Tier | Tool | When to Use |
|---|---|---|
| 1. Local state | useState, useReducer | Single component state |
| 2. Lifted state | Props, composition | Shared between parent/child |
| 3. Context | createContext + useContext | Theme, auth, locale — rarely changes |
| 4. URL state | Search params, path params | Filters, pagination, navigation state |
| 5. Server state | React Query / SWR | API data, caching, synchronization |
| 6. Global store | Zustand / Redux Toolkit | Complex client state across many components |
Context Guidelines
- Split contexts by domain —
AuthContext,ThemeContext, notAppContext - Keep context values stable — use
useMemoon the provider value - Don't put frequently changing values in context — it re-renders all consumers
// Good — stable context value
function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const value = useMemo(() => ({ user, setUser }), [user]);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
Performance
- Use React.lazy + Suspense for route-level code splitting
- Memoize expensive list items with
React.memo— include a custom comparator if props are objects - Virtualize long lists — use
react-windowor@tanstack/virtualfor 100+ items - Avoid anonymous functions in JSX when passing to memoized children
- Use
keycorrectly — stable, unique identifiers, never array index for dynamic lists
// Good — code splitting
const Dashboard = lazy(() => import("./pages/Dashboard"));
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
);
}
Accessibility
- Use semantic HTML —
<button>not<div onClick>,<nav>not<div className="nav"> - All images need alt text — empty
alt=""for decorative images - Form inputs need labels — use
<label htmlFor>oraria-label - Manage focus on route changes — focus the main heading after navigation
- Support keyboard navigation — all interactive elements must be reachable via Tab
- Use ARIA attributes when semantic HTML is insufficient —
aria-expanded,aria-live,role
// Good — accessible modal
function Modal({ isOpen, onClose, title, children }: ModalProps) {
const closeRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (isOpen) closeRef.current?.focus();
}, [isOpen]);
if (!isOpen) return null;
return (
<div role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">{title}</h2>
{children}
<button ref={closeRef} onClick={onClose}>
Close
</button>
</div>
);
}
Error Boundaries
- Wrap route-level components in error boundaries
- Provide a meaningful fallback UI — not just "Something went wrong"
- Log errors to your monitoring service in the boundary
import { ErrorBoundary } from "react-error-boundary";
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<div role="alert">
<h2>Something went wrong</h2>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
// Usage
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Dashboard />
</ErrorBoundary>
Anti-patterns
- Prop drilling more than 2 levels — use composition or context instead
- Giant useEffect blocks — split into multiple focused effects
- Storing derived state — compute it during render
- Premature optimization — profile before adding
useMemo/useCallbackeverywhere - String-based refs — use
useRefonly - Direct DOM manipulation — use refs only when React APIs are insufficient