Kurzfassung
Lighthouse CI (LHCI) führt bei jedem Pull Request einen Performance-Audit aus und blockiert den Merge, wenn LCP 2,5 s überschreitet, CLS 0,1 oder der Performance-Score unter 0,85 fällt. Für ein Next.js-15-Projekt auf Node 22 mit Build node scripts/build.cjs und Lint eslint ist das die günstigste Versicherung gegen „kleine CSS-Änderung“, die eine Woche später SEO und Google-Ads-Conversions schadet. Im Folgenden: fertiges lighthouserc.json, GitHub-Actions-Workflow und bewusster Tradeoff GA4 afterInteractive vs. Ads-Tags lazyOnload.
Für wen ist das
- Next.js / React-Teams mit Deploy auf Vercel oder eigenem Hosting
- Unternehmenswebsites, bei denen Marketing und Ads von schnellem LCP abhängen
- Firmen ohne dediziertes Performance-QA — LHCI ist ein Bot, der Mobile nicht vergisst
- Entwickler, die Lighthouse vierteljährlich manuell starten und hoffen, „dass es noch passt“
Keyword (SEO)
lighthouse ci nextjs, performance audit automatisieren, github actions lighthouse, core web vitals ci, lighthouserc.json, performance regression pull request
Warum ein Einmal-Audit nicht reicht
Jeder PR kann einführen:
- neues Analyse-Skript ohne
strategy="lazyOnload", - Hero-Bild ohne
width/height→ CLS-Sprung, - Import der ganzen Icon-Bibliothek statt Tree-Shaking,
- Font ohne
display: swap, - schwerere Client-Komponente statt Server Component.
Lighthouse auf next dev lokal täuscht oft — anderes Bundling, keine Produktionsoptimierung, anderer Cache. CI auf next build + next start nähert sich dem, was Nutzer und Google sehen.
Referenz-Stack (DevStudio / Next.js 15)
| Element | Wert |
|---|---|
| Framework | Next.js 15.5.x |
| Node | 22.x (engines in package.json) |
| Build | npm run build → node scripts/build.cjs |
| Lint | npm run lint → eslint |
| Produktionsstart | npm run start → next start |
| Analyse | GA4 afterInteractive, Google Ads lazyOnload |
Diese Tag-Aufteilung ist für LHCI wichtig: Ein PR, der ein weiteres Skript afterInteractive hinzufügt, kann funktional passen und Performance fallen lassen — genau das soll die Pipeline fangen.
Schwellenwerte (Mobile)
| Metrik | LHCI-Schwelle | Warum |
|---|---|---|
| LCP | ≤ 2500 ms | Hero, erster Eindruck, SEO |
| CLS | ≤ 0,1 | Springendes Formular = weniger Leads |
| INP | ≤ 200 ms (optional in assert) | Menü, Chatbot, Interaktionen |
| Performance-Score | ≥ 0,85 | Landing-Page-Qualität (Ads, Search) |
Schwellen sind bewusst streng für Marketing-Sites — eine SaaS-App mit Dashboard kann andere Budgets haben.
Datei lighthouserc.json — funktionierendes Beispiel
Im Repository-Root ablegen:
{
"ci": {
"collect": {
"numberOfRuns": 3,
"startServerCommand": "npm run start",
"startServerReadyPattern": "Ready",
"startServerReadyTimeout": 120000,
"url": [
"http://localhost:3000/pl",
"http://localhost:3000/en",
"http://localhost:3000/pl/strony-www"
],
"settings": {
"preset": "desktop",
"onlyCategories": ["performance", "accessibility", "best-practices", "seo"]
}
},
"assert": {
"preset": "lighthouse:recommended",
"assertions": {
"categories:performance": ["error", { "minScore": 0.85 }],
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
"interactive": ["warn", { "maxNumericValue": 3800 }],
"total-blocking-time": ["warn", { "maxNumericValue": 300 }]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}Praktische Hinweise:
numberOfRuns: 3— Median aus drei Läufen reduziert LHCI-Rauschen.- URLs geschäftskritisch wählen (Home PL/EN + wichtige Service-Seite).
- Für Mobile
presetauf"perf"setzen oder separaten Job mitemulatedFormFactor: "mobile"(eigene Config oder Matrix in Actions). upload.target: temporary-public-storage— schnelle Report-Links im PR ohne eigenen LHCI-Server (Produktion: LHCI Server oder GitHub-Artefakte).
GitHub Actions — vollständiger Workflow
Datei .github/workflows/lighthouse.yml:
name: Lighthouse CI
on:
pull_request:
branches: [main, master]
push:
branches: [main, master]
jobs:
lhci:
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Build
run: npm run build
env:
NODE_ENV: production
- name: Run Lighthouse CI
run: |
npm install -g @lhci/cli@0.14.x
lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}Optional separater Job mit Vercel-Preview-URL (näher am CDN):
- name: Collect against Vercel Preview
if: github.event_name == 'pull_request'
run: lhci collect --url="${{ steps.vercel.outputs.preview_url }}/pl"Preview spiegelt Edge-Cache besser, braucht aber stabile URL und Secrets — localhost nach next build ist ein guter Start ohne Extra-Infrastruktur.
Pipeline-Reihenfolge — was vor LHCI laufen muss
npm cinpm run lint— schneller Fail vor teurem Buildnpm run build— derselbe Build wie Produktion (scripts/build.cjs)lhci autorun— Server starten, 3× URL, assert
Ohne Schritt 3 misst LHCI den Dev-Server — falsches Grün.
Typische in PRs erkannte Regressionen
Marketing-Skripte
gtag ohne strategy="lazyOnload" in next/script erhöht TBT und verzögert LCP. Auf der Referenzseite:
- GA4 —
afterInteractive(generate_leadbei schnellem Submit nicht verlieren), - Google Ads (AW-...) —
lazyOnload(Performance schützen).
Ein PR, der alle Tags auf afterInteractive setzt, kann Ads reparieren und Performance zerstören — LHCI sollte das zeigen; Marketing muss den Kompromiss kennen (separater Artikel: Google-Ads-Conversion-Tracking).
Bilder und Fonts
- Hero:
priority, feste Maße, AVIF/WebP wo möglich. - Font:
next/fontoderdisplay: swap. - Logo in der Fußzeile ohne
width/height→ CLS beim Font-Laden.
JavaScript
- Dynamic Import für Chatbot und schwere Widgets.
- Große Pakete nicht im Server-Component-Layout.
Integration mit Vercel und RUM
| Schicht | Nutzen |
|---|---|
| LHCI im PR | Synthetik vor Merge — blockiert Regression |
| Vercel Analytics / Speed Insights | RUM echter Nutzer nach Deploy |
| Search Console Core Web Vitals | Google-Felddaten mit Verzögerung |
LHCI ersetzt RUM nicht — beides nutzen. LHCI = Qualitätsgate; RUM = Wahrheit im Feld (schwache Geräte, 3G, Adblock).
Praktischer Ablauf in einem kleinen Team: Entwickler öffnet PR → LHCI kommentiert LCP/CLS-Delta → Merge nur bei Grün → nach Deploy prüft Marketing Speed Insights auf der Produktionsdomain. Weicht RUM stark von LHCI ab, liegt das oft an Third-Party-Skripten (Ads, Chat-Widget), Consent-Bannern oder regionalem CDN — nicht automatisch am letzten Code-Diff. Dokumentieren Sie solche Abweichungen, damit niemand die LHCI-Schwellen „lockert“, obwohl echte Nutzer leiden.
Bot-Kommentar im PR
Mit LHCI_GITHUB_APP_TOKEN (offizielle Lighthouse-CI-App) landen Reports im Kommentar mit PR vs. Base. Ohne Token: Log in Actions und temporary-public-storage.
Erweitertes assert — SEO und Barrierefreiheit
Zusätzlich sinnvoll:
"categories:seo": ["warn", { "minScore": 0.9 }],
"categories:accessibility": ["warn", { "minScore": 0.9 }],
"categories:best-practices": ["warn", { "minScore": 0.9 }]Zuerst warn, nach Stabilisierung error. Sonst wird jeder PR mit kleiner axe-Regel rot und das Team schaltet LHCI ab.
CI-Zeitkosten
Schätzung: 3 Läufe × 3 URLs × ~40–60 s ≈ 6–10 Minuten + Next-Build (2–8 Min). Beschleunigung:
- Cache
~/.npmund.next/cachein Actions, - weniger URLs im collect (nur
/plauf Feature-Branch), - voller URL-Satz nur auf
main.
Immer noch günstiger als eine Woche Conversion-Einbruch nach LCP-Regression.
FAQ
Ersetzt Lighthouse CI manuelles PageSpeed Insights?
Nein — PSI bleibt für Ad-hoc-Checks. LHCI automatisiert dasselbe pro PR. PSI auf Produktions-URL nach Release als Kontrolle.
Nur Desktop testen?
Möglich, aber Google nutzt Mobile für CWV in Search. Minimum: ein Mobile-Job auf main, Desktop auf PR wenn CI-Zeit wehtut.
Build braucht Umgebungsvariablen — was in CI?
Nicht-geheime Variablen im Job-env:; Secrets in GitHub Secrets. build.cjs lädt oft dasselbe wie Vercel — ohne das bricht der Build vor LHCI.
PR setzt GA4 auf afterInteractive — Fehler?
Nicht immer — bewusste Produktentscheidung (Leads nicht verlieren). LHCI kann Performance-Regression zeigen — dann andere Stelle optimieren (Bilder, Fonts, Code-Split), nicht zwingend GA4 zurückdrehen.
eslint im selben Workflow wie LHCI?
Ja — schneller Fail. Separates Lint-Workflow auch OK; wichtig: Produktionsbuild als Gate vor lhci autorun.
Lokaler Lauf vor dem PR
Auf der Entwicklermaschine (Node 22):
npm run build
npm run start
# zweites Terminal:
npx @lhci/cli autorunWenn lokal grün und CI rot — Unterschiede in Umgebungsvariablen, Node-Version und Cache prüfen. Wenn lokal rot und Vercel OK — vermutlich dev statt production build gemessen.
Tipp für Windows-Entwickler: PowerShell und Git Bash unterscheiden sich manchmal bei Pfaden und Env-Loading — CI läuft auf Ubuntu. Wenn LHCI lokal auf Windows scheitert, aber Actions grün sind, zuerst WSL oder Docker mit Node 22 nutzen, bevor Schwellen angepasst werden. Das spart falsche „CI ist kaputt“-Diskussionen im Team.
Checkliste — Implementierung an einem Tag
-
lighthouserc.jsonim Repo mit LCP / CLS / Performance-Schwellen - Workflow mit Node 22,
npm ci,lint,build,lhci autorun - 2–3 conversion-kritische URLs
- Secret
LHCI_GITHUB_APP_TOKEN(optional Kommentare) - Team weiß: GA4 früh, Ads spät — nicht „alles lazyOnload“
- Nach Merge: RUM auf Produktion zur Verifikation
Beispiel-Job Mobile (eigene Matrix)
Im selben Workflow zweiter Schritt mit Mobile-Preset — kritisch für Google CWV:
- name: Run Lighthouse CI (mobile)
run: |
npm install -g @lhci/cli@0.14.x
lhci autorun --config=lighthouserc.mobile.jsonDatei lighthouserc.mobile.json — Kopie der Basis mit:
"settings": {
"preset": "perf",
"formFactor": "mobile",
"screenEmulation": { "mobile": true }
}LCP/CLS-Asserts gleich lassen — Mobile ist strenger; wer Mobile in CI besteht, ist auf Desktop meist sicher.
Was tun, wenn LHCI nach Merge fehlschlägt
- Report aus
temporary-public-storageoder Bot-Kommentar öffnen. - Opportunities prüfen (Bilder, unused JS, render-blocking).
- Diff mit Base-Branch — welcher PR brachte Regression.
- Schwellen nicht dauerhaft senken ohne Business-Grund — besser einmalige Ausnahme mit Ticket als permanent
minScore: 0.7.
Typischer Win: Skript nach lazyOnload, priority auf LCP-Bild, dynamic(() => import(...)) für Chatbot.
Wann Schwellen temporär lockern (selten)
Manchmal liefert ein PR messbare Business-Verbesserung mit minimaler Performance-Einbuße — z. B. zusätzliches Analytics-Event, das Marketing zwingend braucht. Statt dauerhaft minScore: 0.7 zu setzen: einmaliger LHCI-Waiver im PR mit Ticket, Follow-up-Task für Optimierung in der nächsten Iteration. Permanent niedrigere Schwellen machen LHCI wertlos; das Team gewöhnt sich an Gelb und verpasst echte Regressionen bei Hero-Bildern oder Font-Imports.
Bezug zu Google Ads und Landing-Page-Qualität
Google bewertet Landing-Page-Erlebnis u. a. über Geschwindigkeit und Layout-Stabilität. LCP-Regression in einem gemergten PR kann CPC erhöhen und Quality Score senken — Kosten in Ads, nicht nur in Lighthouse. Wer AW--Tags hinzufügt, sollte rotes LHCI sehen, bevor die Kampagne auf langsamerer Seite lernt.
scripts/build.cjs — warum nicht rohes next build
Im Produktionsprojekt umschließt der Build oft Env-Validierung, Prisma, Asset-Kopien vor next build. LHCI muss dieselbe Kommandozeile wie Vercel (npm run build) nutzen, sonst messen Sie ein anderes Bundle. Build in CI wegen fehlendem DATABASE_URL — Mock oder DB-Skip nur im LHCI-Job, in README dokumentieren.
Dokumentieren Sie im Team-Wiki außerdem, welche URLs in lighthouserc.json Conversion-kritisch sind — Marketing und Dev sollten dieselbe Liste pflegen, wenn neue Landing Pages dazukommen.
Zusammenfassung
Lighthouse CI auf Next.js 15 ist die Spielregel: lint → Produktionsbuild → Audit → assert. Schwellen LCP ≤ 2,5 s, CLS ≤ 0,1, Performance ≥ 0,85 schützen SEO und Landing-Page-Qualität unter Ads. Denken Sie an den Analyse-Tradeoff: GA4 afterInteractive vs. Google Ads lazyOnload — die Pipeline soll Kosten von PRs zeigen, die das Gleichgewicht stören. Synthetik in CI + RUM nach Deploy = vollständiges Bild. Node 22, eslint und scripts/build.cjs — dieselbe Qualitätskette wie Produktion.
Performance-Pipeline bei Ihnen?
- Kontakt aufnehmen — LHCI für Monorepo / Vercel konfigurieren
- Performance-Optimierung — Praxis jenseits CI
- Websites — schnelle Unternehmensseiten mit Core Web Vitals von Anfang an