How to Implement React Server Components
React Server Components (RSC) represent a paradigm shift in how we build web applications, allowing developers to render components on the server and send minimal JavaScript to the client. This article provides a straightforward guide on how to implement Server Components, covering the basic setup, data fetching techniques, and how to seamlessly integrate them with interactive Client Components using a modern framework like Next.js.
Understanding Server vs. Client Components
By default, in modern React frameworks like Next.js (App Router), all components are treated as Server Components. They run exclusively on the server, which keeps large dependencies out of the client-side JavaScript bundle and improves initial page load times.
If you need interactivity (such as useState,
useEffect, or browser-only APIs), you must explicitly
define a Client Component by adding the
'use client' directive at the very top of the file.
Step 1: Set Up your Environment
To implement Server Components today, you need a framework that supports the React Server Components architecture. The easiest way to start is by creating a new Next.js application:
npx create-next-app@latest my-rsc-appDuring the setup, ensure you select the App Router option, as it natively supports Server Components by default.
Step 2: Create a Basic Server Component
Since components in the App Router are Server Components by default,
you can write them using standard React syntax. You can also make the
component function asynchronous (async/await) to fetch data
directly inside the component rendering lifecycle.
Create a file named page.js (or page.tsx)
inside the app/ directory:
// app/page.js
// This is a Server Component by default
export default async function Page() {
const data = await getData();
return (
<main style={{ padding: '20px' }}>
<h1>React Server Components Demo</h1>
<ul>
{data.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</main>
);
}
// Data fetching happens directly on the server
async function getData() {
const res = await fetch('https://api.example.com/items', {
next: { revalidate: 3600 } // Cache data for 1 hour
});
if (!res.ok) {
throw new Error('Failed to fetch data');
}
return res.json();
}Step 3: Implement Interactivity with Client Components
Server Components cannot handle user interactions like click events
or form submissions directly. To add interactivity, create a separate
Client Component using the 'use client' directive.
Create a file named Counter.js in your components
folder:
// components/Counter.js
'use client'; // This directive marks this file as a Client Component
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}Step 4: Import Client Components into Server Components
You can import and render Client Components directly inside your Server Components. This allows you to keep the static, data-heavy parts of your page on the server while isolating interactive elements to the client.
Update your app/page.js to include the
Counter component:
// app/page.js
import Counter from '../components/Counter';
async function getData() {
const res = await fetch('https://api.example.com/items');
return res.json();
}
export default async function Page() {
const data = await getData();
return (
<main style={{ padding: '20px' }}>
<h1>React Server Components Demo</h1>
{/* Interactive Client Component */}
<Counter />
<h2>Data List:</h2>
<ul>
{data.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</main>
);
}Best Practices for Implementation
- Keep Client Components at the Leaves: Keep your Client Components as small as possible and place them at the leaf nodes of your component tree (e.g., individual buttons, search inputs, or forms).
- Pass Server Components as Children: If a Client
Component needs to wrap a Server Component, pass the Server Component as
a
childrenprop rather than importing the Server Component directly into the Client Component file. - Keep Sensitive Data on the Server: Because Server Components do not ship their code to the browser, you can safely query databases and use private API keys directly inside them.