The RSC paradigm introduces three "kinds" of components - Server, Client, and Shared.

Server Components

The source code of server components is not sent to the client, and their rendering process does not happen in the client. Only the result of the server-side rendering process is sent. It might be useful to think of Server Components as template generators that output static HTML, which is sent and displayed to the client. This isn't exactly what happens but it's close enough for a basic mental model.

Because of this, it is safe to make requests to sensitive APIs or resources within the code of Server Components themselves, as long as the output of their render process is carefully controlled.

React Server Components can be async functions, which allows them to await promises.

server
node.js
Hi, I am a Server Component.
{ "hello": "world" }
import fs from "fs";

// whoa, fs is available!
export default function ServerSync() {
// we can read the filesystem inside Server Component!
const data = fs.readFileSync("app/db/test.json", "utf8");
const json = JSON.parse(data);
return (
<div>
<div>Hi, I am a Server Component.</div>
<code>{JSON.stringify(json, null, 2)}</code>
</div>
);
}
Async
server
node.js
Hi, I am an Async Server Component.
{ "hello": "world" }
import fs from "fs/promises";

// whoa, it's async!
export default async function ServerAsync() {
// we can await promises inside Server Components
const data = await fs.readFile("app/db/test.json", "utf8");
const json = JSON.parse(data);
return (
<div>
<div>Hi, I am an Async Server Component.</div>
<code>{JSON.stringify(json, null, 2)}</code>
</div>
);
}

Client Components

These are components that use browser-only APIs. They usually have some kind of interactivity or some kind of rendering that can only be done in the browser. They have the use client directive at the top of their file.They behave like "traditional" react components most of the time.

client
node.js
I am a Client Component. Watch me count: 0
"use client";
// useEffect and useState are only available with "use client" set
import { useEffect, useState } from "react";

export default function Client() {
const [count, setCount] = useState(0);

useEffect(() => {
const interval = setInterval(() => {
setCount((prev) => prev + 1);
}, 100);

return () => clearInterval(interval);
}, []);

return <div>I am a Client Component. Watch me count: {count}</div>;
}

In the RSC world, depending on the framework, Client Components can behave unexpectedly, especially during Hydration. We will look more closely at this later.

Shared Components

Shared Components can act as either Server or Client components. Because they don't use any APIs that are only available on the client or server, they can be rendered in either environment. They automatically "become" Server or Client components to match the environment they are imported into.

Typically, Shared Components are useful for things like UI elements that might need to be rendered on both the client and server.

shared
node.js

Hi, I am a Shared component. I will be rendered on the server by default, but if a Client Component imports me, I will be rendered on the client.

To remain truly shareable, I should not use any client-only or server-only APIs. I will throw an error if I use an unsupported API in the wrong environment.

client
node.js
shared
node.js

Hi, I am a Shared component. I will be rendered on the server by default, but if a Client Component imports me, I will be rendered on the client.

To remain truly shareable, I should not use any client-only or server-only APIs. I will throw an error if I use an unsupported API in the wrong environment.


It is up to you, the developer, to decide which components to use. The kind of component you use for a given task will vary, but it will determine a rendering strategy, how you fetch data, and how the user interacts with your app.

What do you need to do?
ServerSharedClient

Fetch data

Access backend resources (directly)

Keep sensitive information on the server (access tokens, API keys, etc)

Keep large dependencies on the server / Reduce client-side JavaScript

Share common 'dumb' component code that can be rendered in browser and node.js

Add interactivity and event listeners (onClick(), onChange(), etc)

Use State and Lifecycle Effects (useState(), useReducer(), useEffect(), etc)

Use browser-only APIs

Use custom hooks that depend on state, effects, or browser-only APIs

Handle edge cases where rendering on server is inefficient.

As a rule of thumb, and as Next.js defaults dictate, Use Shared Components unless you need to use a Server Component feature like data fetching, and only use a Client Component when you need to use a browser-only API.

Of course, there are always exceptions, but the RSC paradigm provides you with maximum flexibility to tackle any problem.

Next, let's look at the different environments where server and client components run.