React Animations: Framer Motion vs. CSS Transitions
AI generated
</>
{ }
React · Animationen · Framer Motion · CSS Transitions · Performance
React Animations:
Framer Motion vs. CSS Transitions

Die Entscheidung zwischen Framer Motion und CSS Transitions in React hat mehr mit dem Animations-Typ als mit persönlicher Präferenz zu tun. Hover-Effekte und einfache State-Transitions gehören in CSS. Orchestrierte Sequences, Exit-Animations und Shared-Element-Transitions sind ohne Framer Motion kaum realisierbar. Dieses Tutorial erklärt wann welche Lösung die richtige ist.

17 Min. Lesezeit Framer Motion · CSS Transitions · CSS Animations · AnimatePresence · View Transitions API React 19 · TypeScript · Performance

1. Warum React Animations so oft falsch angegangen werden

React Animations werden in der Praxis häufig mit dem falschen Werkzeug umgesetzt. Teams greifen zu Framer Motion für einfache Hover-Effekte, die mit einer CSS-Zeile lösbar wären – und erzeugen damit JavaScript-Overhead für eine Aufgabe, die der Browser nativ und performanter erledigt. Oder umgekehrt: Sie versuchen komplexe Einblend-Animationen, die beim Entfernen eines Elements aus dem DOM stattfinden sollen, mit reinen CSS Transitions zu lösen – was strukturell nicht funktioniert, weil das Element beim Unmount sofort aus dem DOM entfernt wird, bevor die Transition abschließen kann.

Das Kernproblem bei React Animations: React's Komponentenmodell ist nicht von Haus aus auf Animationen ausgerichtet. Ein unmountetes Element verschwindet sofort, es gibt keinen nativen Hook für "vor dem Entfernen animieren". CSS Transitions funktionieren nur für Zustandsänderungen, nicht für Mount/Unmount. Framer Motion löst genau diese Lücke mit AnimatePresence. Die richtige Entscheidung zwischen CSS und Framer Motion hängt also primär davon ab, welchen Animations-Typ man braucht – nicht davon, was gerade in Mode ist oder was andere Projekte verwenden.

2. CSS Transitions und Animations in React – die Grundlage

CSS Transitions sind der effizienteste Weg für zustandsabhängige React Animations. Sie laufen auf dem Compositor-Thread des Browsers, ohne JavaScript-Involvement – also kein React-Render, kein JS-Berechnungsaufwand. Für Hover-Effekte, State-basierte Farbwechsel, Größenänderungen und einfache Einblendungen durch Klassen-Wechsel sind CSS Transitions die erste Wahl. Mit Tailwind CSS sind viele dieser React Animations durch Utility-Klassen wie transition-all duration-300 ease-in-out und State-abhängige Klassen (className={open ? 'opacity-100' : 'opacity-0'}) direkt im JSX ausdrückbar.

CSS @keyframes-Animationen eignen sich für Animationssequenzen, die unabhängig vom Zustand ablaufen – Lade-Indikatoren, Pulse-Effekte, kontinuierliche Rotationen. Sie laufen ebenfalls auf dem Compositor-Thread, solange nur transform und opacity animiert werden. Layout-Eigenschaften wie width, height, margin und padding zu animieren triggert hingegen Reflow und Layout-Berechnungen – das ist sowohl für CSS Transitions als auch für Framer Motion zu vermeiden. Die goldene Regel für performante React Animations: nur transform (translate, scale, rotate) und opacity animieren.


// CSS Transitions in React — no JS animation library needed for simple cases
import { useState } from 'react';

function NotificationBadge({ count }: { count: number }) {
  const visible = count > 0;

  return (
    // CSS transition handles the opacity/scale change — runs on compositor thread
    <span
      className={[
        'inline-flex items-center justify-center rounded-full text-xs font-bold',
        'bg-red-500 text-white px-1.5 py-0.5',
        'transition-all duration-200 ease-out',
        // State-driven classes — React just toggles them, browser handles animation
        visible ? 'opacity-100 scale-100' : 'opacity-0 scale-75',
      ].join(' ')}
    >
      {count}
    </span>
  );
}

// Keyframe animation for a loading spinner — pure CSS, zero JS overhead
function LoadingSpinner() {
  return (
    <div
      className="w-6 h-6 rounded-full border-2 border-slate-200 border-t-sky-600"
      style={ { animation: 'spin 0.7s linear infinite' } }
    />
    // @keyframes spin defined in global CSS:
    // @keyframes spin { to { transform: rotate(360deg); } }
  );
}

// Height transition: max-height trick for collapse — avoid animating height directly
function Accordion({ title, children }: { title: string; children: React.ReactNode }) {
  const [open, setOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setOpen(o => !o)}>{title}</button>
      {/* Animate max-height instead of height — enables CSS transitions for collapse */}
      <div
        className="overflow-hidden transition-all duration-300 ease-in-out"
        style={ { maxHeight: open ? '500px' : '0px' } }
      >
        <div className="py-4">{children}</div>
      </div>
    </div>
  );
}

3. Framer Motion: die wichtigsten Konzepte

Framer Motion baut auf drei Kernkonzepten auf: motion-Komponenten, variants und AnimatePresence. Eine motion.div-Komponente ist ein normales div, das drei zusätzliche Props versteht: initial (Startzustand), animate (Zielzustand) und exit (Zustand beim Unmount). Framer Motion interpoliert zwischen diesen Zuständen mit einem eigenen Animation-Engine, der physikbasierte Spring-Animationen, gestaffelte Sequences und komplexe Timing-Kurven unterstützt. Für diese Klasse von React Animations bietet kein CSS-Ansatz vergleichbare Ausdrucksstärke.

variants sind benannte Animationszustände, die zwischen Eltern- und Kind-Komponenten propagiert werden. Wenn eine Elternkomponente von "hidden" zu "visible" wechselt, können alle Kinder auf denselben Variantenwechsel reagieren – mit gestaffelten Verzögerungen (staggerChildren) ohne explizites Übergeben von Delay-Props. Das ermöglicht Listen-Animationen, bei denen jedes Element nach dem vorherigen eingeblendet wird, mit minimalem Code. React Animations dieser Komplexität sind mit CSS allein nicht ausdrückbar – Framer Motion ist hier die pragmatische Wahl.

4. Enter- und Exit-Animationen: wo CSS nicht reicht

Das fundamentale Problem mit Exit-React-Animations und CSS: Wenn React ein Element aus dem DOM entfernt, passiert das synchron und sofort. Eine CSS Transition, die beim Entfernen ablaufen soll, hat keine Chance – das Element ist weg, bevor die Transition starten kann. Das ist kein React-Bug, sondern eine strukturelle Eigenschaft des DOM-Modells. Framer Motion's AnimatePresence löst dieses Problem: Es verzögert das Entfernen aus dem DOM, bis die Exit-Animation abgeschlossen ist.

AnimatePresence umschließt den bedingt gerenderten Teil der UI und beobachtet, welche Kinder entfernt werden. Wenn ein Kind unmountet wird, führt AnimatePresence die exit-Animation aus und entfernt das Element erst danach aus dem DOM. Das ermöglicht elegante React Animations für Modals, Toasts, Dropdown-Menüs, Routing-Übergänge und alle anderen Patterns, bei denen Elemente ein- und ausgeblendet werden. Mit der mode="wait"-Option wartet AnimatePresence, bis das ausgehende Element verschwunden ist, bevor das eingehende erscheint – ideal für Seiten-Transitions.


import { AnimatePresence, motion } from 'framer-motion';
import { useState } from 'react';

// Modal with enter and exit animation — impossible with CSS alone
function Modal({ isOpen, onClose, children }: {
  isOpen: boolean;
  onClose: () => void;
  children: React.ReactNode;
}) {
  return (
    // AnimatePresence keeps the element mounted until exit animation completes
    <AnimatePresence>
      {isOpen && (
        <>
          {/* Backdrop fade */}
          <motion.div
            key="backdrop"
            className="fixed inset-0 bg-black/50"
            initial={ { opacity: 0 } }
            animate={ { opacity: 1 } }
            exit={ { opacity: 0 } }
            onClick={onClose}
          />
          {/* Modal slide-up with spring physics */}
          <motion.div
            key="modal"
            className="fixed inset-x-4 bottom-0 sm:inset-auto sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2 sm:-translate-y-1/2 bg-white rounded-2xl p-6 shadow-2xl z-50"
            initial={ { opacity: 0, y: 60, scale: 0.95 } }
            animate={ { opacity: 1, y: 0, scale: 1 } }
            exit={ { opacity: 0, y: 60, scale: 0.95 } }
            // Spring physics for natural feel — not possible with CSS ease curves
            transition={ { type: 'spring', damping: 25, stiffness: 300 } }
          >
            {children}
          </motion.div>
        </>
      )}
    </AnimatePresence>
  );
}

// Staggered list animation using variants
const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.08, // each child starts 80ms after the previous
    },
  },
};

const itemVariants = {
  hidden: { opacity: 0, y: 20 },
  visible: { opacity: 1, y: 0 },
};

function AnimatedList({ items }: { items: string[] }) {
  return (
    <motion.ul variants={containerVariants} initial="hidden" animate="visible">
      {items.map((item, i) => (
        // Children inherit variant names from parent — no delay props needed
        <motion.li key={i} variants={itemVariants}>
          {item}
        </motion.li>
      ))}
    </motion.ul>
  );
}

5. Animations-Orchestrierung mit Framer Motion

Orchestrierte React Animations – bei denen mehrere Elemente in einer koordinierten Sequenz animiert werden – sind die Domäne von Framer Motion. Das Schlüssel-Konzept ist die Propagation von variants durch den Komponentenbaum: Eine Elternkomponente steuert den Animations-State, alle Kinder reagieren automatisch darauf, und staggerChildren oder delayChildren in den Transition-Optionen koordinieren das Timing. Das Ergebnis: gestaffelte Einblend-Animationen für Listen, Karten und Navigations-Menüs mit minimalem Code.

Für aufwändige Seiten-Transitions bietet Framer Motion's LayoutGroup und layoutId das "Shared Layout"-Feature: Ein Element kann zwischen verschiedenen Positionen im Komponentenbaum animiert werden, wenn beide Versionen dieselbe layoutId tragen. React erkennt, dass es sich um dasselbe Element handelt, und Framer Motion animiert die FLIP-Transition (First-Last-Invert-Play) automatisch. Dieses Muster ist für React Animations wie Produktkartenübergänge zu Detail-Seiten oder Tab-Übergänge mit wandernden Unterstreichungen sehr elegant und ohne externe Bibliothek kaum umsetzbar.

6. Performance: was CSS-Animations und was JS-Animations kosten

Der kritische Unterschied bei der Performance von React Animations: CSS Transitions auf transform und opacity laufen auf dem Compositor-Thread – komplett außerhalb des JavaScript-Hauptthreads und des React-Render-Zyklus. Sie blockieren nie die Interaktivität der Seite, auch wenn der JS-Thread gerade beschäftigt ist. Framer Motion läuft standardmäßig ebenfalls auf transform und opacity und nutzt dafür die Web Animations API oder requestAnimationFrame – das ist performant, aber dennoch JavaScript-getrieben und damit vom Hauptthread abhängig.

Framer Motion bietet mit useMotionValue und useTransform die Möglichkeit, Werte direkt zu animieren, ohne einen React-Re-Render auszulösen. Das ist performanzkritisch für komplexe React Animations, die auf Scroll-Position oder Mausbewegungen reagieren: Die Animation-Werte ändern sich viele Male pro Sekunde, aber React rendert die Komponente nicht neu – nur die DOM-Eigenschaft wird per Framer Motion direkt aktualisiert. Dieses Muster ist für scroll-getriebene Animations-Effekte die richtige Wahl und vermeidet tausende unnötige Re-Renders pro Sekunde.

7. Bundle-Size und Ladezeiten: Framer Motion im Kontext

Framer Motion hat eine nicht triviale Bundle-Größe: minifiziert und gzipped etwa 45–55 KB. Das ist ein echter Kostenfaktor für React Animations in performance-kritischen Anwendungen. Wer Framer Motion nur für einen einzelnen Hover-Effekt einsetzt, trägt diesen Overhead unverhältnismäßig. Die Entscheidungsregel: Wenn das Projekt mehrere komplexe Animations-Patterns benötigt – Modals mit Exit-Animations, gestaffelte Listen, Seiten-Transitions – amortisiert sich die Bundle-Größe über den Entwicklungsaufwand, den man alternativ in CSS-Hacks investieren würde.

Framer Motion bietet seit Version 10 einen motion-Export aus framer-motion/client und unterstützt Tree-Shaking. Mit expliziten Imports (import { motion } from 'framer-motion' vs. Named Imports für spezifische Features) lässt sich die inkludierte Codemenge reduzieren. Alternativ für kleinere React Animations Anforderungen: @motionone/dom (~15 KB) oder die Web Animations API direkt. Für Projekte, die kein komplexes Animations-Orchestrierung brauchen, ist react-spring (~20 KB) eine leichtere Alternative mit Spring-Physics-Support.

8. View Transitions API: der Browser-native Ansatz

Die View Transitions API ist eine browser-native Alternative für bestimmte React Animations: Seiten-Transitions und Element-Übergänge ohne JavaScript-Animationsbibliothek. Sie funktioniert durch das Einwickeln von DOM-Änderungen in document.startViewTransition(() => { /* DOM-Änderung */ }). Der Browser erstellt automatisch einen Screenshot des alten Zustands, führt die DOM-Änderung durch, und animiert den Übergang zwischen Alt und Neu mit CSS. Seit Chrome 111 und Safari 18 ist die API breit verfügbar.

Im React-Kontext ist die View Transitions API besonders relevant für Routing-Übergänge. React Router 7 und Next.js haben experimentelle Unterstützung für View Transitions eingebaut. Das Muster: Beim Navigation-Event wird die DOM-Änderung (Router-Update) in startViewTransition gewickelt, und der Browser animiert den Seitenwechsel mit einer Standard-Cross-Fade oder einer per CSS angepassten Transition. Für einfache Seiten-Transitions ist das der schlankkste Ansatz ohne Bundle-Overhead. Für komplexere React Animations mit Interaktivitäts-Abhängigkeiten bleibt Framer Motion die vollständigere Lösung.


import { motion, useMotionValue, useTransform, useScroll } from 'framer-motion';

// Scroll-driven animation — no re-render on scroll, only DOM updates
function ParallaxHero({ imageSrc }: { imageSrc: string }) {
  const { scrollY } = useScroll();
  // Transform scroll position to translateY — direct DOM update, no React re-render
  const y = useTransform(scrollY, [0, 500], [0, -150]);

  return (
    <div className="relative h-screen overflow-hidden">
      <motion.img
        src={imageSrc}
        alt="Hero"
        className="absolute inset-0 w-full h-full object-cover"
        style={ { y } } // motion value — bypasses React state, goes directly to style
      />
    </div>
  );
}

// View Transitions API — browser-native, no Framer Motion needed for simple page transitions
function NavigationLink({ href, children }: { href: string; children: React.ReactNode }) {
  const navigate = useNavigate(); // React Router

  const handleClick = (e: React.MouseEvent) => {
    e.preventDefault();
    // Wrap navigation in View Transition for smooth cross-fade
    if (!document.startViewTransition) {
      navigate(href);
      return;
    }
    document.startViewTransition(() => {
      // React Router updates the DOM inside the transition callback
      navigate(href);
    });
  };

  return <a href={href} onClick={handleClick}>{children}</a>;
}

// Gesture-driven animation with drag constraints
function DraggableCard({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      drag="x"
      dragConstraints={ { left: -100, right: 100 } }
      dragElastic={0.2}
      // Spring physics snap back to center when released
      whileDrag={ { scale: 1.05, cursor: 'grabbing' } }
      className="cursor-grab bg-white rounded-2xl p-6 shadow-lg"
    >
      {children}
    </motion.div>
  );
}

9. Framer Motion vs. CSS Transitions: direkter Vergleich

Welche Technologie für welche React Animation? Die folgende Übersicht gibt klare Entscheidungsregeln basierend auf dem Animations-Typ.

Animations-Typ Empfehlung Grund Performance
Hover-Effekte CSS Transition Kein JS overhead, Compositor-Thread Optimal
State-Übergang (Klasse) CSS Transition Tailwind-Klassen reichen aus Optimal
Lade-Indikatoren CSS @keyframes Kontinuierlich, kein Zustand Optimal
Modal einblenden Framer Motion Exit-Animation beim Unmount Gut (transform/opacity)
Gestaffelte Listen Framer Motion staggerChildren Koordination Gut
Seiten-Transitions View Transitions API Browser-nativ, kein Bundle Optimal (nativ)
Scroll-Animationen Framer Motion useMotionValue ohne Re-Render Gut (kein Re-Render)
Drag & Gesture Framer Motion drag-Prop, Spring-Physics Gut

Die Tabelle zeigt: Fast die Hälfte der typischen React Animations-Anforderungen lässt sich ohne Framer Motion lösen. CSS Transitions und @keyframes sind für alles ausreichend, was zustandsgetrieben und ohne Mount/Unmount-Übergang auskommt. Framer Motion lohnt sich, wenn Exit-Animations, Spring-Physics, Orchestrierung oder Scroll-Parallax benötigt werden. Die View Transitions API ist der Wildcard: Browser-nativ, zero Bundle-Size, ideal für Routing-Übergänge – aber noch mit eingeschränkter Browser-Unterstützung für komplexe Szenarien.

Mironsoft

React-Animationen, UI-Performance und Frontend-Architektur

React-Animationen für euer Produkt?

Wir implementieren performante React Animations mit CSS Transitions und Framer Motion – abgestimmt auf den Animations-Typ, die Performance-Anforderungen und die Bundle-Size-Ziele des Projekts.

Animations-Audit

Analyse bestehender Animations auf Performance-Bottlenecks und unnötige Bibliothek-Abhängigkeiten

Framer Motion

Komplexe Exit-Animations, orchestrierte Sequences und Shared-Layout-Transitions implementieren

Performance

Layout-thrashing identifizieren, transform/opacity-only Animations einführen, Bundle optimieren

10. Zusammenfassung

React Animations brauchen das richtige Werkzeug für den richtigen Animations-Typ. CSS Transitions auf transform und opacity sind für zustandsabhängige Änderungen die performanteste Lösung: Compositor-Thread, kein JavaScript, kein Bundle-Overhead. CSS @keyframes für kontinuierliche Animationen ohne Zustandsabhängigkeit. Framer Motion für die Animationsklassen, die CSS strukturell nicht kann: Exit-Animations beim Unmount mit AnimatePresence, orchestrierte gestaffelte Sequences mit variants, Spring-Physics für natürliche Bewegungsgefühle und scroll-getriebene Effekte ohne Re-Renders mit useMotionValue.

Die View Transitions API ist der aufkommende dritte Weg für React Animations im Routing-Kontext – browser-nativ, ohne Bundle-Overhead und mit wachsender Framework-Integration. Die wichtigste Faustregel bleibt: Nie Layout-Eigenschaften animieren (width, height, margin), immer nur transform und opacity. Wer diese Grenze respektiert, erzielt mit CSS Transitions und Framer Motion gleichermaßen 60fps-Animationen – der Unterschied liegt ausschließlich in der Ausdrucksstärke und den Features, nicht in der Grundperformance.

React Animations — Das Wichtigste auf einen Blick

CSS Transitions

Für Hover, State-Änderungen und einfache Ein-/Ausblendungen. Compositor-Thread, kein JS-Overhead. transform und opacity animieren, nie Layout-Eigenschaften.

Framer Motion

Für Exit-Animations (AnimatePresence), orchestrierte Sequences (variants + staggerChildren), Spring-Physics und scroll-getriebene Effekte (useMotionValue).

View Transitions API

Browser-nativ für Seiten-Transitions. document.startViewTransition() wrapping DOM-Änderungen. Kein Bundle-Overhead. Browser-Unterstützung prüfen.

Performance-Regel

Nur transform (translate, scale, rotate) und opacity animieren – egal ob CSS oder Framer Motion. Layout-Eigenschaften triggern Reflow und sind zu vermeiden.

11. FAQ: React Animations – Framer Motion vs. CSS Transitions

1Warum CSS Transitions beim Unmount nicht funktionieren?
React entfernt Elemente sofort aus dem DOM. AnimatePresence verzögert das Entfernen bis die exit-Animation abgeschlossen ist.
2Framer Motion immer die beste Wahl?
Nein. CSS Transitions sind für Hover und State-Änderungen schneller und ohne Bundle-Overhead. Framer Motion für Exit-Animations, Spring-Physics und Orchestrierung.
3Welche Eigenschaften performant animieren?
Nur transform und opacity. Layout-Eigenschaften wie width und height triggern Reflow – in CSS und Framer Motion gleichermaßen vermeiden.
4Bundle-Größe Framer Motion?
~45–55 KB gzipped. Lohnt sich bei mehreren komplexen Animations-Patterns. Leichter: @motionone/dom (~15 KB) oder Web Animations API.
5Framer Motion vs. react-spring?
Framer Motion: umfangreicheres Feature-Set, deklarativer. react-spring: leichter (~20 KB), imperativere API. Framer Motion für die meisten Projekte zugänglicher.
6Was ist AnimatePresence?
Framer-Motion-Wrapper für Exit-Animations. Verzögert DOM-Entfernung bis exit-Animation abgeschlossen. Für Modals, Toasts und Dropdown-Menüs essenziell.
7Re-Renders bei Scroll-Animationen vermeiden?
useMotionValue und useScroll – MotionValues aktualisieren direkt DOM-Eigenschaften ohne React-Re-Render. Entscheidend für scroll-getriebene Effekte.
8Was ist die View Transitions API?
Browser-native API für DOM-Übergangs-Animationen. document.startViewTransition() – kein Bundle, ideal für Seiten-Transitions. Browser-Unterstützung prüfen.
9Gestaffelte Listen-Animationen?
Framer Motion variants mit staggerChildren: Container-Variante steuert, Kinder reagieren gestaffelt – ohne explizite Delay-Props.
10Framer Motion mit Server Components?
Nur in Client Components ('use client'). Daten aus Server Components als Props übergeben, animierte Teile als Client Components implementieren.