React
React components that render on the server before being sent to the browser, allowing for data fetching and logic to happen server-side
- supports Promises, allowing asynchronous tasks, you can use
async/await
without usinguseEffect, useState
- keep expensive data fetches & logic on the server & only send the result to the client
- allow you to query the database directly from the server without an additional API layer
- awesome blog post
- can use react server components with the new “app” router
- all components are assumed to be server components by default
- we have to “opt in” for client components using
use client
- client components can only import other client components
- set client boundaries → Any modules imported in a Client Component file must be Client Components as well
- we have to “opt in” for client components using
- As a general rule, if a component can be a Server Component, it should be a Server Component
- Server Components tend to be simpler and easier to reason about.
- Performance benefit: because Server Components don’t run on the client, their code isn’t included in our JavaScript bundles.
React Server Components (RSC) Flow:
- Initial Request:
- The server generates static HTML (containing the rendered Server Components).
- Client receives this HTML immediately (fast initial load).
- (Note: Server Components never hydrate—they stay static.)
- Hydration (for Client Components):
- The browser downloads the JS bundle asynchronously.
- Only Client Components (marked with
"use client"
) hydrate to become interactive.
- Result:
- Faster initial render (no waiting for JS to load).
- Smaller JS bundle (since Server Components don’t need client-side JS).
Traditional CSR (Client-Side Rendering) Flow:
- Initial Request:
- The browser gets a minimal HTML shell (often just a
<div id="root">
). - No meaningful content until JS loads.
- The browser gets a minimal HTML shell (often just a
- JS Download & Execution:
- The browser downloads the entire React bundle.
- React generates the full DOM client-side (slower, especially on low-end devices).
- Hydration:
- The entire app hydrates (even parts that could be static).
- Result:
- Slower initial load (depends on JS).
- Larger JS bundle (since everything is client-rendered).
small example
// server component in next.js
function Homepage() {
return (
<p>
Hello world!
</p>
);
}
- No JavaScript for this component is sent to the client, renders pure html
// the HTML sent to the client
<!DOCTYPE html>
<html>
<body>
<!-- Static HTML rendered by the server -->
<p>Hello world!</p>
<!-- Client-side JS bundle -->
<script src="/static/js/bundle.js"></script>
<!-- Hydration data for client-side reconciliation -->
<script>
self.__next['$Homepage-1'] = {
type: 'p',
props: null,
children: "Hello world!",
};
</script>
</body>
</html>
bundle.js
- includes dependencies like react, client components, etc
- server component
Homepage
is not included here
self.__next['$Homepage-1']
- this is serialized component data used for hydration (a process where the client-side JavaScript “takes over” the static HTML)