Next.js·Intermediate
Next.js Server Components vs Client Components
8 Aug 2026 · 9 min read · Intermediate
- #Next.js
- #React
- #Architecture
Share
In the App Router, components are Server Components by default. Client Components are an opt-in for interactivity. Mixing them well is the core skill.
Default to the server
Server Components can:
- Read the filesystem / secrets / databases directly (on the server)
- Keep heavy dependencies off the client bundle
- Stream HTML without shipping their JS
Tsx
// app/blog/page.tsx — Server Component
import { getAllArticles } from '@/lib/posts';
export default function BlogPage() {
const articles = getAllArticles();
return <ul>{articles.map((a) => <li key={a.slug}>{a.title}</li>)}</ul>;
}When you need "use client"
Mark a Client Component when you need:
useState/useEffect/ browser APIs- Event handlers (
onClick,onChange) - Certain third-party widgets that assume the DOM
Tsx
'use client';
import { useState } from 'react';
export function SearchBox() {
const [query, setQuery] = useState('');
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search articles..."
/>
);
}Composition pattern
Keep Client Components small and leafy. Pass serializable props from the server:
Tsx
// Server
import { BlogListing } from '@/components/blog-listing';
import { getAllArticles } from '@/lib/posts';
export default function Page() {
return <BlogListing articles={getAllArticles()} />;
}Do not wrap an entire page in "use client" just because one filter chip needs state.
Shared boundaries
You can import a Client Component into a Server Component. You cannot import a Server Component into a Client Component and expect it to stay server-only—pass it as children instead:
Tsx
'use client';
export function Shell({ children }: { children: React.ReactNode }) {
return <div className="shell">{children}</div>;
}Conclusion
Ask: Does this need the browser? If not, keep it on the server. If yes, isolate the interactive island and leave data loading above it.