alirezasaremi.com logo
alirezasaremi.com logo

Alireza Saremi

React Server Components: Rethinking the Boundaries Between Frontend and Backend

2025-07-25

React

React Server Components (RSC) blur the line between frontend and backend. They allow you to fetch data, access secrets and run code on the server while composing a React tree that hydrates on the client. Understanding where the boundary lies—and how to cross it—is essential for building modern Next.js applications. This article dives into RSC and how they change the way we think about client and server code.

Table of Contents

1. Understanding React Server Components

Server components are React components that execute on the server at render time. They can perform data fetching, read from the file system and access environment secrets. Because they never run in the browser, they disappear from the client bundle entirely. The result is less JavaScript sent to the client and faster page loads.

In a Next.js App Router project, every component is a server component by default. To opt into client behaviour—such as event handlers or browser APIs—you add 'use client' at the top of the file.

2. Server vs Client Code Boundaries

The boundary between server and client components is explicit. A server component can import a client component, but not the other way around. This ensures that client code never accidentally depends on server‑only modules. If a client component needs data, you pass it down as props from its parent server component.

// app/profile/page.tsx (server component)
import ProfileCard from './ProfileCard';
import { getUser } from '@/lib/db';

export default async function ProfilePage() {
  const user = await getUser();
  return <ProfileCard user={user} />;
}

// ProfileCard.tsx (client component)
'use client';

export default function ProfileCard({ user }) {
  return <div>{user.name}</div>;
}

Here, the server component fetches data and passes it to the client component. The client component can handle interactions but cannot import server‑only libraries.

3. Sharing Logic and Data

One benefit of RSC is the ability to share code between the server and client without duplicating logic. You can extract data fetching and transformation into helper functions that run on the server. These functions return plain objects that can be passed to client components. If you need to reuse logic on the client, write it in a separate utility file imported by both sides.

Avoid using browser APIs or window references in server components. Similarly, avoid secrets or heavy computation in client components. Treat the boundary as a security and performance barrier.

4. Deploying RSC in Next.js

Deploying an RSC app is straightforward: platforms like Vercel automatically handle streaming and caching. Since server components produce static HTML, they can be cached at the CDN level. When combined with edge functions for dynamic bits, you get fast, personalised pages with minimal client JavaScript. Monitor performance to ensure that server logic doesn’t become a bottleneck.

If you are migrating an existing pages router app, move one page at a time. Start by converting data fetching functions into async server components. Add 'use client' to interactive components. Gradually adopt streaming and caching primitives.

5. Conclusion

React Server Components redraw the boundary between frontend and backend. By running some components on the server and sending only HTML to the client, you reduce bundle sizes and improve performance. Embracing this model requires understanding how server and client code interact, but the payoff is a simpler and faster user experience. Use RSC thoughtfully and combine them with edge functions and caching to build the next generation of web apps.