Back to the blog
React· Jun 19, 2026· 8 min read

React Server Components: a practical decision framework for 2026

RSC is the default in every major React framework now. The question is no longer whether to use it - it's knowing exactly where to put the client boundary.

#react#rsc#performance#typescript

React Server Components are the default in Next.js, TanStack Start, and Remix in 2026. If you're starting a new React project, you're using RSC unless you actively opt out. That means the relevant question has shifted from 'should we use RSC?' to 'where exactly does the client boundary belong?' - and getting that decision wrong is the most common source of RSC-related performance regressions we see in code reviews.

The mental model that helps

Server Components render on the server, send HTML to the client, and ship zero JavaScript for the component itself. They can read from a database, access the filesystem, and use server-only secrets, but they can't use hooks, respond to events, or maintain state. Client Components work exactly like React has always worked - hooks, events, state, browser APIs - but they ship JavaScript to the client. The key insight is that these aren't two separate React systems: a Server Component can render Client Components as children. The tree flows server-first, with client islands where interactivity is needed.

// Server Component - no 'use client', runs only on server
export async function ProductPage({ id }: { id: string }) {
  // Direct DB access - no API round trip
  const product = await db.products.findUnique({ where: { id } });

  return (
    <article>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      {/* AddToCart is a Client Component - only this subtree ships JS */}
      <AddToCart productId={id} price={product.price} />
    </article>
  );
}

// 'use client' marks the boundary - everything from here down
// is a client component and ships JavaScript
'use client';
export function AddToCart({ productId, price }: Props) {
  const [qty, setQty] = useState(1);
  return <button onClick={() => addToCart(productId, qty)}>Add to cart</button>;
}

The decision framework

The rule of thumb that actually works in practice: start everything as a Server Component. Only add 'use client' when you hit a concrete reason that requires it.

  • Add 'use client' when you need event handlers (onClick, onChange, onSubmit).
  • Add 'use client' when you need hooks (useState, useEffect, useContext, custom hooks).
  • Add 'use client' when you need browser-only APIs (window, localStorage, IntersectionObserver).
  • Add 'use client' as deep in the component tree as possible - if only a button needs interactivity, don't mark the entire page client.
  • Never add 'use client' because a child component needs it - the directive propagates downward; put it on the child, not the parent.

The mistakes worth avoiding

The most common mistake is marking large layout components as 'use client' because they contain a single interactive element somewhere deep in the tree. This turns what should be server-rendered HTML into a large JavaScript bundle. The fix is to extract the interactive element into its own small Client Component and leave the surrounding layout as a Server Component. A 40kb page that's 95% static HTML and 5% client JavaScript will always outperform a 40kb page that's 100% JavaScript.

Data fetching patterns that work

Server Components eliminate the client-side data fetching waterfall for initial page loads - you fetch directly in the component that needs the data, with no useEffect, no loading state for the initial render, and no API route required. For data that updates after the page loads, TanStack Query or SWR on the client side still applies. The pattern that's emerged: Server Components for initial data, Client Components with TanStack Query for mutations and live-updating state.

Server Components aren't about avoiding Client Components - they're about making Client Components pay the JavaScript cost only where interactivity actually earns it.

Written by Appesto Engineering.