
by Zelkulon12 March 20251 min read
Next.js builds on React β but the differences are enormous. Server Components, App Router, Streaming: what does this mean concretely for architecture?
What Does Each Do?
React (Library): Next.js (Framework): ββββββββββββββββββββ ββββββββββββββββββββββββββββββββ β UI-Komponenten β Everything from React β State Management β File-based Routing β Hooks β Server Components (RSC) β Routing (choose yourself) β Server-side Rendering (SSR) β Server-Rendering β Static Site Generation (SSG) β Image optimization β API Routes + Image optimization
Rendering Strategies Compared
Strategy When rendered? When refreshed? Use Case ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ SSG (Static) Build time New build Blog, Docs ISR Build + Interval After X seconds Product list SSR Every request Every request Dashboard RSC (Server) Every request On demand Default Client Comp. Browser On demand Interactive
Server Component vs. Client Component
// Server Component (default in Next.js App Router)
// NO "use client" β runs on the server
async function BlogPage() {
// Direct DB/API access on the server
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 } // ISR: revalidate hourly
}).then(r => r.json());
return (
<div>
{posts.map((post: Post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
// Client Component β only where needed
"use client";
export function SearchBar() {
const [query, setQuery] = useState('');
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search..."
/>
);
}Data Fetching: useEffect vs. Server Component
// React (useEffect pattern) β in the browser
function ProductList() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/products')
.then(r => r.json())
.then(data => { setProducts(data); setLoading(false); });
}, []);
if (loading) return <Spinner />;
return <ProductGrid products={products} />;
}
// Next.js (Server Component) β on the server
async function ProductList() {
const products = await getProducts(); // No loading state needed!
return <ProductGrid products={products} />;
}
// Suspense for Streaming
export default function Page() {
return (
<Suspense fallback={<ProductSkeleton />}>
<ProductList /> {/* Skeleton until data arrives */}
</Suspense>
);
}When React Alone, When Next.js?
Choose pure React (+ Vite) when: ββββββββββββββββββββββββββββββββββ β Single-Page App (SPA) behind login β SEO is not important β Maximum freedom desired Examples: Admin dashboard, internal tools Choose Next.js when: ββββββββββββββββββββ β SEO matters (landing pages, blog, e-commerce) β Initial load time should be minimal β Full-stack with API routes desired β Large application with many routes Examples: Company website, SaaS, e-commerce
Performance Comparison
Typical values in practice:
- First Contentful Paint: React CSR ~2.5s | Next.js SSR ~0.5s | SSG ~0.2s
- Time to Interactive: React CSR ~3.5s | Next.js SSR ~1.5s | SSG ~0.5s
- Bundle size: React CSR 300-500 KB | Next.js RSC 80-150 KB | SSG 30-80 KB
- SEO score: React CSR average | Next.js SSR very good | SSG excellent