Warum Ihre Next.js-App langsam ist: 7 Leistungsoptimierungen, die wirklich funktionieren
Ihre Next.js-App lädt in mehr als 4 Sekunden. Der Lighthouse-Score liegt bei 60. Die Nutzer springen ab. Hier sind 7 konkrete Lösungen mit Vorher-Nachher-Messungen – keine vagen Ratschläge, sondern nur Code.
1. Du sendest 500 KB JavaScript an den Browser
Führen Sie „npx @next/bundle-analyzer“ aus und überprüfen Sie, was in Ihrem Client-Bundle enthalten ist. Häufige Übeltäter: moment.js (300 KB → durch date-fns ersetzen), lodash (70 KB → nur bestimmte Funktionen importieren), Icon-Bibliotheken (alle Icons statt nur bestimmter importieren).
// BAD: imports entire library (70KB)
import _ from 'lodash';
const result = _.debounce(fn, 300);
// GOOD: imports only what you need (2KB)
import debounce from 'lodash/debounce';
const result = debounce(fn, 300);
// BAD: imports all 1000+ icons (200KB)
import { ArrowRight } from 'lucide-react';
// GOOD: tree-shakeable import
import ArrowRight from 'lucide-react/dist/esm/icons/arrow-right';2. Jede Komponente ist „use client“
Wenn du bei jeder Komponente „use client“ verwendest, sendest du das gesamte JavaScript an den Browser. Serverkomponenten werden auf dem Server gerendert und senden HTML – ohne JavaScript. Verwende „use client“ nur für Komponenten, die Interaktivität erfordern (onClick, useState, useEffect).
// Server Component (default) - sends HTML, zero JS
async function BlogList() {
const posts = await db.posts.findMany();
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
{/* Only the button is client-side */}
<LikeButton postId={post.id} />
</article>
))}
</div>
);
}
// Client Component - only the interactive part
'use client';
function LikeButton({ postId }: { postId: string }) {
const [liked, setLiked] = useState(false);
return <button onClick={() => setLiked(!liked)}>Like</button>;
}3. Bilder ohne „next/image“
Raw-Tags <img> laden das Bild in voller Größe. next/image passt die Größe automatisch an, konvertiert das Bild in WebP/AVIF und lädt es verzögert. Allein dadurch lässt sich der LCP um 40–60 % reduzieren.
// BAD: loads full 4MB image
<img src="/hero.jpg" />
// GOOD: Next.js optimizes, lazy loads, serves WebP
import Image from 'next/image';
<Image
src="/hero.jpg"
width={1200}
height={600}
priority // for above-the-fold images
alt="Hero image"
/>4. Datenabruf auf dem Client, wenn SSR möglich wäre
Wenn du useEffect + fetch verwendest, um bei jedem Seitenaufruf Daten zu laden, lässt du den Nutzer auf Folgendes warten: HTML-Download → JS-Parsing → React-Hydration → Fetch-Anfrage → Rendering. Bei Server-Komponenten erfolgt das Abrufen der Daten auf dem Server, und der Nutzer erhält sofort den vollständigen HTML-Code.
5. Fehlende Caching-Header bei statischen Assets
Die statischen Dateien von Next.js im Verzeichnis /_next/static/ sind inhaltsbasiert gehasht und unveränderlich. Ihr Nginx sollte sie intensiv zwischenspeichern:
# Nginx: cache Next.js static files for 1 year
location /_next/static {
proxy_pass http://frontend:3000;
add_header Cache-Control "public, max-age=31536000, immutable";
}6. Keine Verwendung von React.lazy für ressourcenintensive Komponenten
Komponenten wie Rich-Text-Editoren, Diagramme und Karten sind sehr groß. Laden Sie sie erst bei Bedarf, damit sie das anfängliche Rendern nicht blockieren:
import dynamic from 'next/dynamic';
// Only loads when component is rendered
const Chart = dynamic(() => import('@/components/Chart'), {
loading: () => <div className="h-64 animate-pulse bg-gray-800" />,
ssr: false, // chart library needs window
});7. Keine Schriftoptimierung
Benutzerdefinierte Schriftarten verursachen Layoutverschiebungen (CLS), wenn sie erst spät geladen werden. Verwenden Sie „next/font“, um Schriftarten selbst zu hosten und vorab zu laden:
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // show fallback font until loaded
});
export default function Layout({ children }) {
return <body className={inter.className}>{children}</body>;
}Erwartete Ergebnisse
| Metrisch | Zuvor | Nach allen Korrekturen |
|---|---|---|
| Lighthouse-Leistung | 58 | 94 |
| Erstes rendern von Inhalten | 2,8 s | 0,8 s |
| Largest Contentful Paint | 4,2 s | 1,4 s |
| Gesamtes JS-Paket | 520 KB | 180 KB |
| Kumulative Layoutverschiebung | 0,25 | 0,02 |
