React 20 makes Server Components the default, deprecating older client-side patterns and promising a 40% reduction in average bundle sizes.
Server Components as the default render path
React 20 treats Server Components as the primary way to build UI. Components run on the server by default, send a lightweight description of the tree to the client, and only pull in browser JavaScript where interactivity is required. That flips the older model, where almost everything shipped as client code and the server mainly delivered a shell or hydration payload.
In practice this means most of your tree can fetch data, format content, and compose layout without adding to the client bundle. You still need client components for forms, local state, event handlers, and browser APIs—but those become deliberate boundaries rather than the default for every file.
What “deprecating older client-side patterns” actually changes
Patterns built around “everything is a client component” lose their default status. Large client trees that only display data, repeated data-fetching hooks on the client for content that never needs to re-run in the browser, and broad “use client” boundaries at the top of an app are the main casualties. They still work in many setups, but they fight the new default and leave bundle size and hydration cost on the table.
A cleaner mental model is: start server-first, then mark a leaf or a small subtree as client when it needs state or events. Pass serializable props down from server parents. Keep client islands small so the rest of the page stays free of that JavaScript.
Where the bundle savings come from
React 20’s pitch of about a 40% reduction in average bundle sizes rests on a simple idea: code that never runs in the browser should not ship to the browser. Server Components let libraries, data-access layers, and presentation logic stay off the client graph when nothing in that subtree is interactive.
- Prefer server components for static and data-driven UI that does not need handlers or local state.
- Push
"use client"(or your framework’s equivalent) as low in the tree as you can. - Avoid importing heavy client-only modules from shared files that server components also use—split those modules so the server path does not pull the client path.
- Treat the client boundary as an API: props should be plain data, not functions or class instances.
Those habits matter more than any single flag. The 40% figure is an average-style claim, not a guarantee for every app; apps that already kept most logic on the server will see smaller wins than apps that hydrated entire dashboards on the client.
How to migrate without rewriting everything at once
Migrate leaf by leaf. Identify routes or sections that are mostly display, move data loading upward into server components, and leave interactive widgets as client children. Measure what actually ships: if a page still loads large client graphs for read-only content, your boundaries are too high or shared imports are still dragging client code into the server tree.
When something feels harder under the new default—complex client-only widgets, third-party UI that assumes a browser, or rich offline behavior—keep that piece as a client island and do not force it onto the server. The goal is not “no client code”; it is “client code only where the product needs it,” with Server Components as the path of least resistance for everything else.