Files
Portfolio/blog-posts/28-nextjs-15-deep-dive.md
damjan_savicandClaude Opus 4.5 e1bbe5455d Initial commit: Portfolio Website
Vollständige Next.js 15 Portfolio-Website mit:
- Blog-System mit 100+ Artikeln
- Supabase-Integration
- Responsive Design mit Tailwind CSS
- TypeScript-Konfiguration
- Testing-Setup mit Vitest und Playwright

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 15:07:20 +01:00

512 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Next.js 15 Deep Dive: React Server Components und Streaming in Production
**Meta-Description:** Umfassende Analyse von Next.js 15 Features. React Server Components, Turbopack, Server Actions und Performance-Optimierung für 2026.
**Keywords:** Next.js 15, React Server Components, RSC, Turbopack, Server Actions, App Router, Streaming SSR, React 19
---
## Einführung
Next.js 15 ist nicht nur ein Update es ist ein **Paradigmenwechsel**. Server-first, Streaming-enabled und mit React 19 als Basis definiert es Full-Stack React 2026 neu.
> "2026 ist Next.js nicht mehr nur für Rendering es ist für skalierbare, server-first, streaming-enabled Applications."
---
## Die wichtigsten Features
```
┌─────────────────────────────────────────────────────────────┐
│ NEXT.JS 15 HIGHLIGHTS │
├─────────────────────────────────────────────────────────────┤
│ │
│ 🚀 Turbopack (Stable) │
│ └── 10x schneller als Webpack │
│ │
│ ⚛️ React 19 Support │
│ └── Server Components, Actions, Compiler │
│ │
│ 🔄 Async Request APIs │
│ └── cookies(), headers(), params() sind async │
│ │
│ 📦 Caching Changes │
│ └── fetch, GET Routes nicht mehr default cached │
│ │
│ ⚡ Partial Prerendering (Experimental) │
│ └── Static Shell + Dynamic Streaming │
│ │
└─────────────────────────────────────────────────────────────┘
```
---
## React Server Components (RSC)
### Das Konzept
```tsx
// Server Component (Default in App Router)
// Kein 'use client' = Server Component
async function ProductPage({ params }: { params: { id: string } }) {
// Direkt DB-Zugriff - kein API nötig!
const product = await prisma.product.findUnique({
where: { id: params.id }
});
// Dieses JavaScript wird NICHT an den Client gesendet
const analytics = calculateComplexAnalytics(product);
return (
<div>
<h1>{product.name}</h1>
<ProductDetails product={product} />
{/* Client Component für Interaktivität */}
<AddToCartButton productId={product.id} />
</div>
);
}
```
### Server vs. Client Components
```tsx
// ❌ Don't: Alles als Client Component
'use client';
export function ProductList() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products').then(r => r.json()).then(setProducts);
}, []);
return products.map(p => <Product key={p.id} product={p} />);
}
// ✅ Do: Server Component mit Client Component für Interaktivität
// Server Component (app/products/page.tsx)
async function ProductsPage() {
const products = await prisma.product.findMany();
return (
<div>
{products.map(p => (
<ProductCard key={p.id} product={p}>
{/* Client Component nur wo nötig */}
<FavoriteButton productId={p.id} />
</ProductCard>
))}
</div>
);
}
// Client Component (components/FavoriteButton.tsx)
'use client';
export function FavoriteButton({ productId }: { productId: string }) {
const [isFavorite, setIsFavorite] = useState(false);
return (
<button onClick={() => setIsFavorite(!isFavorite)}>
{isFavorite ? '❤️' : '🤍'}
</button>
);
}
```
---
## Server Actions
### Form Handling ohne API Route
```tsx
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
export async function createProduct(formData: FormData) {
const name = formData.get('name') as string;
const price = parseFloat(formData.get('price') as string);
// Validation
if (!name || !price) {
return { error: 'Name und Preis erforderlich' };
}
// Direkt DB-Operation
const product = await prisma.product.create({
data: { name, price }
});
// Cache invalidieren
revalidatePath('/products');
// Redirect
redirect(`/products/${product.id}`);
}
```
```tsx
// app/products/new/page.tsx
import { createProduct } from '../actions';
export default function NewProductPage() {
return (
<form action={createProduct}>
<input name="name" placeholder="Produktname" required />
<input name="price" type="number" step="0.01" required />
<button type="submit">Erstellen</button>
</form>
);
}
```
### Server Actions mit useActionState (React 19)
```tsx
'use client';
import { useActionState } from 'react';
import { createProduct } from '../actions';
export function ProductForm() {
const [state, formAction, isPending] = useActionState(
createProduct,
{ error: null }
);
return (
<form action={formAction}>
{state.error && (
<div className="text-red-500">{state.error}</div>
)}
<input name="name" placeholder="Produktname" required />
<input name="price" type="number" step="0.01" required />
<button type="submit" disabled={isPending}>
{isPending ? 'Speichern...' : 'Erstellen'}
</button>
</form>
);
}
```
---
## Streaming SSR
### Suspense für Progressive Loading
```tsx
// app/dashboard/page.tsx
import { Suspense } from 'react';
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
{/* Schneller Content zuerst */}
<Suspense fallback={<StatsSkeleton />}>
<Stats />
</Suspense>
{/* Langsamer Content streamt nach */}
<Suspense fallback={<ChartSkeleton />}>
<RevenueChart />
</Suspense>
<Suspense fallback={<TableSkeleton />}>
<RecentOrders />
</Suspense>
</div>
);
}
// Diese Components können unabhängig laden
async function Stats() {
const stats = await fetchStats(); // 100ms
return <StatsDisplay data={stats} />;
}
async function RevenueChart() {
const data = await fetchRevenueData(); // 500ms
return <Chart data={data} />;
}
async function RecentOrders() {
const orders = await fetchOrders(); // 300ms
return <OrdersTable orders={orders} />;
}
```
### Loading UI
```tsx
// app/dashboard/loading.tsx
export default function DashboardLoading() {
return (
<div className="animate-pulse">
<div className="h-8 bg-gray-200 rounded w-1/4 mb-4" />
<div className="grid grid-cols-4 gap-4 mb-8">
{[...Array(4)].map((_, i) => (
<div key={i} className="h-24 bg-gray-200 rounded" />
))}
</div>
<div className="h-64 bg-gray-200 rounded" />
</div>
);
}
```
---
## Async Request APIs (Breaking Change)
```tsx
// Next.js 14
import { cookies, headers } from 'next/headers';
export default function Page() {
const cookieStore = cookies(); // Synchron
const headersList = headers(); // Synchron
return <div>...</div>;
}
// Next.js 15
import { cookies, headers } from 'next/headers';
export default async function Page() {
const cookieStore = await cookies(); // Async!
const headersList = await headers(); // Async!
return <div>...</div>;
}
```
### Migration Codemod
```bash
npx @next/codemod@canary upgrade latest
```
---
## Caching Changes
### Neues Default-Verhalten
```tsx
// Next.js 14: Gecached by default
const data = await fetch('https://api.example.com/data');
// Next.js 15: NICHT gecached by default
const data = await fetch('https://api.example.com/data');
// Explizit cachen
const cachedData = await fetch('https://api.example.com/data', {
cache: 'force-cache' // oder next: { revalidate: 3600 }
});
```
### Route Handler Caching
```tsx
// app/api/data/route.ts
// Next.js 15: GET ist NICHT mehr default cached
export async function GET() {
const data = await fetchData();
return Response.json(data);
}
// Explizit cachen
export const dynamic = 'force-static';
export async function GET() {
// ...
}
```
---
## Turbopack in Production
### Aktivierung
```bash
# next.config.js ist nicht nötig für Dev
next dev --turbopack
# Für Build (experimentell)
TURBOPACK=1 next build
```
### Performance-Vergleich
| Metrik | Webpack | Turbopack | Verbesserung |
|--------|---------|-----------|--------------|
| **Cold Start** | 8.5s | 1.2s | 7x schneller |
| **HMR** | 500ms | 50ms | 10x schneller |
| **Full Rebuild** | 45s | 8s | 5.6x schneller |
---
## Performance Best Practices
### 1. Component Composition
```tsx
// ❌ Großes Client Component
'use client';
export function Dashboard() {
const [data, setData] = useState(null);
// Viel Logik...
return <div>...</div>;
}
// ✅ Server Component mit kleinen Client Components
export async function Dashboard() {
const data = await fetchData();
return (
<div>
<Header data={data.header} />
<Sidebar items={data.menuItems} />
<Content>
<InteractiveChart data={data.chartData} /> {/* Client */}
<FilterPanel /> {/* Client */}
</Content>
</div>
);
}
```
### 2. Data Fetching Patterns
```tsx
// ❌ Sequential Fetching
async function Page() {
const user = await fetchUser();
const posts = await fetchPosts(user.id);
const comments = await fetchComments(posts.map(p => p.id));
return <div>...</div>;
}
// ✅ Parallel Fetching
async function Page() {
const user = await fetchUser();
// Parallel fetchen
const [posts, friends] = await Promise.all([
fetchPosts(user.id),
fetchFriends(user.id)
]);
return <div>...</div>;
}
```
### 3. Streaming Optimization
```tsx
// Optimale Streaming-Architektur
export default function ProductPage({ params }) {
return (
<div>
{/* Sofort gerendert */}
<Header />
{/* Schnelle Daten zuerst */}
<Suspense fallback={<ProductSkeleton />}>
<ProductInfo id={params.id} />
</Suspense>
{/* Langsame Daten später */}
<Suspense fallback={<ReviewsSkeleton />}>
<ProductReviews id={params.id} />
</Suspense>
<Suspense fallback={<RecommendationsSkeleton />}>
<Recommendations id={params.id} />
</Suspense>
</div>
);
}
```
---
## Partial Prerendering (Experimental)
```tsx
// next.config.js
module.exports = {
experimental: {
ppr: true
}
};
// app/page.tsx
import { Suspense } from 'react';
export default function HomePage() {
return (
<div>
{/* Statisch prerendered */}
<Header />
<Hero />
{/* Dynamisch gestreamt */}
<Suspense fallback={<ProductsSkeleton />}>
<PersonalizedProducts />
</Suspense>
{/* Statisch */}
<Footer />
</div>
);
}
```
---
## Security Notes
Zwei kritische CVEs wurden für Next.js gemeldet:
- **CVE-2025-55184**: DoS via Server Components
- **CVE-2025-55183**: Source Code Exposure
**Sofortiges Update auf die neueste Version empfohlen!**
---
## Fazit
Next.js 15 verändert wie wir React-Anwendungen bauen:
1. **Server-First**: RSC als Default reduziert Client-Bundle
2. **Streaming**: Bessere Time-to-First-Byte
3. **Server Actions**: Keine API-Routes mehr für Forms
4. **Turbopack**: Dramatisch schnellere Builds
2026 ist Next.js die Plattform für moderne Full-Stack React.
---
## Bildprompts
1. "React components streaming from server to client, data flow visualization, modern web architecture"
2. "Next.js logo with speed lines, turbo boost effect, performance concept"
3. "Server and client components merging, hybrid rendering visualization, clean tech diagram"
---
## Quellen
- [Next.js 15 Release Blog](https://nextjs.org/blog/next-15)
- [Next.js Documentation](https://nextjs.org/docs)
- [React Server Components](https://nextjs.org/docs/app/getting-started/server-and-client-components)
- [Medium: Next.js 15 Deep Dive](https://medium.com/@EnaModernCoder/next-js-15-deep-dive-the-hidden-power-behind-the-latest-evolution-of-react-frameworks-e792e3e6e3ae)