React 19 patterns and React Compiler optimization guidance. Understand when to use manual memoization vs letting the compiler optimize, and leverage new hooks like useActionState and useFormStatus.
React 19 patterns and React Compiler optimization guidance for Next.js applications.
Reference Files:
- compiler-guide.md - React Compiler capabilities and optimization
- when-to-memoize.md - When manual memoization is still needed
- hooks.md - useActionState, useFormStatus patterns
- server-components.md - Server vs Client Components
This project has React Compiler enabled:
// next.config.ts
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true,
},
};
With React Compiler enabled, you can safely remove most manual memoization (useMemo, useCallback, React.memo).
// ✅ Good - Let compiler handle optimization
function Component({ items, sortBy }) {
const sorted = [...items].sort((a, b) => a[sortBy] - b[sortBy]);
return <List items={sorted} />;
}
// ❌ Unnecessary with compiler
function Component({ items, sortBy }) {
const sorted = useMemo(() => {
return [...items].sort((a, b) => a[sortBy] - b[sortBy]);
}, [items, sortBy]);
return <List items={sorted} />;
}
"use client";
import { useActionState } from "react";
import { submitForm } from "./actions";
function ContactForm() {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const result = await submitForm(formData);
if (!result.success) {
return result.error;
}
return null;
},
null
);
return (
<form action={submitAction}>
<input type="text" name="message" />
<SubmitButton />
{error && <p className="text-red-500">{error}</p>}
</form>
);
}
// ✅ useFormStatus must be in child component
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Sending..." : "Send"}
</button>
);
}
The React Compiler:
useMemo, useCallback, React.memoSee when-to-memoize.md for details. Key scenarios:
| Hook | Purpose | Key Caveat |
|---|---|---|
useActionState | Form state management | Accepts async action, returns [state, action, isPending] |
useFormStatus | Form submission status | Must be called from child component inside <form> |
useOptimistic | Optimistic UI updates | For instant feedback before server confirmation |
| Purpose | Location |
|---|---|
| Client components | features/*/components/*.tsx with "use client" |
| Server components | app/**/*.tsx, features/*/components/*.tsx (default) |
| Server actions | features/*/server/actions/*.ts |
The compiler is enabled in next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;
When working on this codebase:
# Type check
npm run type-check
# Dev server (compiler active)
npm run dev
# Production build (full optimization)
npm run build
server-actions - For implementing server actions with React 19 patternsstorybook-testing - For testing components with play functions