Boost Performance With React Server Components and Next.js
A practical guide to building fast product pages with React and Next.js.
Jun 12th, 2025 2:00pm by
What Are React Server Components?
React Server Components (RSCs) are a new type of React component that runs only on the server. Unlike traditional React components, which run in the user’s browser (client-side), RSCs execute on the server and send the results to the browser. This means they can directly access server-side resources like databases, file systems, or APIs without needing extra client-side code. Imagine you’re building an online store. To show a list of products, a traditional React app would:- Load the webpage in the browser.
- Make an API call from the browser to the server to fetch product data.
- Display the products once the data arrives.
Why Are RSCs Important?
RSCs bring many benefits that make web apps faster, simpler, and more efficient. Here are the key advantages:- Faster Page Loads: Since RSCs run on the server, the browser gets pre-rendered HTML immediately. Users see content faster without waiting for JavaScript to fetch data.
- Less JavaScript in the Browser: RSCs reduce the amount of JavaScript sent to the client, which is excellent for performance, especially on slower devices or networks.
- Direct Server Access: RSCs can talk directly to databases or APIs on the server, making your code more straightforward and secure.
- Better Developer Experience: With RSCs, you can write server and client logic in the same React component model, keeping your codebase clean and consistent.
How Do React Server Components Work?
To understand RSCs, let’s break down how they fit into a React app. A typical React app with RSCs has two types of components:- Server Components: These run only on the server. They can fetch data, access files, or perform heavy computations. Because they don’t run in the browser, they don’t include interactive features like onClick handlers.
- Client Components: These are the traditional React components that run in the browser. They handle interactivity, like button clicks or form inputs.
use client and use server directives to distinguish between component types. While use client is explicitly required for client components, server components are server by default — but you can also mark a file with use server to clarify intent or enforce placement.
The “.server.js” and “.client.js” Files
In a project using RSCs, you’ll see files with special extensions:
- .server.js: Indicates a Server Component that runs only on the server.
- .client.js: Indicates a Client Component that runs in the browser.
- ProductList.server.js: A Server Component fetches product data from a database and renders a list.
- AddToCartButton.client.js: A Client Component with a button that adds a product to the cart when clicked.
Building an E-Commerce Product Listing Page with RSCs
Let’s walk through a practical example of using React Server Components to build a product listing page for an online store. We’ll use Next.js 13 (or later), which significantly supports RSCs. This example assumes you have some basic knowledge of React and Next.js, but I’ll keep it simple. Step 1: Set Up Your Next.js Project First, create a new Next.js project. Open your terminal and run:
npx create-next-app@latest my-ecommerce-app
cd my-ecommerce-app
npm run dev
// app/products/ProductList.server.js
import products from '../../data/products.json';
export default async function ProductList() {
// Simulate fetching data from a database
const productData = await new Promise((resolve) => {
setTimeout(() => resolve(products), 500); // Fake delay
});
return (
<div>
<h1>Our Products</h1>
<ul>
{productData.map((product) => (
<li key={product.id}>
<h2>{product.name}</h2>
<p>Price: ${product.price}</p>
<p>{product.description}</p>
</li>
))}
</ul>
</div>
);
}
[
{
"id": 1,
"name": "Wireless Headphones",
"price": 99.99,
"description": "High-quality wireless headphones with noise cancellation."
},
{
"id": 2,
"name": "Smart Watch",
"price": 149.99,
"description": "Track your fitness and stay connected on the go."
},
{
"id": 3,
"name": "Portable Charger",
"price": 29.99,
"description": "Compact charger for your devices."
}
]
// app/products/AddToCartButton.client.js
'use client'; // Marks this as a Client Component
export default function AddToCartButton({ productId }) {
function handleClick() {
alert(`Added product ${productId} to cart!`);
// In a real app, you'd update a cart state or make an API call
}
return (
<button onClick={handleClick}>
Add to Cart
</button>
);
}
// app/products/ProductList.server.js
import products from '../../data/products.json';
import AddToCartButton from './AddToCartButton.client';
export default async function ProductList() {
const productData = await new Promise((resolve) => {
setTimeout(() => resolve(products), 500);
});
return (
<div>
<h1>Our Products</h1>
<ul>
{productData.map((product) => (
<li key={product.id}>
<h2>{product.name}</h2>
<p>Price: ${product.price}</p>
<p>{product.description}</p>
<AddToCartButton productId={product.id} />
</li>
))}
</ul>
</div>
);
}
// app/products/page.js
import ProductList from './ProductList.server';
export default function ProductsPage() {
return (
<div>
<ProductList />
</div>
);
}
/* app/globals.css */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
h1 {
color: #333;
}
ul {
list-style: none;
padding: 0;
}
li {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 10px;
border-radius: 5px;
}
button {
background-color: #28a745;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #218838;
}
- Performance: The product list is rendered on the server, so users see content quickly, even on slow networks.
- Simplicity: The Server Component fetches data directly, reducing the need for complex client-side logic.
- Interactivity: The Client Component (AddToCartButton) adds interactivity where needed, keeping the browser lightweight.
- Scalability: In a real app, you could replace the JSON file with a database query (e.g., Prisma or MongoDB) without changing the component structure.
- Streaming Rendering
import { Suspense } from 'react';
import ProductList from './ProductList.server';
export default function ProductsPage() {
return (
<div>
<h1>Online Store</h1>
<Suspense fallback={<p>Loading products...</p>}>
<ProductList />
</Suspense>
</div>
);
}
- Dynamic Data Fetching
// app/products/ProductList.server.js
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default async function ProductList() {
const products = await prisma.product.findMany();
return (
<div>
<h1>Our Products</h1>
<ul>
{products.map((product) => (
<li key={product.id}>
<h2>{product.name}</h2>
<p>Price: ${product.price}</p>
<p>{product.description}</p>
<AddToCartButton productId={product.id} />
</li>
))}
</ul>
</div>
);
}
- Server-Only Logic
// app/admin/Dashboard.server.js
import { getCurrentUser } from '../../lib/auth';
export default async function AdminDashboard() {
const user = await getCurrentUser();
if (!user.isAdmin) {
throw new Error('Access denied');
}
return <div>Welcome, Admin!</div>;
}
Challenges and Limitations of RSCs
While RSCs are powerful, they have some limitations:- Server Dependency: Your app needs a server to run RSCs, so they don’t work in fully static sites (e.g., exported Next.js sites).
- Learning Curve: Mixing Server and Client Components can be confusing at first. You need to understand where the code runs.
- No Interactivity in Server Components: If you need interactivity (e.g., onClick), you must use a Client Component.
- Tooling Requirements: RSCs require a framework like Next.js and a compatible build setup.
Tips for Using RSCs Effectively
Here are some best practices to get the most out of RSCs:- Minimize Client Components: Use Server Components for static or data-heavy parts of your app to reduce JavaScript sent to the browser.
- Leverage Suspense: Use Suspense for streaming to improve perceived performance.
- Keep Components Small: Break down complex components into smaller Server and Client Components for clarity.
- Test Performance: Use tools like Lighthouse to measure how RSCs improve your app’s load time and user experience.
- Stay Updated: RSCs are still evolving, so follow React and Next.js updates for new features and best practices.
- Monitoring tools like Lighthouse or Chrome DevTools can be used to benchmark the performance gained from RSC adoption.
Conclusion
React Server Components are a game-changer for building fast, efficient, and scalable web applications. By running components on the server, RSCs reduce client-side JavaScript, speed up page loads, and simplify data fetching. Our e-commerce product listing example showed how to combine Server and Client Components to create a dynamic, interactive page with minimal effort. If you’re new to RSCs, start small with a project like the one we built here. Experiment with Next.js, try fetching data from a real database, and explore streaming with Suspense. As you get comfortable, RSCs make your apps faster and your code cleaner.
YOUTUBE.COM/THENEWSTACK
Tech moves fast, don't miss an episode. Subscribe to our YouTube
channel to stream all our podcasts, interviews, demos, and more.