.claude/skills/react-expert/SKILL.md
React ecosystem expert including hooks, state management, component patterns, React 19 features, Shadcn UI, and Radix primitives
npx skillsauth add oimiragieo/agent-studio react-expertInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
useMemo/useCallback where the compiler can infer themReact.memo, useMemo, useCallback when the compiler cannot help (complex object identity, external deps, stable callback refs for third-party libraries)React.lazy and SuspenseReact.memo, useMemo, useCallback wrappers — the compiler handles them// @no-react-compiler pragma to a component/file when manual control is neededuseMemo/useCallback for: stable refs passed to third-party libs, expensive computations with external deps the compiler cannot seeaction props for automatic pending/error state managementonSubmit + manual loading/error state boilerplate pattern// Form Action pattern (React 19)
async function saveUser(formData: FormData) {
'use server'; // only in RSC frameworks; omit for client Actions
await db.users.update({ name: formData.get('name') });
}
<form action={saveUser}>
<input name="name" />
<button type="submit">Save</button>
</form>;
useActionState to track the result and pending state of a form Actionconst [state, formAction, isPending] = useActionState(fn, initialState)isPending replaces the manual useState(false) loading flag patternstate holds the return value of the last action invocation (success data or error)import { useActionState } from 'react';
async function submitAction(prevState: State, formData: FormData) {
const result = await saveData(formData);
return result.error ? { error: result.error } : { success: true };
}
function MyForm() {
const [state, formAction, isPending] = useActionState(submitAction, null);
return (
<form action={formAction}>
{state?.error && <p>{state.error}</p>}
<button disabled={isPending}>{isPending ? 'Saving...' : 'Save'}</button>
</form>
);
}
useOptimistic for instant UI feedback before a server response arrivesconst [optimisticState, setOptimistic] = useOptimistic(value, reducer?)import { useOptimistic } from 'react';
function ItemList({ items, addItem }: Props) {
const [optimisticItems, addOptimistic] = useOptimistic(items, (state, newItem) => [
...state,
{ ...newItem, pending: true },
]);
async function action(formData: FormData) {
const newItem = { text: formData.get('text') as string, id: crypto.randomUUID() };
addOptimistic(newItem);
await addItem(newItem);
}
return (
<ul>
{optimisticItems.map(item => (
<li key={item.id} className={item.pending ? 'opacity-50' : ''}>
{item.text}
</li>
))}
<form action={action}>
<input name="text" />
<button>Add</button>
</form>
</ul>
);
}
use(promise) — read a Promise's resolved value during render (integrates with Suspense/ErrorBoundary)use(Context) — replaces useContext; can be called conditionally (unlike other hooks)useEffect, use(promise) does not create a new Promise on each render; pass a stable promiseuse(Context) when you need context conditionally or inside loopsimport { use } from 'react';
// Reading context conditionally (not possible with useContext)
function Component({ show }: { show: boolean }) {
if (!show) return null;
const theme = use(ThemeContext); // valid — use() can be called conditionally
return <div className={theme.bg}>...</div>;
}
// Reading a promise (wrap in Suspense + ErrorBoundary)
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise); // suspends until resolved
return <p>{user.name}</p>;
}
ref is now a plain prop — no forwardRef wrapper needed (function Input({ ref }) { ... })useFormStatus — read the pending/error state of the nearest parent <form> Action<title>, <meta>, <link> anywhere in the component tree; React hoists them to <head>startTransition supports async functions (Transitions) in React 19useDeferredValue now accepts an initialValue parameter for SSR hydrationuseId stable for server components; use for accessibility IDs (label htmlFor / aria-labelledby)RSC is an architectural boundary, not an optimization toggle. Understand the split before placing components.
useState, no useEffect, no event handlers, no browser APIs — renders on server only, zero client JS shipped'use client' directive): interactive, uses hooks, event handlers, browser APIs — hydrates in browser'use client' at the top of the file; all imports below that boundary are also client-sideasync/await — no useEffect, no loading state boilerplateSuspense boundaries to stream Server Component output progressively// Server Component — fetch directly, no useEffect
async function UserCard({ userId }: { userId: string }) {
const user = await db.users.findById(userId); // direct DB / API call
return <div>{user.name}</div>;
}
// Client Component — interactive leaf
('use client');
function LikeButton({ postId }: { postId: string }) {
const [liked, setLiked] = useState(false);
return <button onClick={() => setLiked(l => !l)}>{liked ? 'Unlike' : 'Like'}</button>;
}
children props instead// WRONG: importing a Server Component inside a Client Component
'use client'
import { ServerComp } from './ServerComp' // breaks — ServerComp would be bundled client-side
// CORRECT: pass as children prop
'use client'
function ClientShell({ children }: { children: React.ReactNode }) {
return <div onClick={...}>{children}</div>
}
// In a Server Component parent:
<ClientShell><ServerComp /></ClientShell>
revalidatePath / revalidateTag in Server Actions to bust cache after mutationscache() from React to deduplicate fetches within a single render pass{ cache: 'no-store' } for user-specific or real-time dataaction prop on <form>) over manual onSubmit + useState loading boilerplateuseActionState to track pending, error, and result state from form ActionsuseFormStatus inside child components to read the enclosing form's pending stateuseOptimistic for instant feedback during async submissionsvalue + onChange) when fine-grained validation or character-level feedback is requiredhtmlFor, use aria-describedby for error messages, manage focus on errorexport function Button({ className, children }: ButtonProps) { return (
<div className={className}> {children} </div> ) } </template> <template name="action-form"> 'use client' import { useActionState } from 'react'type State = { error?: string; success?: boolean } | null
async function submitContact(prevState: State, formData: FormData): Promise<State> { try { // perform mutation return { success: true } } catch (err) { return { error: err instanceof Error ? err.message : 'Unknown error' } } }
export function ContactForm() { const [state, formAction, isPending] = useActionState(submitContact, null) return (
<form action={formAction}> {state?.error && <p role="alert">{state.error}</p>} {state?.success && <p>Saved successfully.</p>} {/* form fields */} <button type="submit" disabled={isPending}> {isPending ? 'Saving...' : 'Save'} </button> </form> ) } </template> <template name="hook-with-suspense"> // React 19: use() + Suspense pattern (preferred for data fetching) import { use, Suspense } from 'react'// Create the promise OUTSIDE the component (stable reference) function fetchUserProfile(): Promise<UserProfile> { return fetch('/api/users').then(r => r.json()) }
export function UserProfileDisplay({ dataPromise }: { dataPromise: Promise<UserProfile> }) { const data = use(dataPromise) // suspends until resolved return <div>{/render data/}</div> }
// Usage: <Suspense fallback={<Spinner />}><UserProfileDisplay dataPromise={fetchUserProfile()} /></Suspense> </template>
<template name="hook-classic"> // Classic hook pattern (for non-Suspense or client-only use cases) import { useState, useEffect } from 'react'interface UseUserDataResult { data: UserData | null loading: boolean error: Error | null }
export function useUserData(): UseUserDataResult { const [data, setData] = useState<UserData | null>(null) const [loading, setLoading] = useState(true) const [error, setError] = useState<Error | null>(null)
useEffect(() => { let cancelled = false async function load() { try { setLoading(true) const result = await fetch('/api/users').then(r => r.json()) if (!cancelled) setData(result) } catch (err) { if (!cancelled) setError(err instanceof Error ? err : new Error('Unknown error')) } finally { if (!cancelled) setLoading(false) } } load() return () => { cancelled = true } }, [])
return { data, loading, error } } </template>
useEffect for post-render effects or Server Components for async data fetching.'use client' components, the more JavaScript shipped to the browser.| Anti-Pattern | Why It Fails | Correct Approach |
| ------------------------------------------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Using class components in new code | Incompatible with React Compiler, Server Components, and concurrent features | Always use functional components with hooks |
| Calling hooks conditionally or in loops | Violates Rules of Hooks; React depends on call order stability across renders | Always call hooks at the top level; use conditions inside the hook body |
| Manual useMemo/useCallback everywhere | Premature optimization; adds noise and complexity without measurable benefit | Profile first; use React Compiler; only memoize when DevTools shows real re-render cost |
| Fetching data in useEffect | Causes request waterfalls, loading flicker, and race conditions | Use Server Components for async fetch; React Query for client-side caching |
| Marking large components as 'use client' | Bundles entire component tree including server data into client JS | Push 'use client' to small interactive leaf components; keep data components as Server |
Before starting:
cat .claude/context/memory/learnings.md
After completing: Record any new patterns or exceptions discovered.
ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.
tools
Comprehensive biosignal processing toolkit for analyzing physiological data including ECG, EEG, EDA, RSP, PPG, EMG, and EOG signals. Use this skill when processing cardiovascular signals, brain activity, electrodermal responses, respiratory patterns, muscle activity, or eye movements. Applicable for heart rate variability analysis, event-related potentials, complexity measures, autonomic nervous system assessment, psychophysiology research, and multi-modal physiological signal integration.
tools
Comprehensive toolkit for creating, analyzing, and visualizing complex networks and graphs in Python. Use when working with network/graph data structures, analyzing relationships between entities, computing graph algorithms (shortest paths, centrality, clustering), detecting communities, generating synthetic networks, or visualizing network topologies. Applicable to social networks, biological networks, transportation systems, citation networks, and any domain involving pairwise relationships.
data-ai
Molecular featurization for ML (100+ featurizers). ECFP, MACCS, descriptors, pretrained models (ChemBERTa), convert SMILES to features, for QSAR and molecular ML.
development
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.