Component patterns, hooks, state management, and performance optimization for React applications
npx skills add https://github.com/cosmicstack-labs/mercury-agent-skills --skill react-patterns
Build React applications that are composable, performant, and maintainable.
Build small, focused components and compose them. Avoid giant components with many configuration props. Children and slots are your friends.
Keep state as close to where it's used as possible. Lift state up only when truly shared. Context is not a state management solution.
Derived state (computed from existing state) should never be stored separately. Use useMemo for expensive derivations, compute inline for cheap ones.
useEffect is for synchronizing with external systems (API, DOM, subscriptions). Don't use it for derived state or event handlers.
| Level | Components | State | Effects | Performance |
|-------|------------|-------|---------|-------------|
| 1: Basic | Class components, mix of concerns | Local state only | Raw useEffects | Not considered |
| 2: Foundational | Functional components, some hooks | Lifting state, some context | Cleanup in effects | React.memo basics |
| 3: Proficient | Compound components, custom hooks | useReducer, Zustand/Context | Custom hooks encapsulate effects | useMemo, useCallback |
| 4: Advanced | Render props, slots, polymorphic | Server state (TanStack Query) | Controlled side effects | Code splitting, virtualization |
| 5: Expert | Headless UI, state machines | Zustand + server state + URL | xState or custom event bus | Concurrent features, streaming SSR |
Target: Level 3+ for production apps. Level 4 for complex applications.
// Expose related components that share implicit state
<Select value={selected} onChange={setSelected}>
<Select.Option value="1">Option 1</Select.Option>
<Select.Option value="2">Option 2</Select.Option>
<Select.Option value="3">Option 3</Select.Option>
</Select>
When to use: Complex UI widgets (selects, tabs, accordions, menus) where children share state.
Implementation:
const SelectContext = createContext<SelectContextType | null>(null);
function Select({ children, value, onChange }: SelectProps) {
return (
<SelectContext.Provider value={{ value, onChange }}>
<select className="select">{children}</select>
</SelectContext.Provider>
);
}
Select.Option = function Option({ value, children }: OptionProps) {
const ctx = useContext(SelectContext);
if (!ctx) throw new Error('Option must be inside Select');
return (
<option
value={value}
selected={ctx.value === value}
onClick={() => ctx.onChange(value)}
>
{children}
</option>
);
};
Extract complex logic into reusable hooks. Hooks are your primary code reuse mechanism.
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchResults({ query }: { query: string }) {
const debouncedQuery = useDebounce(query, 300);
const { data } = useQuery(['search', debouncedQuery], fetchResults);
// ...
}
Let consumers control rendering while you control behavior.
function DataList<T>({
items,
renderItem,
renderEmpty,
}: {
items: T[];
renderItem: (item: T, index: number) => ReactNode;
renderEmpty?: () => ReactNode;
}) {
if (items.length === 0) {
return renderEmpty?.() ?? <p>No items found.</p>;
}
return <ul>{items.map((item, i) => <li key={i}>{renderItem(item, i)}</li>)}</ul>;
}
Best for: UI state, form inputs, toggle flags, single-component data.
const [isOpen, setIsOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
Best for: State with multiple sub-values, complex update logic, dependent state transitions.
type State = { count: number; step: number };
type Action = { type: 'increment' } | { type: 'decrement' } | { type: 'setStep'; step: number };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment': return { ...state, count: state.count + state.step };
case 'decrement': return { ...state, count: state.count - state.step };
case 'setStep': return { ...state, step: action.step };
}
}
Best for: API data, caching, refetching, optimistic updates.
function UserProfile({ userId }: { userId: string }) {
const { data, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
});
if (isLoading) return <Skeleton />;
if (error) return <ErrorMessage error={error} />;
return <UserCard user={data} />;
}
Best for: Global UI state (theme, sidebar, auth), cross-component state.
import { create } from 'zustand';
const useStore = create((set) => ({
theme: 'light',
sidebar: 'open',
toggleTheme: () => set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light'
})),
}));
| Tool | Use When | Don't Use When |
|------|----------|----------------|
| React.memo | Component renders often with same props | Props change every render anyway |
| useMemo | Expensive calculations (>1ms) | Simple arithmetic or access patterns |
| useCallback | Passing stable callbacks to memo'd children | Passing to DOM elements |
// Good: useMemo for expensive computation
const sortedItems = useMemo(
() => items.sort((a, b) => expensiveCompare(a, b)),
[items]
);
// Overkill: useMemo for trivial computation
const total = useMemo(() => items.length + 1, [items]); // Just compute inline
// Good: useCallback for stable function reference
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // No dependencies — stable forever
import { lazy, Suspense } from 'react';
const AdminPanel = lazy(() => import('./AdminPanel'));
function App() {
return (
<Suspense fallback={<LoadingSkeleton />}>
{isAdmin ? <AdminPanel /> : <UserPanel />}
</Suspense>
);
}
For long lists (1000+ items), use react-window or @tanstack/virtual:
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }: { items: Item[] }) {
const parentRef = useRef(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize() }}>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div key={virtualItem.key} style={{
position: 'absolute',
top: 0,
transform: `translateY(${virtualItem.start}px)`,
}}>
{items[virtualItem.index].name}
</div>
))}
</div>
</div>
);
}
| Anti-Pattern | Problem | Fix |
|--------------|---------|-----|
| Big useEffect | Single effect doing multiple, unrelated things | Split into multiple effects |
| State as derived data | Storing computed values in state | Use useMemo or compute inline |
| Prop drilling | Passing props through 5+ layers | Use composition or context (sparingly) |
| Context for everything | Re-rendering entire tree on any state change | Split contexts, use Zustand/Jotai |
| Inline functions in render | Breaking React.memo optimization | Move to stable references with useCallback |
| Over-optimization | Memoizing everything prematurely | Profile first, optimize second |
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification
Take cosmicstack-labs/react-patterns from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.