Zum Inhalt springen
aviral gupta

INP im Next.js App Router beheben, Animationen behalten

Ein schlechter INP-Wert auf einer Next.js-App-Router-Seite liegt fast nie an der Animation. Er liegt an JavaScript, das der Browser gar nicht erst hätte laden müssen, und die Lösung ist eine kleinere Client-Grenze, nicht das Löschen von Motion.

Geschrieben von Aviral GuptaVeröffentlicht 9 Min. Lesezeit
  • Core Web Vitals
  • INP
  • Next.js
  • App Router
  • Performance
INP im Next.js App Router beheben, Animationen behaltenLIGHTHOUSE MOBILE · HOME · 08.2026TBT106ms≤ 200msCLS0.000≤ 0.10LCP2.56s≤ 2.5sBUDGET

Was misst INP eigentlich?

Interaction to Next Paint misst genau eine Sache: die Zeitspanne zwischen der Berührung der Seite und dem Frame, den der Browser malt, um zu zeigen, dass etwas passiert ist. Chrome erfasst das für jeden Klick, Tap und Tastendruck während eines Besuchs und meldet dann annähernd den schlechtesten Wert. Der dokumentierte Schwellenwert liegt bei 200ms am 75. Perzentil aller Besuche, alles über 500ms gilt als schlecht (web.dev zu INP).

Diese eine Zahl besteht aus drei voneinander unabhängigen Problemen, die übereinandergestapelt sind — und die Lösung für jedes ist eine andere:

  • Input Delay — der Main Thread war beschäftigt, als der Nutzer geklickt hat, sodass Ihr Handler nicht einmal starten konnte. Hier stecken Hydration, Third-Party-Tags und Long Tasks.
  • Processing Duration — der Handler lief, React hat neu gerendert, und das hat Zeit gekostet. Hier stecken die Größe des Komponentenbaums und der Ort, an dem State liegt.
  • Presentation Delay — die Arbeit ist erledigt, aber der Browser kann trotzdem noch nicht malen. Hier stecken Style-Neuberechnung, Layout und Compositing.

Zwei der drei haben nichts mit Ihrem Event-Handler zu tun, weshalb „die Animationen sind zu schwer, weg damit" meist die falsche Diagnose ist. Eine CSS-transform-Animation läuft auf dem Compositor und trägt zu keinem der drei Buckets etwas bei. Ein 250KB großes Client-Bundle, das geparst wird, während jemand auf einen Nav-Link tippt, trägt zu allen dreien bei. Bevor Sie an einem Keyframe herumschrauben, finden Sie heraus, in welchem Bucket Sie stecken.

Warum besteht Lighthouse, während INP scheitert?

Lighthouse interagiert nie mit Ihrer Seite. Es lädt sie, misst das Painting und meldet Total Blocking Time als Ersatzwert für Reaktionsfähigkeit. TBT ist ein brauchbarer Näherungswert für die ersten zwei Sekunden und ein schlechter für alles danach. Eine Route kann im Labor 98 Punkte erreichen und trotzdem im Feld bei INP scheitern, weil die scheiternde Interaktion ein Akkordeon ist, das jemand vierzig Sekunden nach dem Laden öffnet — auf einem Handy mit einem Viertel der Single-Core-Geschwindigkeit Ihres Laptops.

Die Feldzahl ist diejenige, die Google verwendet. Chrome erfasst sie bei echten Nutzern, aggregiert sie über ein gleitendes 28-Tage-Fenster und veröffentlicht das 75. Perzentil in CrUX; der Core-Web-Vitals-Bericht der Search Console liest denselben Datensatz. Die Feedback-Schleife ist deshalb langsam — Sie liefern einen Fix aus, und die Kurve bewegt sich erst Wochen später —, weshalb jedes Core-Web-Vitals-Projekt, das ich übernehme, ab Tag eins ein eigenes RUM-Beacon bekommt, und weshalb ich nie etwas allein anhand eines Lighthouse-Screenshots abnehme.

Diese Seite ist das Anschauungsobjekt für den Rest des Beitrags. Sie nutzt GSAP-ScrollTrigger-Reveals in jeder Sektion, ein Framer-Motion-Akkordeon, eine Word-Mask-Überschriftenanimation mit gestaffelter Verzögerung pro Wort und eine Marquee. In Lighthouse mobile gegen die Produktion verzeichnet sie eine Total Blocking Time von 106ms und eine CLS von 0,000, nicht weil die Motion entfernt wurde, sondern weil fast nichts davon den Main Thread berührt und fast nichts von der Seite eine Client Component ist. Largest Contentful Paint liegt bei 2,56s, über dem Budget von 2,5s, und das ist ein Payload- und Netzwerkproblem, kein Motion-Problem. Das sind Laborwerte, die nach dem oben beschriebenen Maßstab die schwächere Art von Beleg sind: Für diese Domain gibt es noch keine CrUX-Felddaten, lesen Sie sie also als Obergrenze dessen, was die Motion kostet, statt als Feldergebnis.

Drei Anzeigen, die diese Seite mit den Core-Web-Vitals-Budgets vergleichen: Total Blocking Time 106 Millisekunden gegenüber einem Budget von 200 Millisekunden, CLS 0,000 gegenüber 0,10, und Largest Contentful Paint 2,56 Sekunden gegenüber einem Budget von 2,5 Sekunden, das überschritten wird.LIGHTHOUSE MOBILE · HOME · 08.2026TBT106ms≤ 200msCLS0.000≤ 0.10LCP2.56s≤ 2.5sBUDGET
Lighthouse mobile gegen die Produktion, Startseite, 21.08.2026, während GSAP, Framer Motion und die Überschriftenanimation weiterhin laufen. Total Blocking Time steht hier stellvertretend für INP, weil Lighthouse keinen Lab-INP-Wert liefert.

Wie finden Sie heraus, welche Interaktion langsam ist?

Raten ist teuer. Die web-vitals-Bibliothek liefert einen Attribution-Build, der das Element, den Event-Typ und die Drei-Wege-Aufteilung benennt, sodass Sie aufhören, über Theorien zu streiten. Installieren Sie sie (npm i web-vitals) und binden Sie das hier einmal ein.

'use client';

import {useEffect} from 'react';

/**
 * Logs every interaction over 16ms with its attribution breakdown.
 * Mount once in the root layout, behind an env check, and drive the UI.
 */
export function InpProbe() {
  useEffect(() => {
    let cancelled = false;

    import('web-vitals/attribution').then(({onINP}) => {
      if (cancelled) return;

      onINP(
        ({value, rating, attribution}) => {
          console.table({
            inp: Math.round(value),
            rating,
            event: attribution.interactionType,
            target: attribution.interactionTarget,
            inputDelay: Math.round(attribution.inputDelay),
            processing: Math.round(attribution.processingDuration),
            presentation: Math.round(attribution.presentationDelay)
          });
        },
        {reportAllChanges: true, durationThreshold: 16}
      );
    });

    return () => {
      cancelled = true;
    };
  }, []);

  return null;
}
src/components/InpProbe.tsx — eine Client Component, die nichts an den Server meldet.

Lesen Sie die drei Spalten, nicht die Summe. Dominiert inputDelay, war der Main Thread blockiert, bevor Ihr Code lief — schauen Sie, was hydriert und welche Skripte geladen werden. Dominiert processing, ist Ihre React-Arbeit zu groß für einen Frame. Dominiert presentation, thrashen Sie das Layout oder animieren eine Eigenschaft, die eines erzwingt.

Welche App-Router-Muster ruinieren INP?

Der App Router macht es denkbar einfach, eine serverseitig gerenderte Seite auszuliefern, die gleichzeitig eine vollständig hydrierte Client-Anwendung ist, weil ein einziges 'use client' am Kopf einer gemeinsam genutzten Komponente alles darunter über die Grenze zieht. Diese eine Tatsache erklärt die meisten scheiternden Core Web Vitals in einer Next.js-Codebasis. Das sind die Ursachen, die ich am häufigsten finde, zugeordnet dazu, wie die Attribution-Ausgabe aussieht, wenn genau diese Ursache das Problem ist.

INP-Symptome, Ursachen und Lösungen in einer Next.js-App-Router-Codebasis.
Was die Attribution zeigtÜbliche UrsacheLösung
Hoher Input Delay, nur bei frühen InteraktionenDie gesamte Route hydriert noch, während der Nutzer schon tipptSeiten als Server Components belassen; Islands hydrieren, nicht Layouts
Hoher Input Delay, bei jeder InteraktionEin Tag-Manager, Chat-Widget oder eine Scroll-Bibliothek blockiert den Main ThreadThird-Party-Skripte erst bei der ersten Interaktion laden; Scroll-Listener passive machen
Hohe Processing DurationEin State-Update rendert einen großen Teilbaum neuState nach unten verschieben oder den nicht dringenden Teil mit startTransition markieren
Hohe Processing-Werte auf jeder RouteEin Client-Provider bekommt weit mehr Daten, als er brauchtNur den Ausschnitt übergeben, den Client Components tatsächlich lesen; Contexts aufteilen
Hoher Presentation DelayLayout Thrashing — in einem Effect messen und im selben Frame schreibenAlle Lesezugriffe vor den Schreibzugriffen bündeln oder ResizeObserver verwenden
Hoher Presentation Delay bei animierten Abschnittenwidth, height, top oder box-shadow werden animiertNur transform und opacity animieren
INP-Ausreißer lange nach Ende einer Animationwill-change bleibt auf Dutzenden Elementen stehen und fixiert Compositor-LayerErst unmittelbar vor der Animation setzen, bei transitionend wieder entfernen
Scheitert auf Mobilgeräten, besteht auf DesktopEin JS-Budget, das auf Laptop-Hardware festgelegt wurdeBudget pro Route festlegen und auf einem echten Gerät prüfen

Was kostet ein unachtsamer Client-Provider?

Diese Seite ist sechsfach mehrsprachig, und Übersetzungen kommen von next-intl. Das dokumentierte Muster ist, die App in NextIntlClientProvider einzupacken, damit Client Components useTranslations aufrufen können. Was das Muster nicht bewirbt: Ohne messages-Prop greift der Provider standardmäßig auf getMessages() zurück und serialisiert jeden geladenen Namespace in die RSC-Payload jeder Route.

Gemessen am gebauten Artefakt kamen die acht Namespaces auf 88.908 Byte JSON pro Sprache. Die Startseite trug den kompletten Servicekatalog, die gesamte Datenschutzerklärung und das Message-Set des Kontaktformulars mit sich herum — nichts davon rendert sie. Das Dokument wog 269KB. Jedes Byte davon musste während der Hydration heruntergeladen, geparst und von React durchlaufen werden — das ist Input Delay unter anderem Namen.

import type {ReactNode} from 'react';
import {NextIntlClientProvider} from 'next-intl';
import {getMessages, setRequestLocale} from 'next-intl/server';

export default async function LocaleLayout({
  children,
  params
}: {
  children: ReactNode;
  params: Promise<{locale: string}>;
}) {
  const {locale} = await params;
  setRequestLocale(locale);

  // Without a `messages` prop the provider calls getMessages() itself and
  // ships all eight namespaces to the browser on every route. Destructure
  // the one that Navbar, LanguageSwitcher and the banners actually read.
  const {common} = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={{common}}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}
src/app/[locale]/layout.tsx — nur der Namespace, den Client Components tatsächlich lesen, überquert die Grenze.

Die allgemeine Regel ist mehr wert als der konkrete Fix: Ein Provider ist eine Serialisierungsgrenze. Was immer Sie ihm übergeben, wird zur Payload auf jeder Route, die er umschließt — unabhängig davon, ob diese Route es nutzt. Prüfen Sie Ihre Provider so, wie Sie eine API-Antwort prüfen würden.

Welche Komponenten brauchen wirklich "use client"?

Weit weniger, als Sie haben. 'use client' ist kein Label, das „dieser Teil ist interaktiv" bedeutet — es ist eine Grenze. Alles, was darunter importiert wird, geht ebenfalls an den Browser. Die billigste INP-Arbeit, die es in den meisten App-Router-Codebasen gibt, ist es, die Direktive aus Komponenten zu entfernen, die sie nie gebraucht haben.

  1. 1

    Listen Sie jede Grenze auf

    Führen Sie grep -rn "use client" src/ aus und behandeln Sie das Ergebnis als Stückliste. Jeder Eintrag ist ein Teilbaum, dessen Hydration Sie bezahlen.

  2. 2

    Fragen Sie, was sie zur Client-Komponente macht

    State, Effects, Event-Handler, Browser-APIs oder eine Bibliothek, die diese nutzt. Wenn die Antwort lautet „sie ruft einen Übersetzungs-Hook auf", übergeben Sie die Strings stattdessen als Props vom Server.

  3. 3

    Versuchen Sie zuerst die Plattform

    Ein FAQ-Akkordeon braucht überhaupt kein JavaScript: <details> und <summary> sind per Tastatur bedienbar, animieren sich mit CSS und lassen die Komponente auf dem Server, sodass die Antworten für Crawler im HTML stehen.

  4. 4

    Schieben Sie die Grenze nach unten

    Eine Seite, die nur einen interaktiven Button braucht, sollte keine Client Component sein. Lassen Sie die Seite auf dem Server und machen Sie den Button zur Island.

  5. 5

    Messen Sie neu

    Prüfen Sie nach jeder Änderung die .next/-Chunk-Größen pro Route und lassen Sie die Probe erneut laufen. Die Bundle-Größe ist ein Frühindikator; die Probe ist die Wahrheit.

Das ist dieselbe Disziplin, die hinter jedem Next.js-Projekt steht, das ich ausliefere: Der Standard ist Server, und jede Grenzüberschreitung wird begründet. Sie kostet beim Schreiben nichts und zahlt sich auf jeder Route aus.

Welche Animationen sind kostenlos, welche werden „besteuert"?

Der Browser kann transform und opacity auf dem Compositor-Thread animieren, ganz ohne den Main Thread zu konsultieren. Diese Animationen laufen weiter flüssig, während JavaScript beschäftigt ist, und tragen zu keinem der drei INP-Buckets etwas bei. Animieren Sie irgendetwas anderes — width, height, top, margin, box-shadow, filter — erzwingt jeder Frame Style-Neuberechnung, Layout oder Paint auf dem Main Thread, was direkt im Presentation Delay landet.

Die Einschränkung lautet also nicht „keine Animation". Sie lautet: Bewegen Sie Dinge mit transform, blenden Sie sie mit opacity ein und aus, und nutzen Sie einen Klassenwechsel plus IntersectionObserver statt eines Scroll-Handlers, der bei jedem Frame läuft.

.reveal {
  opacity: 0;
  transform: translate3d(0, 24px, 0);
  transition:
    opacity 560ms cubic-bezier(0.16, 1, 0.3, 1),
    transform 560ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* Added by the observer one frame before the class below, removed on
   transitionend. A layer that is never released is a layer that costs
   memory for the rest of the session. */
.reveal[data-animating='true'] {
  will-change: opacity, transform;
}

.reveal[data-visible='true'] {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
Ein Scroll-Reveal, das nie das Layout berührt, und das zuerst die Motion-Präferenz respektiert.

Zwei weitere Regeln aus derselben Familie. Jeder scroll-, touchstart- oder wheel-Listener muss mit {passive: true} registriert werden, damit der Browser nie abwarten muss, ob Sie preventDefault() aufrufen. Und messen Sie nie in einem Effect und schreiben im selben Durchlauf: getBoundingClientRect() nach einem Style-Schreibvorgang aufzurufen erzwingt ein synchrones Layout, und das in einer Schleife über eine Liste zu tun ist der zuverlässigste Weg, aus einer 40ms-Interaktion eine 400ms-Interaktion zu machen.

prefers-reduced-motion gehört in denselben Abschnitt, nicht in einen Anhang zur Barrierefreiheit. Es zu respektieren ist von WCAG vorgeschrieben, und es ist zugleich ein kostenloser Performance-Modus genau für die Nutzer, die am ehesten eingeschränkte Hardware haben — Sie überspringen die Arbeit komplett, statt sie nur schneller zu erledigen.

Wie verhindern Sie, dass ein Long Task das Painting blockiert?

Manchmal ist die Arbeit wirklich notwendig — eine lange Liste filtern, einen Index aufbauen, ein Diagramm initialisieren. Das Problem ist nicht, dass es 300ms dauert, sondern dass es 300ms in einem einzigen, ununterbrochenen Task dauert, und der Browser kann mitten in einem Task weder malen noch ein Event zustellen. Zerlegen Sie die Arbeit und geben Sie die Kontrolle ab.

type SchedulerLike = {yield?: () => Promise<void>};

/** scheduler.yield() where supported, setTimeout(0) everywhere else. */
function yieldToMain(): Promise<void> {
  const scheduler = (globalThis as {scheduler?: SchedulerLike}).scheduler;
  if (scheduler?.yield) return scheduler.yield();
  return new Promise((resolve) => setTimeout(resolve, 0));
}

export async function runInChunks<T>(items: T[], work: (item: T) => void) {
  let started = performance.now();

  for (const item of items) {
    work(item);

    if (performance.now() - started > 50) {
      await yieldToMain();
      started = performance.now();
    }
  }
}
Alle 50ms abgeben, damit ein Tap mitten in der Berechnung bestätigt werden kann.

scheduler.yield() ist besser als setTimeout(0), weil es an den Anfang der Warteschlange zurückkehrt statt ans Ende, sodass Ihre verbleibende Arbeit nicht von jedem anderen anstehenden Task überholt wird. Wo die Arbeit überhaupt nicht dringend ist — Analytics, Prefetching, das Aufwärmen eines Caches — ist requestIdleCallback das richtige Werkzeug, und in React lässt das Einpacken eines schweren State-Updates in startTransition das dringende Painting zuerst passieren.

Das Muster, das Sie verinnerlichen sollten: Bestätigen Sie die Interaktion im ersten Frame, dann erledigen Sie die Arbeit. Setzen Sie den Pressed-State, zeigen Sie den Spinner, schließen Sie das Menü — malen Sie das — und führen Sie erst danach den teuren Teil aus. INP endet beim ersten Paint, das die Interaktion widerspiegelt, also ist eine schnelle Bestätigung ein echter Fix, kein Trick.

Wie beweisen Sie, dass der Fix tatsächlich gewirkt hat?

Drei Quellen, in aufsteigender Reihenfolge ihrer Autorität. Ihre lokale Probe sagt Ihnen innerhalb von Sekunden, ob die Interaktion, der Sie nachgejagt sind, schneller geworden ist. Ihr RUM-Beacon — derselbe onINP-Aufruf, an einen Endpunkt mit navigator.sendBeacon gesendet statt geloggt — sagt Ihnen innerhalb eines Tages, ob es für echte Nutzer auf echten Geräten schneller geworden ist. CrUX und Search Console sagen Ihnen innerhalb von vier bis sechs Wochen, ob Google zustimmt, weil das 28-Tage-Fenster erst durchlaufen muss, bevor die alten Daten herausfallen.

Segmentieren Sie die RUM-Daten nach Geräteklasse und Route, bevor Sie irgendwelche Schlüsse ziehen. Ein INP, der im Median gut aussieht und am 75. Perzentil schlecht, ist fast immer auf eine Geräteklasse und eine Route zurückzuführen, und Mittelwerte verdecken das. Diese Segmentierung ist auch der Grund, warum sich eine Analytics-Ebene lohnt: Ein ungeprüftes Messsetup macht jede Vorher-Nachher-Behauptung unfalsifizierbar.

Es gibt einen kommerziellen Grund, sich damit zu befassen, der über das Search-Console-Abzeichen hinausgeht. Reaktionsfähigkeit fließt in Googles Landing-Page-Experience-Bewertung ein, und eine überdurchschnittliche Bewertung korreliert mit einem Cost-per-Click, der rund 36% unter dem Durchschnitt liegt (Search Engine Land, 2023). Bei Paid-Media-Budgets rechnet Ihnen ein langsames Interface jeden Monat leise die Kosten hoch.

Wann muss die Animation wirklich weichen?

Manchmal muss sie, und etwas anderes zu behaupten wäre unehrlich. Streichen Sie sie, wenn eine Bibliothek eine große Runtime für einen einzigen Effekt mitbringt — diese Seite schleppt GSAP rein für ein Opacity-und-Translate-Reveal mit, das dreißig Zeilen IntersectionObserver plus das obige CSS kostenlos erledigen würden, und genau deshalb steht dieser Tausch auf der Liste. Streichen Sie sie, wenn die Animation von einem Scroll-Handler getrieben wird, der bei jedem Frame Positionen neu berechnet. Streichen Sie sie, wenn sie eine Eigenschaft animiert, die Layout erzwingt, und sich das Design nicht mit transform neu ausdrücken lässt. Streichen Sie sie, wenn sie während der Hydration läuft und mit der Arbeit konkurriert, die die Seite überhaupt erst nutzbar macht.

Was Sie nicht tun sollten, ist Motion als erste Reaktion auf einen roten INP-Wert zu entfernen, denn in den meisten App-Router-Codebasen ist die Animation nicht die Ursache, und sie zu entfernen ändert nichts außer dem Design. Die Reihenfolge ist: mit Attribution messen, die Client-Grenze verkleinern, die animierte Eigenschaft korrigieren, die Long Tasks abgeben — und erst dann, wirklich erst dann, über die Motion selbst diskutieren.

Der Großteil meiner Arbeit ist mit Unternehmen in den VAE, wo E-Commerce voraussichtlich von 12,30 Mrd. USD im Jahr 2026 auf 21,01 Mrd. USD bis 2031 wächst (Mordor Intelligence, 2026) — ein Markt, der über Mobilfunknetze auf Android-Mittelklassegeräten einkauft, also genau die Hardware, die INP misst. Wenn Ihr Search-Console-Bericht rot geworden ist und Ihnen jemand gesagt hat, die Animationen müssten weg, schicken Sie mir die URL und ich führe den Attribution-Durchlauf durch. Jedes Projekt beginnt mit einem ersten Projektgespräch, und wie fertige Arbeit aussieht, sehen Sie in den Case Studies oder auf der Dubai-Seite.

Was sollten Sie über meine Arbeitsweise dabei wissen?

Nein, und wer das verspricht, verkauft Ihnen etwas. Core Web Vitals sind ein kleiner, bestätigter Teil von Googles Page-Experience-Signalen — sie helfen, zwischen Seiten vergleichbarer Relevanz zu entscheiden, aber sie lassen keine irrelevante Seite ranken. Was ein behobener INP zuverlässig bewirkt, ist, einen bekannten Negativfaktor zu entfernen, die Erfahrung für Menschen zu verbessern, die bereits auf der Seite sind, und zu verhindern, dass die Search Console URLs als verbesserungsbedürftig markiert. Behandeln Sie den Ranking-Effekt als Nebeneffekt und den Usability-Effekt als den eigentlichen Ertrag.

In Ihren eigenen RUM-Daten innerhalb eines Tages nach dem Deployment. In CrUX und Search Console vier bis sechs Wochen. Google meldet das 75. Perzentil über ein gleitendes 28-Tage-Fenster, sodass die alten langsamen Sessions erst aus der Stichprobe herausfallen müssen, bevor die Kennzahl grün wird — selbst wenn jeder Besucher seit dem Deployment eine schnelle Erfahrung hatte. Machen Sie die Änderung nicht rückgängig, nur weil sich die Kurve in Woche zwei nicht bewegt hat. Beobachten Sie stattdessen Ihre eigenen Felddaten und lassen Sie die öffentliche Zahl nachziehen.

Indem ich das Schnelle zum Standard mache statt zu einer Aufräumaufgabe. In der Praxis: Server Components, es sei denn, eine Komponente braucht wirklich State oder eine Browser-API; Provider, die nur die Daten bekommen, die sie nutzen; `transform` und `opacity` für alles, was sich bewegt; passive Listener; Third-Party-Skripte, die bis zur Interaktion aufgeschoben werden; und ein Pro-Route-JavaScript-Budget, das beim Build geprüft wird. Performance, die als Phase am Ende behandelt wird, verliert immer gegen Deadlines. Als Randbedingung behandelt, die die Architektur bereits erfüllt, kostet sie nichts, sie aufrechtzuerhalten.

Fast immer lässt es sich vor Ort beheben. INP-Probleme sind konzentriert: eine überdimensionierte Client-Grenze, ein blockierendes Third-Party-Skript, ein Handler, der das Layout thrasht. Sie zu finden erfordert einen Attribution-Durchlauf, und sie zu beheben sind meist eine Handvoll Dateien. Ein Neubau ist nur dann die richtige Antwort, wenn das Framework selbst clientseitiges Rendering von allem erzwingt oder wenn in der Codebasis keine Grenzdisziplin mehr zu retten ist. Ich sage Ihnen lieber, dass es ein Zwei-Tage-Fix ist, als Ihnen einen Neubau zu verkaufen, den Sie nicht brauchen.

Jede von ihnen, wenn sie korrekt eingesetzt wird — und keine, wenn nicht. Entscheidend ist, welche Eigenschaften animiert werden und wie viel Runtime mitgeliefert wird. Eine Bibliothek, die `transform` und `opacity` auf dem Compositor animiert, kostet zur Interaktionszeit nichts; dieselbe Bibliothek, die `height` auf zwanzig Elementen animiert, kostet Sie bei jedem Frame Presentation Delay. Die zweite Frage ist das Bundle-Gewicht: eine komplette Animations-Runtime für ein einziges Fade ist ein schlechter Tausch, und CSS plus `IntersectionObserver` erledigen das kostenlos.

Er entfernt eine konkrete, messbare Form von Friktion: Taps, die scheinbar nichts tun, sodass Nutzer erneut tippen, Formulare doppelt absenden oder den Flow abbrechen. Dieser Effekt ist real, und er ist am größten bei den Interaktionen, die dem Geld am nächsten sind — Filter, In-den-Warenkorb, Formularabsendung. Ich werde für Ihre Seite keine Prozentzahl nennen, denn jede ehrliche Zahl muss aus Ihrem eigenen Vorher-Nachher mit geprüftem Tracking kommen. Was ich tun werde, ist den Test so zu instrumentieren, dass das Ergebnis auch etwas bedeutet.

Ja — ein Vorher-Nachher in Felddaten, kein Lighthouse-Screenshot. Das bedeutet: die Attribution-Aufschlüsselung für die scheiternden Interaktionen, die RUM-Verteilung nach Geräteklasse und Route, und die Bewegung bei CrUX oder Search Console, sobald das 28-Tage-Fenster durchgelaufen ist. Der Bericht listet außerdem auf, was geändert wurde und warum, damit Ihre Entwickler das nach der Übergabe pflegen können. Eine Zahl ohne Erklärung ist kein Bericht, sondern Marketing.

Dreißig Minuten, für beide Seiten kostenfrei, in denen ich mir gemeinsam mit Ihnen Ihre Live-Seite ansehe: die Felddaten in CrUX, die scheiternden Interaktionen, die Client-Grenzen in Ihrem Bundle und die zwei oder drei Änderungen, die die Zahl am wahrscheinlichsten bewegen. Sie bekommen eine ehrliche Einschätzung, ob das Problem klein oder strukturell ist, und einen groben Umriss der Arbeit. Keine Verpflichtung, keine Präsentation. Wenn die Antwort lautet, dass Ihre Seite in Ordnung ist und die Agentur falsch lag, sage ich Ihnen genau das.

// OFFEN FÜR NEUE ROLLEN

Sie stellen ein oder bauen etwas, das einen Entwickler braucht?