dataLayer-Events einplanen, um den INP zu optimieren

Wie verzögerte GTM-Events nach dem Browser-Paint deine INP-Werte verbessern

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-09-28

TL;DR: INP durch verzögertes dataLayer.push() verbessern

Feuert dataLayer.push() synchron in einem Event-Handler, verarbeitet der GTM Tags, bevor der Browser visuelles Feedback zeichnen kann. Das blockiert den Main-Thread und treibt deinen Interaction to Next Paint (INP) nach oben. Die Lösung: Lass dataLayer.push() per requestAnimationFrame erst laufen, nachdem der Browser den nächsten Frame gezeichnet hat. Dieses Pattern senkt den INP auf den meisten Websites um 20 bis 100 ms – und macht aus einem durchgefallenen Wert oft ein „Bestanden“. Die Datenerfassung verzögert sich um 50 bis 250 ms. Für Analytics und Marketing-Tracking spielt das keine Rolle.

Wie dataLayer.push() deinem INP schadet

Bei einem unserer Kunden haben wir den Interaction to Next Paint (INP) um 100 ms gesenkt – allein dadurch, dass wir den Ausführungszeitpunkt von dataLayer.push() nach einer Nutzerinteraktion verschoben haben. Der Fix ist ein JavaScript-Drop-in-Replacement, das Rendering vor Tracking priorisiert.

Zuletzt geprüft von Arjen Karel im März 2026

Das Problem mit synchronem dataLayer.push()

Wenn du mit dem Google Tag Manager arbeitest, kennst du dataLayer.push(). Es ist die Standardmethode, um Events an den Data Layer zu senden, und tief in die meisten Websites integriert. Das Problem: Niemand hinterfragt, wann der Aufruf ausgeführt wird.

Wird dataLayer.push() direkt in einem Event-Handler aufgerufen (Button-Klick, Formular absenden, Menü öffnen), läuft der Aufruf synchron. GTM-Tags, die auf dieses Event reagieren, feuern ebenfalls sofort. Das blockiert den Main-Thread, bevor der Browser das Layout aktualisieren kann. Der Besucher klickt auf einen Button und sieht keine Reaktion, während der GTM im Hintergrund Tracking-Skripte abarbeitet. Genau diese Verzögerung ist dein Presentation Delay – und die Hauptursache für schlechte INP-Werte.

Der Performance-Trace unten stammt von einer großen Nachrichtenseite und zeigt die GTM-Aktivität nach einer Nutzerinteraktion. Die GTM-Tasks brauchten rund 90 ms zur Ausführung und trieben den gesamten INP auf 263 ms. Damit fällt diese Interaktion bei den Core Web Vitals durch.

Der Fix: Erst zeichnen, dann pushen

Die Lösung folgt den Empfehlungen von web.dev zur INP-Optimierung: Yield an den Main-Thread, damit der Browser zeichnen kann, bevor unkritischer Code läuft. Statt Tracking synchron im Event-Handler auszuführen:

  1. Führe den Code für das visuelle Update sofort aus.
  2. Lass den Browser zeichnen.
  3. Pushe erst dann in den dataLayer.

So sieht dieselbe Interaktion mit diesem Pattern aus. Die einzige Änderung: dataLayer.push() läuft jetzt erst, nachdem der Browser den nächsten Frame gezeichnet hat.

Die zuvor durchgefallene Interaktion besteht jetzt problemlos. Alle Daten erreichen nach wie vor den dataLayer. Der Unterschied: GTM-Skripte laufen erst, nachdem der Browser auf die Nutzeraktion reagiert hat – nicht davor.

Der awaitPaint-Helper

Dieser Helper nutzt requestAnimationFrame, um einen Callback einzuplanen, nachdem der Browser gezeichnet hat. Das verschachtelte setTimeout stellt sicher, dass die Funktion erst nach Abschluss des Paints läuft, nicht schon davor. Genau dieses Pattern empfiehlt web.dev, um unkritische Aufgaben nach einer Interaktion zu verzögern.

async function awaitPaint(fn) {
    await new Promise((resolve) => {
        // Fallback: Stellt sicher, dass nichts hängen bleibt, falls RAF nie feuert (z. B. Hintergrund-Tabs)
        setTimeout(resolve, 200);

        // Nach dem nächsten Paint ausführen
        requestAnimationFrame(() => {
            setTimeout(resolve, 50);
        });
    });

    if (typeof fn === 'function') {
        fn();
    }
}

Den Helper verwenden

Packe jeden dataLayer.push()-Aufruf in awaitPaint, um ihn bis nach dem Paint zu verzögern:

function pushToDataLayer(event, data) {
    window.dataLayer = window.dataLayer || [];

    awaitPaint(() => {
        window.dataLayer.push({
            event,
            ...data,
            timestamp: new Date().toISOString()
        });
    });
}

// Verwendung
document.querySelector('.buy-button').addEventListener('click', () => {
    // Visuelles Feedback erfolgt sofort
    showLoadingSpinner();

    // Tracking feuert nach dem Paint
    pushToDataLayer('purchase_click', { productId: '123' });
});

Globaler Override für schnelle Tests

Um dieses Pattern auf deiner gesamten Website zu testen, musst du nicht jeden dataLayer.push()-Aufruf umbauen. Überschreibe die Push-Funktion einfach global. Platziere dieses Skript im <head> direkt hinter dem GTM-Container-Snippet.

<script type="module">
    window.dataLayer = window.dataLayer || [];

    async function awaitPaint(fn) {
        await new Promise((resolve) => {
            // Fallback: Stellt sicher, dass nichts hängen bleibt, falls RAF nie feuert (z. B. Hintergrund-Tabs)
            setTimeout(resolve, 200);

            // Nach dem nächsten Paint ausführen
            requestAnimationFrame(() => {
                setTimeout(resolve, 50);
            });
        });

        if (typeof fn === 'function') {
            fn();
        }
    }

    if (window.dataLayer && typeof window.dataLayer.push === 'function') {
        const originalPush = window.dataLayer.push.bind(window.dataLayer);

        window.dataLayer.push = function (...args) {
            awaitPaint(() => {
                originalPush(...args);
            });
        };
    }
</script>

Das überschreibt jeden dataLayer.push() auf der Seite. Alle bestehenden GTM-Event-Trigger, hardcodierten Tracking-Aufrufe und Plugin-Integrationen profitieren automatisch – ganz ohne Codeänderungen.

Warum das den INP verbessert

Der INP misst die Zeitspanne von der Nutzerinteraktion bis zu dem Moment, in dem der Browser die visuelle Reaktion zeichnet. Führst du die GTM-Event-Verarbeitung synchron im Event-Handler aus, blockierst du den Main-Thread und verhinderst das Rendern. Laut dem Web Almanac 2024 ist der Presentation Delay im Median der größte Treiber für schlechte INP-Werte – und Tracking-Skripte gehören zu den Hauptursachen.

Die Zahlen bestätigen das. Der Web Almanac 2025 zeigt: Insgesamt bestehen 77 % der mobilen Seiten den INP, aber nur 63 % der 1.000 meistbesuchten Websites. Diese Top-Websites laden deutlich mehr Third-Party-Skripte. Bei Subito (Italiens größtem Kleinanzeigenportal) sank der INP durch das Deaktivieren eines einzigen TikTok-Tracking-Skripts im GTM von 208 ms auf rund 170 ms. Ein einziges Skript, 38 ms gespart.

Indem du dataLayer.push() auf die Zeit nach dem Paint verschiebst, nimmst du die gesamte GTM-Verarbeitung aus dem kritischen Pfad der Interaktion. Der Nutzer erhält sofort visuelles Feedback. Der GTM feuert trotzdem – nur eben 50 bis 250 ms später.

Warum keine feste Verzögerung, kein Idle-Callback oder scheduler.yield()?

  • Feste Verzögerung per setTimeout(delay): Eine hardcodierte Verzögerung (z. B. setTimeout(..., 100)) ist reines Raten, wann das Rendering fertig ist. Ist sie zu lang, verzögerst du das Tracking unnötig. Ist sie zu kurz, blockierst du den Paint trotzdem.
  • requestIdleCallback: Plant Aufgaben ein, wenn der Browser im Leerlauf ist – garantiert aber keine zeitnahe Ausführung nach einer bestimmten Interaktion. Der Callback läuft womöglich viel später oder gar nicht mehr, bevor der Nutzer die Seite verlässt.
  • scheduler.yield(): Die moderne API für das Yielding an den Main-Thread. Sie behält die Task-Priorität bei (anders als setTimeout, das deine Fortsetzung ganz ans Ende der Queue schickt). Allerdings garantiert scheduler.yield() nicht, dass vor der Ausführung deines Codes wirklich ein Paint stattgefunden hat. Für diesen konkreten Anwendungsfall ist requestAnimationFrame das bessere Tool, weil es direkt an den Rendering-Lifecycle gekoppelt ist. Zudem unterstützt Safari scheduler.yield() bislang noch nicht.

Die Kombination aus requestAnimationFrame + setTimeout ist maßgeschneidert für dieses Szenario. requestAnimationFrame feuert unmittelbar vor dem Paint. Das verschachtelte setTimeout erzeugt einen neuen Task, der erst nach Abschluss dieses Paints ausgeführt wird. Zusammen stellen sie sicher, dass dein verzögerter Code nach dem visuellen Update läuft – genau das, was der INP misst.

Trade-offs

  • Minimale Verzögerung bei der Datenerfassung: Events erreichen den GTM 50 bis 250 ms später als bei einem synchronen Push. Für Analytics und Marketing-Tracking ist das völlig unerheblich.
  • Datenverlust bei schnellem Verlassen der Seite: Löst ein Besucher ein Event aus und verlässt die Seite innerhalb dieses Zeitfensters, feuert das Event unter Umständen nicht mehr. Nutze für kritische Events vor einem Redirect oder Page-Unload navigator.sendBeacon() statt dataLayer.push(). Mehr zur Beacon-API erfährst du in Warum du Analytics-Skripte begrenzen solltest.

Für das meiste Tracking überwiegt der INP-Gewinn die minimale Verzögerung bei Weitem. Brauchst du beim Event-Logging Millisekunden-Präzision (Real-Time Bidding, Finanz-Dashboards), passt dieser Ansatz nicht. Für alles andere ist er ein klarer Gewinn.

Überprüfe die Verbesserung nach dem Override mit Real User Monitoring. Lab-Tests zeigen den Unterschied – aber für deine Core-Web-Vitals-Scores zählt allein Field Data von echten Nutzern in echten Netzwerken.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

Arjen Karel

Brauchst du eine schnellere Website?

Ich bin Arjen und mache seit 17 Jahren langsame Websites schnell. Wenn du auf Probleme stößt, die du nicht selbst lösen kannst, schick mir deine URL und wir reden darüber.

Schick mir deine URL
Arjen Karel
Arjen Karel, Web-Performance-Consultant seit 2009

Warum funktioniert unser Fix nicht?

Manchmal liefert das CDN noch das alte HTML aus, oder der Fix steckt im Desktop-Template und Mobilgeräte nutzen ein anderes. Schick mir deine URL und das Problem, dann antworte ich mit ein paar Terminvorschlägen. Ich prüfe zuerst deine Field Data, dann arbeiten wir eine Stunde an deiner Website, und innerhalb eines Tages hast du meine Notizen schriftlich. 350 € zzgl. MwSt.

Schick mir deine URL

Kein Entwickler für den Fix? Ich mache die Arbeit selbst, auf einer Kopie deiner Website.

dataLayer-Events einplanen, um den INP zu optimieren Core Web Vitals dataLayer-Events einplanen, um den INP zu optimieren