September 20, 2025•3 min read
When adapting to the Next.js app router, it is tempting to aggressively default to 'use client' simply to keep existing data-fetching logic working. However, relying too heavily on client-side rendering often comes with an invisible performance tax.
We've all seen it: you load a dashboard and watch five different skeleton loaders pulse aimlessly while the browser races to fetch data. It's jarring, causes layout shifts, and search engines hate it.
There's a better way. Enter React Server Components.
The Problem with Client Components
Client components load a JavaScript bundle in the browser, parse it, and then execute it to fetch your data via tools like SWR or react-query.
During that whole process, your users are staring at a skeleton.
'use client';
import useSWR from 'swr';
import { fetcher } from '@/lib/fetcher';
export function DashboardPanel() {
const { data, isLoading } = useSWR('/api/data', fetcher);
if (isLoading) return <div className="animate-pulse">Loading...</div>;
return <div>{data.title}</div>;
}This works great if you absolutely need browser interactivity (like onClick handlers, state updates, or browser APIs). But if you're just fetching static or slowly-changing data, you're making the user's browser do heavy lifting it shouldn't have to do.
The Fix: Server Components
Server Components run entirely on your backend before the HTML is even sent to the browser.
Next.js essentially pauses rendering, waits for your data to resolve, bakes that data right into the HTML structure, and ships it.
import { getData } from '@/lib/api';
export async function DashboardPanel() {
const data = await getData();
return <div>{data.title}</div>;
}Look at how much cleaner that is. No useSWR, no useEffect, no loading states. The user loads the page and the data is just there. Instantly. No jumping layouts.
The Caching Advantage
Here is where Server Components truly pull ahead: caching.
When you configure caching on your fetch calls (like next: { revalidate: 3600 }), Server Components leverage that instantly. Because the page is generated on the server, if the cache is valid, Next.js simply hands the pre-rendered HTML to the user in milliseconds.
If you attempt to use that same 1-hour cache layer with Client Components, the browser still has to download your JavaScript bundle, parse it, and fire off the network request to your endpoint. Even if that endpoint returns a cached result instantly, the user still sees a jarring flash of a skeleton loader for a fraction of a second while the browser connects the dots.
Server Components completely eliminate that round-trip.
SEO Superpowers
Search engines don't want to wait for your JavaScript to execute to figure out what your page is about.
Because Server Components pre-render everything into static HTML, Google sees your content immediately. If you're building a blog, an e-commerce site, or a public portfolio, Server Components aren't just nice to have—they are a mandatory SEO upgrade.
When to use which?
Keep it simple:
- Use Server Components (the default) for fetching data, accessing backend resources, or rendering static UI.
- Use Client Components (
'use client') only when you need interactivity:useState, event listeners (onClick), or accessing browser-only APIs likewindow.