Framer Motion Performance: Layout-Animationen, reduced-motion & Lazy-Mount
Framer Motion performant einsetzen: Layout-Animationen vs. CSS-transform, prefers-reduced-motion-Handling, Lazy-Mount für schwere Animations-Trees und Profiling-Tipps.
Rechtliches & Info
Konkrete Optimierungsmuster für INP, LCP und CLS: Event-Handler-Tuning, responsive Bilder mit priority, Skeleton-Layouts gegen Layout-Shifts und Lighthouse-Profiling.
Seit März 2024 ist Interaction to Next Paint (INP) offiziell ein Core Web Vital — First Input Delay (FID) wurde ersetzt. Das ändert die Prioritätensetzung bei Performance-Audits erheblich. Dieser Artikel zeigt konkrete Optimierungsmuster für alle drei aktuellen Core Web Vitals: INP, LCP und CLS.
INP misst die Reaktionszeit auf Nutzerinteraktionen über den gesamten Lebenszyklus einer Seite — nicht nur den ersten Klick. Der Schwellenwert: unter 200ms gilt als “gut”, über 500ms als “schlecht”.
1. Langer Haupt-Thread durch synchronen JavaScript-Code
// Problematisch: synchrone Berechnung blockiert den Haupt-Thread
button.addEventListener('click', () => {
const result = expensiveCalculation(largeDataset); // blockiert für 300ms
updateUI(result);
});
// Besser: Arbeit in Tasks aufteilen
button.addEventListener('click', async () => {
// Lass den Browser zunächst die visuelle Reaktion rendern
const result = await scheduler.postTask(() => expensiveCalculation(largeDataset), {
priority: 'background',
});
updateUI(result);
});
2. Unnötige Reflows durch DOM-Manipulationen
// Falsch: abwechselnd lesen und schreiben erzwingt mehrere Reflows
elements.forEach((el) => {
const height = el.offsetHeight; // Lesen (Browser muss Layout berechnen)
el.style.height = height * 2 + 'px'; // Schreiben
const width = el.offsetWidth; // Lesen (erzwingt erneutes Layout)
el.style.width = width + 'px'; // Schreiben
});
// Richtig: alle Lesezugriffe erst, dann alle Schreibzugriffe
const measurements = elements.map((el) => ({
height: el.offsetHeight,
width: el.offsetWidth,
}));
elements.forEach((el, i) => {
el.style.height = measurements[i].height * 2 + 'px';
el.style.width = measurements[i].width + 'px';
});
3. Event-Handler ohne Debouncing auf hochfrequenten Events
// Scroll-Handler ohne Throttling — feuert hunderte Male pro Sekunde
window.addEventListener('scroll', updateStickyHeader);
// Mit requestAnimationFrame throttlen
let rafPending = false;
window.addEventListener('scroll', () => {
if (rafPending) return;
rafPending = true;
requestAnimationFrame(() => {
updateStickyHeader();
rafPending = false;
});
});
import { startTransition, useState } from 'react';
export function SearchFilter({ onFilter }) {
const [query, setQuery] = useState('');
function handleInput(e) {
const val = e.target.value;
// Input-Update ist dringend (hohe Priorität)
setQuery(val);
// Filter-Berechnung ist nicht dringend (niedrige Priorität)
startTransition(() => {
onFilter(val);
});
}
return <input value={query} onChange={handleInput} />;
}
startTransition markiert State-Updates als nicht dringend. React kann sie unterbrechen und zurückstellen, wenn dringendere Arbeit (wie Tastatureingaben) wartet.
LCP misst, wann das größte sichtbare Inhaltselement geladen ist. Unter 2,5 Sekunden ist “gut”. Das LCP-Element ist meistens ein Bild — entweder das Hero-Bild oder ein großes Produkt-Bild above the fold.
---
import { Image } from 'astro:assets';
import heroBild from '../assets/images/hero-wender-media.webp';
---
<!-- fetchpriority="high" + loading="eager" für das LCP-Bild -->
<Image
src={heroBild}
alt="Webdesign und SEO von Wender Media — Halle (Saale)"
width={1200}
height={600}
fetchpriority="high"
loading="eager"
decoding="async"
quality={85}
/>
Das fetchpriority="high"-Attribut weist den Browser an, dieses Bild frühzeitig in der Fetch-Queue zu priorisieren — noch bevor das Preload-Scanner es entdeckt.
---
// Im <head> des Layouts
---
<link
rel="preload"
href="/fonts/inter-var.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<!-- Preload für das LCP-Bild, falls es als CSS-Background kommt -->
<link
rel="preload"
fetchpriority="high"
as="image"
href="/images/hero-bg.webp"
type="image/webp"
/>
<!-- Astro's Image-Komponente generiert srcset automatisch -->
<Image
src={heroImage}
alt="..."
widths={[400, 800, 1200, 1600]}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
formats={['avif', 'webp']}
/>
AVIF bietet ca. 50% kleinere Dateigrößen gegenüber WebP bei gleicher Qualität — mit automatischem Fallback auf WebP für ältere Browser.
CLS misst unerwartete Verschiebungen im Layout während des Ladens. Unter 0,1 ist “gut”. Layout Shifts zerstören die Nutzererfahrung — besonders mobil, wenn Nutzer gerade auf einen Link klicken wollen, der sich verschiebt.
1. Bilder ohne explizite Dimensionen
<!-- FEHLER: kein width/height → Browser reserviert keinen Platz -->
<img src="/images/team.jpg" alt="..." />
<!-- RICHTIG: explizite Dimensionen oder aspect-ratio -->
<img
src="/images/team.jpg"
alt="..."
width="800"
height="450"
/>
<!-- Oder via CSS aspect-ratio -->
<img
src="/images/team.jpg"
alt="..."
class="w-full aspect-video object-cover"
/>
2. Dynamisch eingefügte Elemente above the fold
Cookie-Banner, Newsletter-Popups oder Ads, die nach dem initialen Render eingefügt werden, verschieben existierende Inhalte. Lösung: Platz reservieren.
/* Reservierter Platz für den Cookie-Banner */
.cookie-banner-placeholder {
min-height: 80px; /* Approximate height des Banners */
}
/* Oder: Banner positioniert absolut über den Content */
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
/* Kein Layout Shift, weil fixed */
}
3. Skeleton-Layouts für dynamische Inhalte
---
// Skeleton-Komponente für Lade-Zustände
---
<div class="grid grid-cols-1 md:grid-cols-3 gap-6" aria-busy="true" aria-label="Artikel werden geladen">
{Array.from({ length: 3 }).map((_, i) => (
<div class="bg-gray-100 dark:bg-gray-800 rounded-xl p-6 animate-pulse" aria-hidden="true">
<div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/3 mb-4"></div>
<div class="h-6 bg-gray-200 dark:bg-gray-700 rounded w-full mb-2"></div>
<div class="h-6 bg-gray-200 dark:bg-gray-700 rounded w-4/5 mb-4"></div>
<div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2"></div>
</div>
))}
</div>
Das animate-pulse von Tailwind erzeugt eine Puls-Animation, die signalisiert, dass Inhalte geladen werden — ohne tatsächlichen Layout Shift.
4. Web Fonts und FOUT
Font-Swap erzeugt einen Layout Shift, wenn der Fallback-Font eine andere Metrik hat als der Web Font:
/* font-face mit font-display: optional — vermeidet FOUT komplett */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2');
font-display: optional;
font-weight: 100 900;
}
font-display: optional lädt den Font nur, wenn er innerhalb eines sehr kurzen Zeitfensters verfügbar ist — vermeidet FOUT vollständig.
Lighthouse im Chrome DevTools liefert einen CWV-Score, aber keine Root-Cause-Analyse. Dafür braucht man das Performance-Panel.
// PerformanceObserver für INP in der Produktion
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'event' && entry.duration > 100) {
console.warn('Langsame Interaktion:', {
type: entry.name,
duration: entry.duration,
target: entry.target,
});
}
}
});
observer.observe({ type: 'event', buffered: true, durationThreshold: 100 });
Lighthouse ist ein Lab-Tool — es misst unter kontrollierten Bedingungen. Die echten CWV-Werte kommen aus dem Chrome User Experience Report (CrUX) und werden in der Google Search Console unter “Core Web Vitals” angezeigt.
Feld-Daten (CrUX) und Lab-Daten (Lighthouse) können stark voneinander abweichen — besonders INP, weil es reale Nutzerinteraktionen misst, die Lighthouse nicht simulieren kann.
Performance-Audit für Ihre Website? Wender Media identifiziert Bottlenecks und behebt sie — messbar und nachhaltig. Schreiben Sie uns: info@wendermedia.info.
Themen:
Wender Media unterstützt Sie bei der praktischen Umsetzung — von der technischen Konzeption bis zum Launch. Schreiben Sie uns, wir antworten innerhalb von 24 Stunden.
Jetzt Beratung anfragenKostenlos & unverbindlich — info@wendermedia.info
Framer Motion performant einsetzen: Layout-Animationen vs. CSS-transform, prefers-reduced-motion-Handling, Lazy-Mount für schwere Animations-Trees und Profiling-Tipps.
CLS systematisch reduzieren: Bilder mit width/height, font-display, Reserved Space für Embeds und Ad-Slots.
So funktioniert LCP-Optimierung — Hero-Bilder, Server, Schriften und Render-Blocking-Resources systematisch verbessern. Mit fetchpriority, Preload und CDN.