React is thirteen years old, and its core idea has not changed in thirteen years: UI is a function of state. Components are functions, data flows down, events flow up. Everything that made React 19 (December 2024) and 19.2 (October 2025) the biggest shift since hooks is not a new idea — it is that idea finally extending across the whole stack. The function got a server side, the form got a server action, and memoization got a compiler.
This part is about using React 19 the way it is actually used in mid-2026: Server Components as the default, Actions as the mutation pattern, the Compiler as the reason your useMemo collection is a museum piece, and the small set of hooks that still earn their place.
The mental model that survived a decade
Before the new machinery, the foundations from Part 2 still apply. A React component is a plain JavaScript function: props in, UI description out. State is data that changes over time; rendering is React calling your functions with the current data. If you understand that re-rendering is just “the function runs again with new inputs,” you understand 80% of React — including the parts people find mysterious.
What changed is where the function runs. In 2026 the default answer is: on the server. The client is where you put the exceptions.
Server Components: the boundary is the feature
React Server Components (RSC) are stable and, inside a meta-framework like Next.js 16, they are the default component type. The model to internalize:
- Server components run only on the server — at build time or per request. They can read the database, the filesystem, and server secrets directly. They ship zero JavaScript to the browser. They cannot use state, effects, or event handlers, because they never come alive on the client.
- Client components are the ones you mark with
"use client". They hydrate in the browser and get the full interactive toolkit: state, effects, event handlers, browser APIs.
The tree is not “server side first, then client side.” It is a single component tree where the boundary weaves through it: a server Page can render a client SearchBar island next to a server PostList, which itself contains a client LikeButton island.
The three rules that follow from the model, and that the linter enforces:
- A client island can only receive serializable props from the server — JSON-shaped data, plus specially handled Server Actions. No class instances, no functions, no database connections.
- Server components never re-render on the client. If data changes, the new UI is produced on the server and streamed over.
"use client"is a boundary, not a file-wide downgrade — everything imported into a client file becomes client code, so you push the directive as far down the tree as interactivity actually requires. The bundle you ship is precisely the islands you marked.
Actions: the mutation model
Reading data is solved by rendering on the server. Writing data is solved by Actions: the form-or-event counterpart of RSC, and the pattern that deleted the boilerplate API route for most UI-driven mutations.
A Server Action is a function marked "use server" that the client can call like an RPC — most cleanly, directly from a <form action={...}>. React generates the endpoint, assigns the action an ID, and posts to it. Because it rides on the platform’s form semantics from Part 2, it works without any client JavaScript — progressive enhancement is the default, not a feature you add.
The hooks that complete the pattern:
useActionState— threads the action’s return value back into your component and gives youisPendingfor free. It replaced theuseState(isLoading) + useState(error) + try/catchtriplet.useFormStatus— lets a child button read the pending state of its parent form, so the submit button disables itself without prop drilling.useOptimistic— shows the expected result instantly (the like appears, the comment posts) and reconciles automatically when the server answers, rolling back on failure. This is the 2026 answer to perceived performance, and it is the legitimate replacement for most client-side state synchronization.
The discipline that remains: an action is server code reachable over HTTP, so validate inputs and check authorization inside the action — the same rules as any API endpoint. (Part 5 and Part 6 go deep on validation and auth.)
What remains of hooks
Hooks did not disappear, but their job description shrank to reality:
- Data fetching in
useEffectis over. Server components fetch during render; client components use framework data layers oruse()with a cached promise. Effects still exist — for synchronizing with external systems: a WebSocket, a media query listener, a non-React widget. use()reads promises and context during render, and unlike older hooks it can be called conditionally — the clean answer to “await this, but only in this branch.”useEffectEvent(19.2) finally separates “events that happen inside an effect” from the effect’s reactive dependencies — the fix for a decade of stale-closure postmortems and ref workarounds.eslint-plugin-react-hooksv6 is stricter by design: it flags patterns that break memoization, which is effectively a free audit for Compiler readiness.
The Compiler: memoization is a build step
React Compiler reached 1.0 in October 2025 and is stable in Next.js 16. It analyses your components at build time and inserts memoization automatically — value memoization, callback stability, the works — with semantics identical to hand-written useMemo/useCallback, except it never forgets a dependency.
The practical consequences:
- Write components as if every render were cheap. The compiler makes it true for the common cases.
- Manual
useMemo/useCallbackbecome rare exceptions for things the compiler cannot prove — and a code smell worth questioning in review, the way a handwrittenforloop makes you ask why notmap. - When something still feels slow, the answer is measurement, not superstition: React 19.2 added Performance Tracks to Chrome DevTools — a Scheduler track showing task priorities and a Components track showing render timings and causes.
Activity and pre-rendering: rendering in the background
The 19.2 additions are about when things render:
<Activity>replaces{isVisible && <Page />}with a declarative boundary:mode="hidden"keeps the tree alive but unmounts effects and defers updates to idle time. Two immediate wins — tab UIs that keep form drafts when you switch away and back, and background pre-rendering of the page the user will probably visit next, so navigation feels instant.- Partial Pre-rendering (
prerender, thenresumeorresumeAndPrerender) lets a static shell be prerendered and served from a CDN, with dynamic holes resumed per request. These APIs are primarily for framework authors — Next.js 16’s stable PPR is exactly this idea, productized. - On the server, React now batches Suspense boundary reveals briefly so streamed content appears in coherent chunks instead of a waterfall of spinners — and
useIdvalues are now validview-transition-names, which is the kind of detail that tells you where the platform is heading (Part 2’s View Transitions, meet React).
Governance and housekeeping
Two non-technical notes that matter for long-lived projects:
- React has a foundation. In February 2026 Meta contributed React to the newly formed React Foundation under the Linux Foundation, with Amazon, Callstack, Expo, Huawei, Meta, Microsoft, Software Mansion, and Vercel as platinum members. Stewardship is now vendor-neutral — a real de-risking for anyone betting a decade on the ecosystem.
- Patch your React. The React2Shell vulnerability (CVE-2025-55182) affected React 19.0.0 through 19.2.2. If any deployment is still on those versions, upgrading is not optional. Pin current versions and subscribe to the React blog’s security feed.
Practice, then Part 4
- Build a page with one server component that reads from a local data source (a JSON file is fine) and one client island — a counter. Move
"use client"up and down the tree and watch the shipped bundle change in your framework’s build output. - Convert that counter into a form with a Server Action that persists the value anywhere (a file, SQLite, anything). Delete your
isLoadingstate and redo it withuseActionState; adduseOptimisticand watch the UI answer before the server does. - Disable JavaScript in DevTools and submit the form. If it still works, you built it the 2026 way.
- Enable the React Compiler and open Chrome DevTools’ Performance Tracks. Find one component that still re-renders wastefully — and fix the cause, not the memo.
Part 4 zooms out one level: the meta-frameworks that wire all of this into routes, caching, and deployment — Next.js 16 versus Astro, and how to choose by the shape of what you are building.