Next.js verwandelt React in eine vollständige Suite von Tools für moderne Webentwicklung. Die neueste Version bringt revolutionäre Funktionen, die unsere Art der Entwicklung von Webanwendungen verändern. Entdecken wir, warum Next.js unverzichtbar geworden ist!
Die Stärken von Next.js 15 🎯
- App Router - Ein modernes, dateibasiertes Routing-System
- Server Components - Standardmäßig serverseitig gerenderte React-Komponenten
- Server Actions - Servermutationen direkt aus Ihren Komponenten
- Streaming - Progressives Laden von Seiten für eine flüssige UX
- Intelligenter Cache - Automatische Datenzwischenspeicherung
- Zero Config - Sofort einsatzbereit mit integrierten Optimierungen
Schnellstart ⚙️
1# Neues Projekt erstellen
2npx create-next-app@latest meine-app --typescript --tailwind --app
3
4# Entwicklungsserver starten
5cd meine-app
6npm run dev
Moderne Architektur 📂
Der App Router führt eine intuitivere Projektstruktur ein:
1meine-app/
2├── app/
3│ ├── layout.tsx # Root Layout
4│ ├── page.tsx # Startseite (/)
5│ ├── loading.tsx # Globales Laden
6│ ├── error.tsx # Fehlerbehandlung
7│ ├── not-found.tsx # 404-Seite
8│ ├── blog/
9│ │ ├── page.tsx # /blog
10│ │ └── [slug]/
11│ │ └── page.tsx # /blog/:slug
12│ └── api/
13│ └── route.ts # API-Routen
14├── components/
15│ ├── server/ # Server-Komponenten
16│ ├── client/ # Client-Komponenten
17│ └── ui/ # UI-Komponenten
18└── lib/
19 └── actions.ts # Server Actions
Server Components ⚡️
Komponenten werden standardmäßig serverseitig gerendert für bessere Performance:
1// app/blog/page.tsx
2import { PostList } from '@/components/server/PostList';
3import { Suspense } from 'react';
4
5export default async function BlogPage() {
6 return (
7 <main className="container mx-auto px-4 py-8">
8 <h1 className="text-4xl font-bold mb-8">Unser Blog</h1>
9 <Suspense fallback={<p>Beiträge werden geladen...</p>}>
10 <PostList />
11 </Suspense>
12 </main>
13 );
14}
15
16// components/server/PostList.tsx
17async function getPosts() {
18 const posts = await db.post.findMany();
19 return posts;
20}
21
22export async function PostList() {
23 const posts = await getPosts();
24
25 return (
26 <div className="grid gap-6 md:grid-cols-2">
27 {posts.map(post => (
28 <article key={post.id} className="p-4 rounded-lg border">
29 <h2 className="text-xl font-semibold">{post.title}</h2>
30 <p className="mt-2 text-gray-600">{post.excerpt}</p>
31 </article>
32 ))}
33 </div>
34 );
35}
Client Components 🔄
Für Komponenten, die Interaktivität benötigen:
1// components/client/SearchBar.tsx
2'use client';
3
4import { useState } from 'react';
5import { useRouter } from 'next/navigation';
6
7export function SearchBar() {
8 const [query, setQuery] = useState('');
9 const router = useRouter();
10
11 function handleSearch(e: React.FormEvent) {
12 e.preventDefault();
13 router.push(`/search?q=${encodeURIComponent(query)}`);
14 }
15
16 return (
17 <form onSubmit={handleSearch} className="flex gap-2">
18 <input
19 type="search"
20 value={query}
21 onChange={(e) => setQuery(e.target.value)}
22 placeholder="Suchen..."
23 className="px-4 py-2 rounded border"
24 />
25 <button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded">
26 Suchen
27 </button>
28 </form>
29 );
30}
Server Actions ⚡️
Serverseitige Mutationen direkt aus Ihren Komponenten:
1// lib/actions.ts
2'use server';
3
4import { revalidatePath } from 'next/cache';
5import { redirect } from 'next/navigation';
6import { z } from 'zod';
7
8const PostSchema = z.object({
9 title: z.string().min(3, "Der Titel muss mindestens 3 Zeichen enthalten"),
10 content: z.string().min(10, "Der Inhalt muss mindestens 10 Zeichen enthalten")
11});
12
13export async function createPost(formData: FormData) {
14 const data = {
15 title: formData.get('title'),
16 content: formData.get('content')
17 };
18
19 const result = PostSchema.safeParse(data);
20
21 if (!result.success) {
22 return { error: result.error.flatten() };
23 }
24
25 const post = await db.post.create({
26 data: result.data
27 });
28
29 revalidatePath('/blog');
30 redirect('/blog');
31}
32
33// app/blog/new/page.tsx
34import { createPost } from '@/lib/actions';
35
36export default function NewPostPage() {
37 return (
38 <form action={createPost} className="space-y-4 max-w-lg mx-auto">
39 <div>
40 <label htmlFor="title" className="block text-sm font-medium">
41 Titel
42 </label>
43 <input
44 type="text"
45 name="title"
46 id="title"
47 required
48 className="mt-1 block w-full rounded-md border-gray-300"
49 />
50 </div>
51 <div>
52 <label htmlFor="content" className="block text-sm font-medium">
53 Inhalt
54 </label>
55 <textarea
56 name="content"
57 id="content"
58 required
59 rows={5}
60 className="mt-1 block w-full rounded-md border-gray-300"
61 />
62 </div>
63 <button
64 type="submit"
65 className="w-full py-2 px-4 bg-blue-500 text-white rounded-md"
66 >
67 Veröffentlichen
68 </button>
69 </form>
70 );
71}
Moderne API-Routen 🛠️
1// app/api/posts/route.ts
2import { NextResponse } from 'next/server';
3import { z } from 'zod';
4
5const PostSchema = z.object({
6 title: z.string().min(3),
7 content: z.string().min(10),
8});
9
10export async function GET(request: Request) {
11 const { searchParams } = new URL(request.url);
12 const query = searchParams.get('q');
13
14 const posts = await db.post.findMany({
15 where: query
16 ? {
17 OR: [
18 { title: { contains: query } },
19 { content: { contains: query } },
20 ],
21 }
22 : undefined,
23 });
24
25 return NextResponse.json({ posts });
26}
27
28export async function POST(request: Request) {
29 try {
30 const body = await request.json();
31 const result = PostSchema.safeParse(body);
32
33 if (!result.success) {
34 return NextResponse.json(
35 { error: result.error.flatten() },
36 { status: 400 },
37 );
38 }
39
40 const post = await db.post.create({
41 data: result.data,
42 });
43
44 return NextResponse.json(post, { status: 201 });
45 } catch (error) {
46 return NextResponse.json({ error: 'Serverfehler' }, { status: 500 });
47 }
48}
Optimierungen und Best Practices 🚀
1. Streaming mit Suspense
1import { Suspense } from 'react';
2import { PostList } from '@/components/server/PostList';
3import { SidebarNav } from '@/components/server/SidebarNav';
4import { Loading } from '@/components/ui/Loading';
5
6export default function BlogLayout({
7 children
8}: {
9 children: React.ReactNode
10}) {
11 return (
12 <div className="flex gap-8">
13 <Suspense fallback={<Loading />}>
14 <SidebarNav />
15 </Suspense>
16 <main className="flex-1">
17 <Suspense fallback={<Loading />}>
18 {children}
19 </Suspense>
20 </main>
21 <aside className="w-64">
22 <Suspense fallback={<Loading />}>
23 <PostList type="recent" />
24 </Suspense>
25 </aside>
26 </div>
27 );
28}
2. Fehlerbehandlung
1// app/error.tsx
2'use client';
3
4export default function Error({
5 error,
6 reset
7}: {
8 error: Error & { digest?: string };
9 reset: () => void;
10}) {
11 return (
12 <div className="flex flex-col items-center justify-center min-h-[400px]">
13 <h2 className="text-2xl font-bold mb-4">
14 Ein Fehler ist aufgetreten!
15 </h2>
16 <button
17 onClick={() => reset()}
18 className="px-4 py-2 bg-blue-500 text-white rounded"
19 >
20 Erneut versuchen
21 </button>
22 </div>
23 );
24}
3. Optimierte Bildladung
1import Image from 'next/image';
2
3export function Avatar({ src, alt }: { src: string; alt: string }) {
4 return (
5 <div className="relative w-10 h-10">
6 <Image
7 src={src}
8 alt={alt}
9 fill
10 sizes="40px"
11 className="rounded-full object-cover"
12 priority
13 />
14 </div>
15 );
16}
Weiterführende Informationen 🎈
Zusammenfassung ✨
Next.js 15 stellt eine bedeutende Evolution in der React-Webentwicklung dar mit:
- Server-First: Standardmäßig Server-Komponenten für bessere Performance
- Einfachheit: Eine intuitive, dateibasierte Architektur
- Leistung: Automatische Optimierungen und Streaming
- Verbesserte DX: Moderne Server Actions und API-Routen
Die Kombination aus App Router, Server Components und Server Actions bietet eine solide Grundlage für den Aufbau moderner, effizienter und wartbarer Webanwendungen.
Jetzt, da Sie die Grundlagen von Next.js 14 verstehen, sind Sie bereit, moderne und effiziente Webanwendungen zu erstellen! Zögern Sie nicht, die offizielle Dokumentation zu konsultieren, um tiefer in jedes Konzept einzutauchen. Viel Spaß beim Entwickeln! 🚀