Back to skills
SKILL.md
React Specialist
ASecurityUse when React 19+ specialist. use(), useActionState, useOptimistic, React Compiler, Server/Client Components, Zustand/Jotai, React Query. Use when building components, managing state, optimizing renders.
- 5 stars
- 0 votes
- 0 copies
- 0 views
- Added September 27, 2026
Works with
Security analysis
100/100npx -y skills add Harmitx7/tribunal-kit --skill react-specialist --agent claude-codeAre you the author of React Specialist?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/harmitx7-react-specialist-tribunal-kit)---
name: react-specialist
description: "Use when React 19+ specialist. use(), useActionState, useOptimistic, React Compiler, Server/Client Components, Zustand/Jotai, React Query. Use when building components, managing state, optimizing renders."
version: 5.0.0
last-updated: 2026-09-13
skills:
- nextjs-react-expert
- react-doctor
- baseline-ui
tools: Read, Grep, Glob, Bash, Edit, Write
scripts-binding:
- .agent/scripts/lint_runner.js
- .agent/scripts/verify_all.js
---
# React 19+ β Dense Reference
---
## π οΈ Technical Architecture & Reference Recipes
---
## 2026 React 19 Performance & Architecture Invariants
1. **Direct `ref` as a Prop**: Never use `forwardRef`. In React 19, `ref` is passed directly as a standard component prop.
2. **Server Actions & `useActionState`**: Prefer native form actions and `useActionState` over manual `onSubmit` event handlers with `preventDefault()`.
3. **Optimistic Updates**: Use `useOptimistic` for instant local feedback before network requests settle.
4. **Transition-Aware State Updates**: Wrap non-urgent state updates in `startTransition` to keep main thread interactions responsive.
5. **No Context Provider Wrapper Boilerplate**: In React 19, `<ThemeContext value={theme}>` replaces `<ThemeContext.Provider value={theme}>`.
## Hallucination Traps (Read First)
- β `forwardRef((props, ref) => ...)` β β
In React 19, accept `ref` directly as a component prop
- β `<ThemeContext.Provider value={...}>` β β
In React 19, use `<ThemeContext value={...}>`
- β `useFormState()` β β
`useActionState()` (stable API)
- β `useContext()` inside `if` conditionals β β
`use(Context)` CAN be called conditionally
- β Manual `useMemo`/`useCallback` everywhere β β
Let React Compiler handle memoization automatically
- β Destructuring whole store `const { user } = useStore()` β β
Granular selector `useStore(s => s.user)`
- β `next/router` β β
`next/navigation` in App Router
---
## React 19 Core APIs
### `use()` β Replaces many useEffect patterns
```tsx
import { use } from 'react';
// Reads promises (suspends until resolved)
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise); // suspends
return <h1>{user.name}</h1>;
}
// Reads context β CAN be called conditionally (unlike useContext)
function Admin({ isAdmin }: { isAdmin: boolean }) {
if (isAdmin) return <Panel theme={use(ThemeContext)} />;
return <PublicPanel />;
}
```
### `useActionState` β Form actions with state
```tsx
import { useActionState } from 'react'; // NOT useFormState
async function submitForm(prevState: FormState, formData: FormData) {
const email = formData.get('email') as string;
if (!email.includes('@')) return { error: 'Invalid email' };
await saveToDatabase(email);
return { error: null, success: true };
}
function SignupForm() {
const [state, formAction, isPending] = useActionState(submitForm, { error: null });
return (
<form action={formAction}>
<input name="email" disabled={isPending} />
{state.error && <p>{state.error}</p>}
<button disabled={isPending}>{isPending ? 'Saving...' : 'Submit'}</button>
</form>
);
}
```
### `useOptimistic` β Instant UI feedback
```tsx
import { useOptimistic } from 'react';
function TodoList({ todos }: { todos: Todo[] }) {
const [optimistic, addOptimistic] = useOptimistic(todos, (current, newTodo: Todo) => [
...current,
newTodo,
]);
async function handleAdd(formData: FormData) {
addOptimistic({ id: crypto.randomUUID(), title: formData.get('title'), pending: true });
await saveTodo(formData.get('title') as string);
}
return (
<form action={handleAdd}>
{optimistic.map(t => (
<li key={t.id} style={{ opacity: t.pending ? 0.5 : 1 }}>
{t.title}
</li>
))}
</form>
);
}
```
### `useFormStatus` β Button pending state (must be INSIDE `<form>`)
```tsx
import { useFormStatus } from 'react-dom';
// β TRAP: Cannot be called in the same component as <form>
function SubmitButton() {
const { pending } = useFormStatus(); // reads nearest parent form
return <button disabled={pending}>{pending ? 'Saving...' : 'Save'}</button>;
}
```
### `useTransition` β Non-blocking updates (React 19: supports async)
```tsx
const [isPending, startTransition] = useTransition();
startTransition(async () => {
const data = await search(query); // async supported in React 19 only
setResults(data);
});
```
### `useDeferredValue` β Stale-while-rerender
```tsx
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<div style={{ opacity: isStale ? 0.6 : 1 }}>
<ExpensiveList query={deferredQuery} />
</div>
);
// React 19: useDeferredValue(value, initialFallback) β 2-arg form
```
---
## React Compiler (React 19)
- Auto-memoizes components/values/callbacks. **Don't manually memoize in React 19 projects.**
- β `useMemo(() => calc(a), [a])` / `useCallback(fn, [id])` / `React.memo(Comp)` β legacy
- β
Write plain functions/values. Compiler optimizes automatically.
- Exception: still use manual memo if compiler is explicitly disabled in config.
---
## Component Patterns
### Compound Components (shared state via context)
```tsx
const TabsContext = createContext<{ active: string; setActive: (id: string) => void } | null>(null);
function Tabs({ children, defaultTab }: { children: ReactNode; defaultTab: string }) {
const [active, setActive] = useState(defaultTab);
return (
<TabsContext value={{ active, setActive }}>
<div>{children}</div>
</TabsContext>
);
}
function Tab({ id, children }: { id: string; children: ReactNode }) {
const ctx = use(TabsContext)!;
return (
<button onClick={() => ctx.setActive(id)} aria-selected={ctx.active === id}>
{children}
</button>
);
}
```
### Render Props / Higher Order Hooks
Prefer custom hooks over render props for modern React.
### Context Performance Pattern
```tsx
// Split context β prevents all consumers from re-rendering on every change
const CountStateCtx = createContext<number>(0);
const CountDispatchCtx = createContext<Dispatch<Action>>(() => {});
```
---
## State Management
### Zustand (preferred for global state)
```tsx
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useStore = create<Store>()(
persist(
(set, get) => ({
count: 0,
increment: () => set(s => ({ count: s.count + 1 })),
getDoubled: () => get().count * 2,
}),
{ name: 'my-store' },
),
);
// β TRAP: Do NOT destructure the whole store β causes re-render on every change
// β
const count = useStore(s => s.count); // selector
```
### Jotai (preferred for derived/atomic state)
```tsx
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
const countAtom = atom(0);
const doubledAtom = atom(get => get(countAtom) * 2); // derived atom
// β TRAP: atomWithStorage is from jotai/utils β NOT from jotai
import { atomWithStorage } from 'jotai/utils';
```
### React Query / TanStack Query
```tsx
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
const { data, isPending, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
staleTime: 5 * 60 * 1000, // don't refetch for 5 minutes
});
// Optimistic update:
const qc = useQueryClient();
const mutation = useMutation({
mutationFn: updateUser,
onMutate: async newUser => {
await qc.cancelQueries({ queryKey: ['user', newUser.id] });
const prev = qc.getQueryData(['user', newUser.id]);
qc.setQueryData(['user', newUser.id], newUser);
return { prev };
},
onError: (_, __, ctx) => qc.setQueryData(['user'], ctx?.prev),
onSettled: () => qc.invalidateQueries({ queryKey: ['user'] }),
});
```
---
## Performance
| Technique | When |
| -------------------------------------- | ------------------------------------------ |
| `useDeferredValue` | Expensive derived render (charts, filters) |
| `useTransition` | Page nav, tab switches, data load |
| `lazy()` + `Suspense` | Code-split heavy components |
| `<Virtuoso>` / `<WindowVirtualizer>` | Lists > 200 items |
| Avoid `useEffect` for state transforms | Use `useMemo` or derived atoms |
---
## Refs & DOM
```tsx
// React 19: ref is now a prop (no forwardRef needed)
function Input({ ref, ...props }: ComponentProps<'input'> & { ref?: Ref<HTMLInputElement> }) {
return <input ref={ref} {...props} />;
}
// β TRAP: forwardRef is deprecated in React 19 β still works but not needed
```
---
## Testing Checklist
- β
Use React Testing Library β test behavior, not implementation
- β
`userEvent` over `fireEvent` (async, closer to real interaction)
- β
Mock server calls with MSW (Mock Service Worker)
- β Never test internal state, ref values, or component instances
Attribution
Comments
Loading commentsβ¦