Add blog posts, cleanup unused files, update components
- Add 100 blog posts covering AI, development, and tech topics - Add .env.example for environment configuration - Add accessibility and lighthouse audit scripts - Remove obsolete SEO reports and temporary files - Remove dev-dist build artifacts and backup files - Remove unused portrait images (moved/consolidated elsewhere) - Update contact form and component improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,511 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user