Published on 9 min read
React - Introducing Server and Shared Components
Update (September 2026): Server Components shipped as stable in React 19. The final design differs from this early RFC: "Shared Components" were dropped, and the .server.js/.client.js file conventions were replaced by the 'use client' directive. Data is fetched with async/await directly in Server Components.
Motivation
In software development, the "Fast/Good/Cheap Rule" term refers to compromises we need to make on one of speed, quality and cost in order to maximize the other two.
When we develop components, the rule is reflected and becomes a concrete challenge - how could we create components that are simple with maintainable code, producing a good user-experience, while having fast performance as much as possible? Indeed, according to the rule, it’s not possible.
But maybe, there is a way to leverage the server-side to solve the performance problems by default, while still achieving interactive and rich user-experience with sort of the same ecosystem, though? 👀
Well, for that purpose, the React core and data teams recently researched and created an RFC proposing new types of components "Server Components" and "Shared Components" aiming to beat the challenge. They also did an informative talk (all chapters are attached below) and created an example project - check them out as well.
Let’s introduce those components!
⚠️ Note: This proposal isn’t production-ready and still under experiment, which means, we shouldn’t use it for production. However, we’re more than welcome to play with it and give the React team our feedback.
The content is available as a video as well:
Server Components
Server Components are a new sort of React components which simply run only on the server-side, and so primarily letting us avoid downloading their code to the client-side:
import MyClientComponent from 'MyComponent.client';
function MyServerComponent(props) {
// For instance, the data could be fetched directly from a database
const { data } = props;
return <MyClientComponent data={data} />;
}
They look like traditional React components (from now on called “Client Components”), but unlike them, Server Components cannot manage state, use rendering lifecycle (useEffect/useLayoutEffect), access browser’s APIs or any custom functionality that depends on those. Also, notice the naming conventions - .client.js and .server.js for Client and Server Components respectively.
However, as stated, they are meant to complete the ecosystem and work together with Client Components and SSR, so that we have the interactivity of client-side application but with a better performance using the server-side. The interesting thing is, that placing components on the server-side yields a couple of benefits in addition to the fact that their code isn’t downloaded.
Obviously, there are drawbacks, whether it’s a new standard of components to learn, specific constraints to make them work, the new conventions might seem confusing and messy and probably more. Having said that, with a successful RFC, the team (and the community) might minimize the drawbacks and intensify the benefits.
So speaking of the benefits, let’s explain them.
Zero Bundle Size
SSR is a technique to quickly render client-side applications on the server thereby returning a complete HTML markup, which was rendered in advance, to the client. That is, in SSR we use the server-side to output pre-made HTML, thereby improve the loading performance. Yet, it doesn’t deal with:
- JavaScript Bundle - SSR merely uses the bundle to render the pre-made HTML without affecting it
- Interactivity - SSR is mainly being used for a non-interactive initial render while the components are hydrated
Which begs the question, why not have a technique completing SSR and reducing the JS bundle size while allowing interactivity?
Indeed, Server Components were born exactly to fulfill this purpose.
Previously, we mentioned that they allow avoiding downloading their code to the client-side. So, instead of adding them to the JS bundle and letting the client render them - they are rendered statically on the server (the render stops at native elements and Client Components) and streamed in a format ("Virtual DOM") allowing reconciling seamlessly the server-side tree with the client-side tree:

The magic happens since the client regularly renders the native elements and Client Components, while progressively receives and paints the streamed response. The important thing to understand here, is that the dependencies the Server Components use stay on the server-side and aren’t part of the bundle at all.
In this way, Server Components (and the dependencies they use) have a nonexistent impact on the bundle size and by using them - we can reduce the overall bundle size significantly. ⚡
But we also said that Server Components allow interactivity, which doesn’t completely get along with the fact they are rendered statically.
The truth is, that Server Components will arrive with a framework (such as Next.js) supporting a refetching mechanism, which allows the client-side to request a rendered result of a Server Component. Meaning, the client-side makes a request for refetching a specific piece of UI using the framework, and when the framework on the server-side receives that, it makes React on the server-side rerender the Server Component and similarly the framework streams the rendered result progressively to the client-side. 💪🏻
Full Back-End Access
Another benefit of having the components on the server-side, is accessing its data and files easily.
For example, we can directly read from the database and pass it to the client component, without performing any request to fetch the data:
import db from 'db.server';
import MyClientComponent from 'MyComponent.client';
function MyServerComponent(props) {
const { id } = props;
const dataById = db.data.get(id);
return <MyClientComponent data={dataById} />;
}
It might help to remove the request latency and improve the performance, since the client-side doesn’t need to make sequential requests to fetch the data, but rather to have it directly inside the Server Component by the need.
Of course, this is only a demonstration - in the same manner, we could read from the file-system, access microservices and so on.
Automatic Code Splitting
Code splitting is a technique to split the bundle into multiple smaller bundles, that will be loaded dynamically and lazily at runtime, and so improve dramatically the performance. This ability is made possible through dynamic imports, React.lazy and supported bundlers (for example - Webpack, Rollup, etc.).
The main drawback of this technique is that we need to do it manually, meaning, splitting the code into bundles in order to load it lazily based on some logic:
const FirstComponent = React.lazy(() => import('./FirstComponent.client.js'));
const SecondComponent = React.lazy(() => import('./SecondComponent.client.js'));
function MyClientComponent(props) {
const shouldDisplaySecondComponent = ...; // Some logic
return shouldDisplaySecondComponent ? <SecondComponent/> : <FirstComponent/>;
}
Contrarily, the beauty of Server Components is that this bundle-separation happens automatically:
import FirstComponent from './FirstComponent.client.js';
import SecondComponent from './SecondComponent.client.js';
function MyServerComponent(props) {
const shouldDisplaySecondComponent = ...; // Some logic
return shouldDisplaySecondComponent ? <SecondComponent/> : <FirstComponent/>;
}
As we already said, the client-side only "downloads" the rendered result without any impact on the JS bundle.
The Same Syntax
Typically, when developing client-side and server-side, we tackle different challenges which demand different technologies and solutions.
The power of Server Components is bringing the ability to mix (for better or worse) and use a single ecosystem with the same language for both the client-side and server-side. Although it might arrive with a little learning-curve and magical interactivity mechanism, it’s still React.
Shared Components
Sometimes, we might want to create a component working both on client-side and server-side, so that the same logic and behavior are shared between both environments.
The RFC introduces a pretty good example in order to explain the need - a MarkdownRenderer component:
- On client-side - the component is needed when the user wants to edit the content, and the MarkdownRenderer component can be downloaded and provide a live preview while the user edits
- On server-side - the component is needed to render a viewer displaying the content that was written in markdown
That sounds great, so theoretically why not just develop Shared Components? 🤔
The thing is, there are no free rides, and Shared Components have all constraints of Server Components (no state/rendering lifecycle hooks/browser APIs) and Client Components as well (which is merely not accessing the server-side resources).
That being said, Shared Components might still be handy for certain use-cases, as we already explained with the MarkdownRenderer component.
Wrapping Up
Today we introduced the idea behind the new experimental Server Components and Shared Components.
Let’s recap:
- As software developers, we’d like to strive for an easy and maintainable code, producing a good user-experience, while having fast performance as much as possible
- Server Components are run on the server-side only and so their code isn’t downloaded to the client-side
- Server Components cannot manage state, use rendering lifecycle or access browser’s APIs
- Server Components are rendered on the server-side which means they have zero bundle size
- Server Components are returned to the client-side as a virtual DOM format of the server tree ready to be reconciled on the client-side
- Server Components will support refetching mechanism to be rerendered when the client-side triggers so
- Server Components have full access to the server resources, such as the file-system or a database
- Server Components bring automatic code splitting by default without doing anything manually
- Server Components make a single ecosystem for both client-side and server-side
- Shared Components are components that can run on both client-side and server-side
- Shared Components should follow the constraints of Client Components and Server Components
The Talk
Since I really enjoyed the "Data Fetching with React Server Components" talk - I’m attaching here all parts of the video by chapters. 🍿
Motivation
The Fast/Good/Cheap Rule of Software Development
Explaining Each Constraint of the Rule
Is There any Existing Solution?
The Motivation for Server Components
Live Demo
Creating a Server Component
Adding Interactivity
Rerendering a Server Component against SSR
Reading from a File
Shared Components
Filtering Using DB Directly
Preserving State After Server Tree is Rerendered
Streaming a Rendered Server Result Progressively
Clarifications
Benefit 1 - Zero Effect on Bundle Size
Benefit 2 - Accessing to Server Resources
React IO Libraries for Caching
Do We Have to Use GraphQL on Server?
Benefit 3 - Automatic Code Splitting
Benefit 4 - Ability to Unify Client and Server
Benefit 5 - Focusing on the UI
React App as a Single Tree Containing Client and Server
Recap & Development Status
RFC's Roadmap
Production Experiment at Facebook
Ongoing Research
Instructions and Gratitude
Final Recap
You’re welcome to share:
Join My Newsletter
Get new posts directly to your inbox.
Comments are powered by DisqusDetails
Loading comments activates Disqus, which collects information as a third-party service.
The site owner has no access to or control over the information collected by Disqus.
Related Posts

How to Sync a Design System with Claude Design
18 min read
Syncing React components into Claude Design with /design-sync: what the mirror holds, how it's verified and a step-by-step example with shadcn/ui, Tailwind v4 and Storybook.

ECMAScript - Introducing Deferred Module Evaluation with import defer
8 min read
Introducing the “import defer” proposal, a new ECMAScript import form that loads and links modules eagerly while deferring their evaluation until a namespace property is first accessed - currently at stage 3 in the TC39 process.

ECMAScript - Introducing BigInt Primitive in ES2020 (ES11)
5 min read
BigInt in ES2020 (ES11) - a new primitive for arbitrarily large integers, with its operators, comparisons and limits.