Dung (Donny) Nguyen

Senior Software Engineer

Server Components & Modern React Architecture

For most of React’s history, every component ran in the browser. React Server Components (RSC) change that by letting some components render on the server and never ship their code to the client. Combined with streaming, Suspense, and Server Actions, they form the foundation of modern React architecture used by frameworks like Next.js (App Router) and React Router / Remix.


1. The Problem RSC Solves

Traditional client-side React sends the entire component tree—and every library it imports—to the browser. That means:

Server Components address all three by moving rendering and data access to the server, sending only the finished HTML (and minimal JS) to the browser.


2. Server Components vs. Client Components

Modern React apps are built from two kinds of components that work together.

  Server Component Client Component
Runs on Server only Server (initial) + browser
Ships JS to client? No Yes
Can fetch data directly Yes (async/await, DB access) No (needs an API/hook)
Can use state/effects No Yes (useState, useEffect)
Can handle events No Yes (onClick, onChange)
Marked with Default 'use client' directive

The key idea: Server Components are the default, and you opt into the client only where interactivity is needed.


3. Writing a Server Component

A Server Component can be async and fetch data directly—no useEffect, no loading state, no extra API layer.

// app/posts/page.tsx  (Server Component by default)
async function PostsPage() {
  const posts = await db.post.findMany(); // runs on the server

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

export default PostsPage;

Because this code never reaches the browser, the database client and its dependencies stay off the client bundle entirely.


4. Opting Into the Client

When you need state, effects, or event handlers, add the 'use client' directive at the top of the file.

'use client';

import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}

A Server Component can import and render a Client Component, passing data down as props. The reverse is limited: a Client Component cannot import a Server Component, but it can receive one through the children prop.

// Server Component
import { Counter } from './Counter';

function Dashboard({ user }) {
  return (
    <section>
      <h1>Welcome, {user.name}</h1>
      <Counter /> {/* interactive island inside a server-rendered page */}
    </section>
  );
}

5. Streaming and Suspense

Modern React streams HTML to the browser as it becomes ready, instead of waiting for the whole page. Wrap slow parts in <Suspense> to show a fallback while they load, without blocking the rest of the page.

import { Suspense } from 'react';

function Page() {
  return (
    <>
      <Header />
      <Suspense fallback={<p>Loading comments…</p>}>
        <Comments /> {/* async Server Component */}
      </Suspense>
    </>
  );
}

The header renders immediately; the comments stream in when their data resolves.


6. Server Actions

Server Actions let a client component call server-side functions—such as writing to a database—without you manually building an API endpoint. Mark a function with 'use server'.

// app/actions.ts
'use server';

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string;
  await db.post.create({ data: { title } });
}
// A form can call the action directly
import { createPost } from './actions';

export function NewPostForm() {
  return (
    <form action={createPost}>
      <input name="title" />
      <button type="submit">Create</button>
    </form>
  );
}

7. Rendering Strategies

Modern frameworks combine several strategies, often per-route:

Strategy When it renders Best for
CSR (Client-Side Rendering) In the browser Highly interactive dashboards
SSR (Server-Side Rendering) On each request Personalized, frequently changing pages
SSG (Static Site Generation) At build time Marketing pages, docs, blogs
ISR (Incremental Static Regeneration) At build time, then revalidated Content that changes occasionally

Server Components work alongside all of these, deciding what renders on the server while these strategies decide when.


8. Best Practices


Conclusion

Modern React architecture is a blend of server and client: Server Components render and fetch data on the server for speed and security, while Client Components add interactivity exactly where it is needed. Layered with streaming, Suspense, and Server Actions—and orchestrated by frameworks like Next.js—this model delivers fast initial loads, smaller bundles, and a cleaner separation between data and interactivity. Start server-first, and reach for the client only when the UI needs to react.