latest-nextjs
Latest React & Next.js Skill
Comprehensive knowledge of React and Next.js features from mid-2024 through 2025. This skill fills the gap between LLM training cutoffs and current knowledge, covering React 19.2 (October 2025), Next.js 16 (October 2025), and supporting ecosystem changes.
React 19.2 (Latest - October 2025)
Current Status
- Latest stable version: React 19.2 (released October 1, 2025)
- Previous updates: React 19.1 (March 2025 - refinement release)
- Initial release: React 19 (December 2024)
Key Improvements in 19.1-19.2
- No breaking changes from 19.0
- Performance optimizations
- Enhanced DevTools features
- 30-40% smaller JavaScript bundle sizes
- Faster TTFB (Time to First Byte)
React 19 Core Features
New Hooks
useOptimistic
Manages optimistic UI updates during async mutations:
import { useOptimistic } from "react";
function LikeButton({ postId, initialLiked }) {
const [optimisticLiked, addOptimistic] = useOptimistic(
initialLiked,
(state, newLiked) => !state
);
async function handleToggle() {
addOptimistic(!optimisticLiked);
await toggleLike(postId);
}
return (
<button onClick={handleToggle}>
{optimisticLiked ? "Unlike" : "Like"}
</button>
);
}
Use cases: Like buttons, todo lists, any UI showing predicted state during server updates.
useActionState
Connects forms to action functions and tracks response state:
import { useActionState } from "react";
const [state, formAction, isPending] = useActionState(
async (prevState, formData) => {
const email = formData.get("email");
await subscribe(email);
return { success: true, message: "Subscribed!" };
},
null
);
Replaces: Manual form state management, loading states, error handling.
useFormStatus
Accesses parent form status from nested components:
import { useFormStatus } from "react";
function SubmitButton() {
const { pending, data, method, action } = useFormStatus();
return (
<button disabled={pending}>{pending ? "Sending..." : "Submit"}</button>
);
}
Key use case: Submit buttons that need parent form state.
Enhanced useTransition
Now supports async functions with automatic state management:
const [isPending, startTransition] = useTransition();
startTransition(async () => {
// Automatic pending, error, and optimistic UI handling
await searchAPI(query);
});
New use Hook
Reads resources (Promises, Context) in render:
import { use } from "react";
// Read promises in Server Components
const data = use(fetchPromise);
// Read context
const theme = use(ThemeContext);
Use case: Streaming data in Server Components without useEffect.
Server Components (Stable)
Production-ready and used at scale by Meta (Facebook, Instagram):
// Server Component - runs on server only
async function BlogPost({ id }) {
const post = await db.post.findUnique({ where: { id } });
return <article>{post.content}</article>;
}
Key characteristics:
- Zero JavaScript bundled to client
- Direct database access
- Build-time or per-request rendering
- Next.js defaults to Server Components
React Compiler (Stable Integration with Next.js 16)
Automatically optimizes components without manual memoization:
Before:
const memoizedValue = useMemo(() => expensiveCalc(a, b), [a, b]);
const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);
With Compiler:
// Compiler automatically optimizes
const value = expensiveCalc(a, b);
const callback = () => doSomething(a, b);
Capabilities:
- Eliminates need for useMemo/useCallback in most cases
- Automatic re-render optimization
- Integrated and stable in Next.js 16
Server Functions (formerly Server Actions)
Simplified server-side mutations:
// app/actions.ts
"use server";
export async function updateProfile(formData: FormData) {
const name = formData.get("name");
await db.user.update({ where: { id }, data: { name } });
}
// Client Component usage
import { updateProfile } from "./actions";
export default function ProfileForm() {
return (
<form action={updateProfile}>
<input name="name" />
<button type="submit">Update</button>
</form>
);
}
Simplified Forms
React 19 eliminates much of the complexity:
// No more controlled state needed
export default function ContactForm() {
return (
<form
action={async (formData) => {
await submitToServer(formData);
}}
>
<input name="email" />
<button type="submit">Submit</button>
</form>
);
}
API Simplifications
Context Providers - No More .Provider
// Before
<MyContext.Provider value={value}>{children}</MyContext.Provider>
// React 19+
<MyContext value={value}>{children}</MyContext>
Ref as Prop - No More forwardRef
// Before
const Button = forwardRef((props, ref) => <button ref={ref} {...props} />);
// React 19+
const Button = ({ ref, ...props }) => <button ref={ref} {...props} />;
Document Metadata Support
Place <title>, <meta>, <link> directly in components:
function BlogPost({ title, description }) {
return (
<>
<title>{title} | My Blog</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<article>{content}</article>
</>
);
}
React automatically "hoists" these to <head>.
Enhanced Ref Callbacks
Ref callbacks can now return cleanup functions:
const buttonRef = useCallback((element: HTMLButtonElement | null) => {
if (!element) return; // cleanup on unmount
const handler = () => console.log("Clicked");
element.addEventListener("click", handler);
return () => element.removeEventListener("click", handler);
}, []);
Improved Hydration
- Better handling of third-party script DOM mutations
- Enhanced error messages with detailed diffs
- Consolidated error logging
New Error Callbacks
createRoot(document.getElementById("root")!, {
onCaughtError: (error, errorInfo) => {
// Errors caught by Error Boundaries
console.error("Caught:", error, errorInfo);
},
onUncaughtError: (error, errorInfo) => {
// Errors NOT caught by Error Boundaries
console.error("Uncaught:", error, errorInfo);
},
});
Custom Elements Support
Full support for Web Components with proper attribute and event handling.
Next.js 16 (Released October 2025)
Major Changes
Turbopack - Now Default & Stable
Turbopack is now the default bundler for both next dev and next build:
# Turbopack is now default - no flags needed
next dev
next build
Performance improvements:
- 5-10x faster Fast Refresh
- Significantly faster builds
- Production-ready stability
Cache Components (New Programming Model)
Cache Components replace Partial Prerendering (PPR) with a more explicit caching model:
// next.config.js - Enable Cache Components
export default {
cacheComponents: true, // Opt-in feature
};
Key characteristics:
- More explicit and flexible than implicit App Router caching
- Requires opt-in via config flag
- Component-level caching control
- Supersedes experimental PPR from Next.js 15
proxy.ts Replaces middleware.ts
Middleware has been renamed to proxy:
// Before: middleware.ts
export function middleware(request: NextRequest) {
// logic
}
// After: proxy.ts
export function proxy(request: NextRequest) {
// Same logic, just renamed
}
Migration:
# Automated codemod available
npx @next/codemod@canary middleware-to-proxy
What changed:
- File renamed:
middleware.ts→proxy.ts - Export renamed:
middleware→proxy - Logic remains identical
- Runs on Node.js runtime
Async Route Parameters (Breaking Change)
Route parameters are now async:
// Before Next.js 16
export default async function Page({ params, searchParams }) {
const id = params.id;
const query = searchParams.q;
}
// Next.js 16+
export default async function Page({ params, searchParams }) {
const id = await params.id; // Now async!
const query = await searchParams.q; // Now async!
}
This is a breaking change requiring migration of components using params/searchParams.
React 19.2 Integration
Full compatibility and optimization for React 19.2 features.
Deprecated APIs Removed
- Various
unstable_APIs promoted to stable - Old middleware convention fully removed
- Legacy caching patterns removed
Next.js 15 Features (Still Relevant)
App Router (Default)
File-system based router with Server Components:
app/
layout.tsx # Root layout
page.tsx # Home page
blog/
layout.tsx # Blog section layout
[slug]/ # Dynamic route
page.tsx # Blog post page
Server Actions (Stable)
Direct server-side mutations from client components:
// app/actions.ts
"use server";
export async function createTodo(formData: FormData) {
const title = formData.get("title");
await db.todo.create({ data: { title } });
}
// app/page.tsx
import { createTodo } from "./actions";
export default function Page() {
return (
<form action={createTodo}>
<input name="title" />
<button type="submit">Add</button>
</form>
);
}
Server Components (Default)
All components in app/ are Server Components by default:
// Server Component - no 'use client' needed
async function Dashboard() {
const user = await getCurrentUser();
const posts = await db.post.findMany();
return <div>Welcome {user.name}</div>;
}
Add 'use client' directive only for:
- Event handlers (
onClick, etc.) - React hooks (
useState,useEffect, etc.) - Browser APIs
Improved Caching
More predictable and granular caching:
- Better defaults for data fetching
- Explicit cache control via
revalidatePath/revalidateTag fetchrequests cached by default
// Cache for 1 hour
const data = await fetch("https://api.example.com/data", {
next: { revalidate: 3600 },
});
// Invalidate on demand
revalidatePath("/blog");
revalidateTag("posts");
after() API
Post-response operations:
import { after } from "next/server";
export default function handler() {
after(() => {
// Runs after response is sent to client
analytics.track();
});
return <Response />;
}
Best Practices (2025-2026)
Server-First Mental Model
- Default to Server Components - Only use Client Components for interactivity
- Leverage Server Functions - Replace API routes for mutations
- Stream with Suspense - Progressive loading for better UX
- Push state to edges - Client components only where necessary
Form Handling Pattern
// Server Action
"use server";
async function submitContact(prevState, formData) {
const email = formData.get("email");
await db.contact.create({ data: { email } });
return { success: true };
}
// Client Component
("use client");
import { useActionState } from "react";
import { submitContact } from "./actions";
export default function ContactForm() {
const [state, formAction, isPending] = useActionState(submitForm, null);
return (
<form action={formAction}>
<input name="email" disabled={isPending} />
<button disabled={isPending}>
{isPending ? "Sending..." : "Submit"}
</button>
{state?.success && <p>Thanks!</p>}
</form>
);
}
Optimistic UI Pattern
"use client";
import { useOptimistic } from "react";
import { toggleLike } from "./actions";
function LikeButton({ postId, initialLiked }) {
const [optimisticLiked, addOptimistic] = useOptimistic(
initialLiked,
(state) => !state
);
return (
<button
formAction={async () => {
addOptimistic();
await toggleLike(postId);
}}
>
{optimisticLiked ? "Unlike" : "Like"}
</button>
);
}
Server Component Data Fetching
// Simple, direct database access
async function BlogList() {
const posts = await db.post.findMany({
orderBy: { createdAt: "desc" },
});
return (
<div>
{posts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
Streaming with Suspense
import { Suspense } from "react";
export default function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<StatsSkeleton />}>
<Stats />
</Suspense>
<Suspense fallback={<PostsSkeleton />}>
<RecentPosts />
</Suspense>
</div>
);
}
Migration Guides
Next.js 15 → 16
-
Update async params:
// Add await to all params/searchParams access const id = await params.id; -
Rename middleware to proxy:
npx @next/codemod@canary middleware-to-proxy -
Enable Turbopack (already default, but verify):
next dev --turbo # Should be default now -
Consider Cache Components:
// next.config.js export default { cacheComponents: true, // Opt-in for new caching model };
React 18 → 19
- Update forms to use Actions
- Simplify components - Remove
forwardRef, update Context syntax - Remove manual memoization where React Compiler is active
- Add error callbacks to
createRoot - Migrate to native metadata instead of third-party libraries
Pages Router → App Router
| Pages Router | App Router |
|---|---|
pages/index.js |
app/page.tsx |
getServerSideProps |
async Server Component |
getStaticProps |
async Server Component + generateStaticParams |
getStaticPaths |
generateStaticParams |
_app.js |
app/layout.tsx |
_document.js |
app/layout.tsx |
API routes pages/api/ |
Route handlers app/api/ |
Common Patterns
Document Metadata by Route
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
images: [post.ogImage],
},
};
}
Loading States
// app/blog/loading.tsx
export default function Loading() {
return <BlogListSkeleton />;
}
// Or inline Suspense
export default function BlogPage() {
return (
<Suspense fallback={<BlogListSkeleton />}>
<BlogList />
</Suspense>
);
}
Error Handling
// app/blog/error.tsx
"use client";
export default function Error({ error, reset }) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
Resources
Official Documentation
- React 19 Official Blog
- React 19.2 Release
- React Versions
- Next.js 16 Official Blog
- Next.js 16 Upgrade Guide
- Cache Components Documentation
- Proxy Migration Guide
React References
- useActionState Reference
- useOptimistic Reference
- useFormStatus Reference
- use Reference
- Server Components Reference
- Server Functions Reference