Help us improve
Share bugs, ideas, or general feedback.
Generates React 19 patterns for Server Components, Server Actions, useOptimistic, useActionState, useTransition, Suspense boundaries, and TypeScript in Next.js App Router apps. Useful for optimistic UI, concurrent rendering, and performance optimization.
npx claudepluginhub giuseppe-trisciuoglio/developer-kit --plugin developer-kit-typescriptHow this skill is triggered — by the user, by Claude, or both
Slash command
/developer-kit-typescript:react-patternsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
React 19 patterns for Next.js App Router, Server Actions, optimistic UI, and concurrent features. See Quick Reference for API summary and Examples for copy-paste patterns.
Covers React 19 patterns: use() hook, Server Components, Actions, useActionState, useOptimistic, and component composition for modern React apps.
Delivers React 19 patterns for Server Components, Actions, useOptimistic, useFormStatus, Suspense, concurrent features, React Compiler, and TypeScript components/forms. Use for modern React development.
React 18/19 patterns for hooks discipline, server/client component boundaries, Suspense, form actions, data fetching, state management, and accessibility-first composition. Use when writing or reviewing React components.
Share bugs, ideas, or general feedback.
React 19 patterns for Next.js App Router, Server Actions, optimistic UI, and concurrent features. See Quick Reference for API summary and Examples for copy-paste patterns.
useOptimistic or useTransitionuseReducer or custom hooks| Pattern | Hook / API | Use Case |
|---|---|---|
| Local state | useState | Simple component state |
| Complex state | useReducer | Multi-action state machines |
| Side effects | useEffect | Subscriptions, data fetching |
| Shared state | useContext / createContext | Cross-component data |
| DOM access | useRef | Focus, measurements, timers |
| Performance | useMemo / useCallback | Expensive computations |
| Non-urgent updates | useTransition | Search/filter on large lists |
| Defer expensive UI | useDeferredValue | Stale-while-updating |
| Read resources | use() (React 19) | Promises and context in render |
| Optimistic UI | useOptimistic (React 19) | Instant feedback on mutations |
| Form status | useFormStatus (React 19) | Pending state in child components |
| Form state | useActionState (React 19) | Server action results |
| Auto-memoization | React Compiler | Eliminates manual memo/callback |
// Server Component (default) — async, fetches data
async function ProductPage({ id }: { id: string }) {
const product = await db.product.findUnique({ where: { id } });
return (
<div>
<h1>{product.name}</h1>
<AddToCartButton productId={product.id} />
</div>
);
}
// Client Component — handles interactivity
'use client';
function AddToCartButton({ productId }: { productId: string }) {
const [isPending, startTransition] = useTransition();
const handleAdd = () => {
startTransition(async () => {
await addToCart(productId);
});
};
return (
<button onClick={handleAdd} disabled={isPending}>
{isPending ? 'Adding...' : 'Add to Cart'}
</button>
);
}
'use client';
import { useOptimistic } from 'react';
function TodoList({ todos, addTodo }: { todos: Todo[]; addTodo: (t: Todo) => Promise<void> }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo: Todo) => [...state, { ...newTodo, pending: true }]
);
const handleSubmit = async (formData: FormData) => {
const newTodo = { id: Date.now(), text: formData.get('text') as string };
addOptimisticTodo(newTodo); // Immediate UI update
await addTodo(newTodo); // Actual backend call
};
return (
<form action={handleSubmit}>
{optimisticTodos.map(todo => (
<div key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
{todo.text}
</div>
))}
<input type="text" name="text" />
<button type="submit">Add</button>
</form>
);
}
// app/actions.ts
'use server';
import { z } from 'zod';
import { revalidatePath } from 'next/cache';
const schema = z.object({
title: z.string().min(5),
content: z.string().min(10),
});
export async function createPost(prevState: any, formData: FormData) {
const parsed = schema.safeParse({
title: formData.get('title'),
content: formData.get('content'),
});
if (!parsed.success) {
return { errors: parsed.error.flatten().fieldErrors };
}
await db.post.create({ data: parsed.data });
revalidatePath('/posts');
return { success: true };
}
// app/blog/new/page.tsx
'use client';
import { useActionState } from 'react';
import { createPost } from '../actions';
export default function NewPostPage() {
const [state, formAction, pending] = useActionState(createPost, {});
return (
<form action={formAction}>
<input name="title" placeholder="Title" />
{state.errors?.title && <span>{state.errors.title[0]}</span>}
<textarea name="content" placeholder="Content" />
<button type="submit" disabled={pending}>
{pending ? 'Publishing...' : 'Publish'}
</button>
</form>
);
}
export function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() { setIsOnline(true); }
function handleOffline() { setIsOnline(false); }
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
function SearchableList({ items }: { items: Item[] }) {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const [filteredItems, setFilteredItems] = useState(items);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value);
startTransition(() => {
setFilteredItems(items.filter(i => i.name.toLowerCase().includes(e.target.value.toLowerCase())));
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <span>Filtering...</span>}
<ul>{filteredItems.map(i => <li key={i.id}>{i.name}</li>)}</ul>
</div>
);
}
'use client' only for: hooks, browser APIs, event handlersuseReducer for state with multiple related actionsuseMemo, useCallback, memouseMemo for expensive computations, useCallback for stable callbacksuseTransition for low-priority state updatesuse(promise) components in Suspense boundariesuseActionState for form-server action integration'use server' directive; always validate inputsuseEffect dependency arraysConsult these files for detailed patterns: