# Server Components in Next.js Explained Simply
Introduction
"Wait, so components run on the server now? But React runs in the browser, right?"
If you've been confused by Server Components, you're not alone. When Next.js 13 introduced the App Router with React Server Components, many developers scratched their heads. How do components run on the server? What about useState? When do I use "use client"?
The shift from Next.js 12 to Next.js 13–15 represents a fundamental change in how we build React apps. The goal? Faster, more efficient web applications that deliver less JavaScript to users while maintaining the interactivity we love.
This guide will explain Server Components in simple terms, show you exactly when to use them, and help you understand how they work alongside traditional Client Components. No jargon, just clear explanations with practical examples.
Let's demystify Server Components once and for all.
What Are React Server Components (RSC)?
Here's the simplest definition:
Server Components are React components that run only on the server—never in the browser.
Think about it this way: traditionally, all React components get bundled into JavaScript, sent to the browser, and executed there. With Server Components, the component runs on the server, renders to a special format, and sends only the HTML result to the browser.
The Key Difference
Traditional React Component (Client Component):
- Server sends JavaScript bundle
- Browser downloads the bundle
- React executes and renders the component
- User sees the content
Server Component:
- Server runs the component
- Server sends rendered result (not JavaScript)
- User sees the content immediately
- No component JavaScript in the bundle
This means Server Components never increase your JavaScript bundle size. They don't exist in the client-side code at all.
Next.js Leads the Way
Next.js was the first major framework to fully adopt React Server Components. As of Next.js 13+, all components in the App Router are Server Components by default—unless you explicitly mark them as Client Components.
This is a huge shift from Next.js 12 and earlier, where everything was a Client Component by default.
Server Components vs Client Components
Let's break down the differences with a clear comparison:
| Feature | Server Component | Client Component |
|---|---|---|
| Runs On | Server | Browser |
| Can Use Hooks like 'useState'? | ❌ No | ✅ Yes |
| Can Fetch Data Directly? | ✅ Yes | ⚠️ Needs API |
| Increases Bundle Size? | ❌ No | ✅ Yes |
| Can Access Backend Resources? | ✅ Yes (DB, filesystem) | ❌ No |
| Can Use Browser APIs? | ❌ No | ✅ Yes (window, localStorage) |
| Ideal For | Data fetching, static content | Interactive UI, forms |
Mixing Both: The Power of Hybrid Apps
The magic happens when you combine both types. Use Server Components for the heavy lifting (data fetching, rendering static content) and Client Components for interactivity (buttons, forms, animations).
Next.js makes this seamless. You can nest Client Components inside Server Components, creating powerful hybrid applications that are both fast and interactive.
How Server Components Improve Performance
Server Components deliver multiple performance benefits:
1. Smaller JavaScript Bundles
Server Components don't ship JavaScript to the browser. If you have a large data-fetching component that renders a list, all that code stays on the server.
Before (Client Component):
Bundle size: 150KB (includes component + dependencies)
After (Server Component):
Bundle size: 0KB (component runs on server)
2. Automatic Data Fetching
Server Components can fetch data directly from databases or APIs without creating separate API routes:
typescript
// This runs on the server, not the browser
async function ProductList() {
const products = await db.products.findMany();
return {products.map(p => {p.name} )};
}
No need for:
- Client-side fetch calls
- Loading states
- Error handling on the client
- Extra API endpoints
3. Faster First Paint
Because Server Components render on the server, users see content faster. There's no waiting for JavaScript to download, parse, and execute before seeing the page.
4. Less Hydration
Hydration is the process where React "activates" server-rendered HTML in the browser. Server Components don't need hydration because they don't have client-side JavaScript. This reduces the work the browser needs to do.
Example: Before and After
Old Way (Client Component):
typescript
"use client";
import { useState, useEffect } from 'react';
export default function Page() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products')
.then(res => res.json())
.then(data => setProducts(data));
}, []);
return (
Shop
{products.map(p => {p.name})}
);
}
New Way (Server Component):
typescript
// app/page.tsx
import ProductList from './ProductList';
export default function Page() {
return (
Shop
);
}
// ProductList.tsx (Server Component by default)
async function ProductList() {
const res = await fetch('https://api.example.com/products');
const products = await res.json();
return (
{products.map(p => {p.name})}
);
}
The second version is simpler, faster, and requires zero client-side JavaScript for the data fetching logic.
When to Use Server vs Client Components
Understanding when to use each type is crucial for building efficient Next.js apps.
Use Server Components For:
✅ Fetching data from databases or external APIs
typescript
async function BlogPosts() {
const posts = await db.post.findMany();
return ;
}
✅ Rendering static content
typescript
function AboutPage() {
return (
About Us
We've been in business since 2020...
);
}
✅ SEO-critical pages Server Components render on the server, making content immediately available to search engines.
✅ Accessing backend resources
typescript
async function ServerLogs() {
const logs = await fs.readFile('/var/log/app.log', 'utf-8');
return {logs}
;
}
Use Client Components For:
✅ Interactive elements requiring state
typescript
"use client";
import { useState } from 'react';
export default function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? "❤️ Liked" : "♡ Like"}
);
}
✅ Forms and user inputs
typescript
"use client";
import { useState } from 'react';
export default function ContactForm() {
const [email, setEmail] = useState('');
return (
);
}
✅ Browser-specific features
typescript
"use client";
import { useEffect } from 'react';
export default function ThemeToggle() {
useEffect(() => {
const theme = localStorage.getItem('theme');
document.body.className = theme;
}, []);
return ;
}
✅ Animations and interactive dashboards Anything using event listeners, timers, or browser APIs needs to be a Client Component.
The "use client" Directive
To mark a component as a Client Component, add '"use client"' at the top of the file:
typescript
"use client";
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count};
}
Important: You only need this at the "boundary" where you transition from server to client. Child components automatically inherit the client behavior.
The Role of the App Directory in Next.js
The '/app' directory is where Next.js 13+ enables React Server Components by default.
Key Files in the App Directory
'page.tsx' - The main page component (Server Component by default) 'layout.tsx' - Wraps pages with shared UI (Server Component by default) 'loading.tsx' - Shows while the page loads (Server Component) 'error.tsx' - Handles errors (must be Client Component)
Example Folder Structure
app/
├── layout.tsx (Server Component)
├── page.tsx (Server Component)
├── loading.tsx (Server Component)
├── dashboard/
│ ├── page.tsx (Server Component)
│ ├── Chart.tsx (Client Component with "use client")
│ └── Sidebar.tsx (Server Component)
└── blog/
├── page.tsx (Server Component)
└── [slug]/
└── page.tsx (Server Component)
Default Behavior
In the App Router:
- Everything is a Server Component unless you add '"use client"'
- This is the opposite of the Pages Router (Next.js 12), where everything was a Client Component
This default makes sense: most components don't need interactivity. They just render content.
Data Fetching with Server Components
One of the most powerful features of Server Components is direct data fetching.
Direct API Calls
No need for API routes in many cases:
typescript
// app/posts/page.tsx
export default async function Posts() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await res.json();
return (
Blog Posts
{posts.slice(0, 5).map((post) => (
- {post.title}
))}
);
}
Direct Database Access
With Server Components, you can query your database directly:
typescript
// app/products/page.tsx
import { db } from '@/lib/db';
export default async function Products() {
const products = await db.product.findMany({
where: { inStock: true },
orderBy: { createdAt: 'desc' },
});
return (
{products.map(product => (
))}
);
}
Why This Is Powerful
No API routes needed - Skip the middleman for many use cases
Data ready on first render - No loading spinners for initial data
Better SEO - Content is available immediately for search engines
Secure - Database credentials and secrets never reach the client
Faster - Fewer network roundtrips
Caching and Revalidation
Next.js automatically caches fetch requests. You can control this:
typescript
// Revalidate every 60 seconds
export const revalidate = 60;
export default async function Posts() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return ;
}
Common Misconceptions
Let's clear up some confusion:
❌ "Server Components replace APIs"
Not true. APIs are still important for:
- Client-side data fetching
- Third-party integrations
- Mobile apps
- Public data endpoints
Server Components simplify data fetching for web pages, but APIs remain valuable.
❌ "You can't use any hooks"
Partially true. You can't use hooks that require client-side reactivity (useState, useEffect, etc.), but you can use:
- Context (for sharing data between Server Components)
- Async/await (since Server Components are async)
❌ "Server Components are complex"
Actually simpler when structured properly. They remove the need for:
- useState for loading/error states
- useEffect for data fetching
- API route creation
- Client-side fetch logic
The code becomes more straightforward.
❌ "Everything should be a Server Component"
Not true. Use the right tool for the job:
- Interactive UI → Client Component
- Static content/data fetching → Server Component
Benefits at a Glance
Here's what you gain with Server Components:
🚀 Faster Page Loads Less JavaScript to download and execute
💾 Smaller JS Bundles Server Components add zero bytes to your bundle
⚡ Better SEO and TTFB Content renders server-side, improving Time to First Byte
🧩 Simpler Data Fetching No need for useEffect and loading states
🔒 More Secure Backend secrets never exposed to the client
🎯 Better Performance Less hydration and faster interactivity
Conclusion
React Server Components represent a fundamental shift in how we build modern web applications. They're not just a Next.js feature—they're a React innovation that Next.js pioneered.
The key insight is simple: not everything needs to run in the browser. By moving non-interactive code to the server, we create faster, lighter applications that still deliver the rich interactivity users expect.
The Mental Model
Think of your app in two layers:
- Server Components - The static shell that fetches data and renders structure
- Client Components - The interactive islands that respond to user actions
Master this separation, and you've unlocked Next.js superpowers.
Getting Started
If you're building a new Next.js 15 project:
- Start with Server Components by default
- Add '"use client"' only when you need interactivity
- Fetch data directly in Server Components
- Keep your bundle size small
The beauty of Server Components is that once you understand which parts belong on the server and which on the client, everything clicks into place.
Go experiment. Build something. Break the mental model of "React only runs in the browser." The future of web development is here, and it's faster than ever.
Happy coding! 🚀




