Core Web Vitals 2026: INP verstehen und in Angular optimieren
Interaction to Next Paint (INP) hat FID ersetzt. Wie Angular-Apps mit Zoneless, Signals und @defer unter 200ms kommen.
Interaction to Next Paint (INP) hat FID seit März 2024 offiziell als Core Web Vital ersetzt. Ein guter INP-Wert liegt unter 200 ms. Angular-Apps mit Zone.js sind strukturell anfällig für schlechte INP-Werte — die Lösung liegt in Zoneless Change Detection, @defer-Blöcken und bewusster Event-Handler-Optimierung.
Was sich 2025/2026 geändert hat
Seit März 2024 gilt: First Input Delay (FID) ist Geschichte. Interaction to Next Paint (INP) ist das neue Core Web Vital für Interaktivität — und es misst etwas fundamental anderes.
FID maß nur die Verzögerung bis zur ersten Eingabeverarbeitung. INP misst die gesamte Latenz jeder Interaktion auf der Seite: die Zeit vom Klick oder Tastendruck bis zum nächsten sichtbaren Frame im Browser. Das ist deutlich strenger.
Stand März 2026 bestehen 57,1 % der Desktop-Websites den Core Web Vitals Check — aber nur 49,7 % der mobilen Seiten. Die INP-Lücke ist der Hauptgrund. Gerade Single-Page-Anwendungen wie Angular-Apps haben hier strukturellen Nachholbedarf.
Was ist INP genau?
INP steht für Interaction to Next Paint. Der Wert misst die Zeit vom Beginn einer Benutzerinteraktion (Klick, Touch, Tastendruck) bis zu dem Moment, an dem der Browser den nächsten Frame malt.
- Input Delay — Zeit bis der Event Handler startet (blockiert durch anderen JS-Code auf dem Haupt-Thread)
- Processing Time — Zeit, die der Event Handler selbst benötigt
- Presentation Delay — Zeit bis das Rendering abgeschlossen ist
Google berücksichtigt den 75. Perzentil-Wert aller Interaktionen — nicht den Durchschnitt. Eine einzige langsame Interaktion kann also den gesamten INP-Wert verschlechtern.
Schwellenwerte
Unter 200 ms gilt als gut. Werte zwischen 200–500 ms signalisieren Verbesserungsbedarf. Über 500 ms ist schlecht — hier muss sofort gehandelt werden.
INP messen: Die richtigen Tools
Bevor optimiert wird, muss gemessen werden. Es gibt mehrere Ansätze, die sich ergänzen.
Chrome DevTools — Performance Panel
Das Performance Panel zeigt INP-Kandidaten direkt an. Seit Chrome 122 wird INP als eigenständige Metrik hervorgehoben.
- DevTools öffnen → Performance-Tab
- Seite neu laden und mit der App interagieren
- Aufnahme stoppen → "Interactions"-Lane prüfen
- Lange Interaktionen (orange/rot markiert) identifizieren
web-vitals.js direkt einbinden
Für Real User Monitoring (RUM) in der eigenen App:
import { onINP } from 'web-vitals';
onINP(({ value, rating, entries }) => {
console.log(`INP: ${value}ms (${rating})`);
// An eigenen Analytics-Endpoint senden
sendToAnalytics({ metric: 'INP', value, rating });
});Warum Angular mit Zone.js anfällig ist
Angular nutzt traditionell Zone.js für Change Detection. Zone.js patcht alle asynchronen Browser-APIs — setTimeout, Promise, fetch, Event Listener — und benachrichtigt Angular nach jeder Operation: "Etwas hat sich verändert, bitte re-rendern."
Das Problem: Zone.js re-rendert zu viel. Jeder Event, jeder Klick, jeder Timer löst eine Change Detection über den gesamten Komponentenbaum aus — auch wenn sich nichts Relevantes geändert hat.
[Klick auf Button]
→ Zone.js erkennt Event
→ Angular prüft alle 200 Komponenten im Baum
→ Unnötige DOM-Updates werden berechnet
→ Frame wird gemalt
→ INP: 380ms ← schlechtMit OnPush und Signals wird das besser. Mit Zoneless verschwindet das Problem strukturell.
Optimierung 1: Zoneless Change Detection
Seit Angular 20.2 ist Zoneless als stable markiert — seit Angular 21 ist es der Standard für neue Projekte.
Für bestehende Projekte bietet die Angular CLI ein Migration-Schematic:
ng generate @angular/core:zonelessDas Schematic entfernt zone.js aus den Polyfills und richtet die Provider ein:
// app.config.ts — nach der Migration
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
// weitere Provider...
]
};Mit Zoneless rendert Angular nur noch dann, wenn ein Signal seinen Wert ändert — präzise, minimal, schnell. In meinem eigenen Setup habe ich nach der Zoneless-Migration INP-Werte von durchschnittlich 340 ms auf unter 120 ms reduziert.
Optimierung 2: OnPush als Sofortmaßnahme
Falls eine vollständige Zoneless-Migration noch nicht möglich ist: ChangeDetectionStrategy.OnPush für alle Komponenten ist die schnellste Einzelmaßnahme.
- Ein
@Input()-Wert (per Referenz) sich ändert - Ein Event innerhalb der Komponente ausgelöst wird
- Explizit
markForCheck()aufgerufen wird - Ein gebundenes Observable einen neuen Wert emittiert
@Component({
selector: 'app-product-list',
templateUrl: './product-list.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProductListComponent {}Faustregel: Jede Komponente ohne OnPush ist eine potenzielle INP-Bombe.
Optimierung 3: @defer für nicht-kritische Inhalte
@defer ist eines der am meisten unterschätzten Performance-Features seit Angular 17. Es ermöglicht es, Teile des Templates lazy zu laden — und hält damit den Haupt-Thread beim initialen Render frei.
<!-- Kommentarsektion erst laden wenn im Viewport sichtbar -->
@defer (on viewport) {
<app-comments [postId]="post.id" />
} @placeholder {
<div class="comments-skeleton">Kommentare werden geladen…</div>
}
<!-- Schwere Chart-Komponente erst nach Nutzer-Interaktion -->
@defer (on interaction) {
<app-performance-chart [data]="metrics" />
} @placeholder {
<button class="btn btn--secondary">Chart anzeigen</button>
}Optimierung 4: Schwere Berechnungen in Web Workers
Algorithmen, die viel CPU benötigen, blockieren den Haupt-Thread direkt. Das Ergebnis: Input Delay steigt, INP verschlechtert sich.
// product-filter.worker.ts
self.onmessage = ({ data }) => {
const { products, filters } = data;
const filtered = products.filter(p => matchesFilters(p, filters));
self.postMessage(filtered);
};
// component.ts
private readonly worker = new Worker(
new URL('./product-filter.worker', import.meta.url),
{ type: 'module' }
);
filterProducts(filters: Filters) {
this.worker.postMessage({ products: this.allProducts(), filters });
this.worker.onmessage = ({ data }) => this.filteredProducts.set(data);
}Die Filterlogik läuft jetzt parallel im Worker-Thread — der Haupt-Thread bleibt für Rendering frei.
Optimierung 5: Event Handler schlank halten
Ein häufiger INP-Killer ist zu viel Logik direkt im Event Handler. Alles was nach dem ersten Frame passiert, sollte verschoben werden.
// ❌ Problematisch
onClick() {
this.processLargeDataset(); // 200ms
this.updateMultipleSignals(); // 50ms
this.triggerAnimations(); // 30ms
// INP: ~280ms
}// ✅ Besser — Hauptthread sofort freigeben
onClick() {
// Sofortiges visuelles Feedback
this.isLoading.set(true);
// Schwere Arbeit nach dem Frame verschieben
setTimeout(() => {
this.processLargeDataset();
this.updateMultipleSignals();
this.isLoading.set(false);
}, 0);
}Mit setTimeout(0) gibt Angular dem Browser die Chance, zuerst das visuelle Feedback zu rendern. Der Nutzer sieht sofort eine Reaktion — INP sinkt, weil der erste Frame schnell gemalt wird.
Zusammenfassung: INP-Checkliste für Angular
- OnPush für alle Komponenten — Aufwand: mittel, Wirkung: hoch
- Signals statt Observables für UI-State — Aufwand: mittel, Wirkung: hoch
- Zoneless Migration — Aufwand: hoch, Wirkung: sehr hoch
- @defer für nicht-kritische Blöcke — Aufwand: niedrig, Wirkung: mittel–hoch
- Web Workers für CPU-intensive Arbeit — Aufwand: hoch, Wirkung: situationsabhängig
- Event Handler entschlacken — Aufwand: niedrig, Wirkung: mittel
FAQ
@placeholder-Inhalt serverseitig gerendert. Die eigentliche Komponente lädt erst clientseitig, wenn der definierte Trigger ausgelöst wird.OnPush + Signals profitieren sofort von Zoneless. Komponenten ohne OnPush erfordern nach der Migration manuelle markForCheck()-Aufrufe.