<v/>
{ }
Vue 3 · Transition · TransitionGroup · Animation · Performance
Transitionen in Vue 3:
was wirklich smooth ist und was nur blinkt

Vue Transitionen sind mächtig, aber voller versteckter Fallen. Ein falsch gesetztes CSS-Property löst Layout Reflow aus und macht die Animation ruckelig. Ein fehlender key-Wechsel lässt Elemente verschwinden, ohne dass Transition überhaupt ausgelöst wird. Dieser Artikel erklärt, wie Vue Transitionen tatsächlich funktionieren, welche CSS-Properties smooth laufen und welche Muster in der Praxis zuverlässig schöne Animationen liefern.

17 Min. Lesezeit Transition · TransitionGroup · Route-Transitions · FLIP · JavaScript-Hooks Vue 3.4+ · CSS Transitions · Web Animations API

1. Wie Vue Transition intern funktioniert

Die Vue Transition-Komponente ist kein reines CSS-Feature, sondern eine Laufzeit-Koordination zwischen Vue und dem Browser. Wenn ein Element unter <Transition> durch v-if oder v-show auftaucht, fügt Vue zu definierten Zeitpunkten CSS-Klassen hinzu und entfernt sie. Vue wartet dabei auf den Browser — konkret: Auf das nächste Animations-Frame, um sicherzustellen, dass der Browser die Ausgangs-Klasse tatsächlich gerendert hat, bevor die aktive Klasse hinzugefügt wird. Dieses Warten auf den Browser ist notwendig, weil CSS-Transitions nur ausgelöst werden, wenn der Browser einen Zustandsunterschied zwischen zwei Frames beobachtet.

Ein häufiges Missverständnis: Vue Transition startet die Animation nicht. Es fügt CSS-Klassen hinzu — der Browser führt dann die CSS-Transition aus, die in diesen Klassen definiert ist. Wenn die CSS-Transition-Property fehlt oder auf 0s gesetzt ist, passiert kein sichtbarer Übergang, obwohl Vue die Klassen korrekt setzt. Wenn das Element beim Einblenden keine Zeit hat, den Ausgangszustand gerendert zu bekommen — weil Vue und Browser zu schnell hintereinander arbeiten — startet die Transition von einem falschen Ausgangspunkt. Der nextTick-Mechanismus in Vue stellt sicher, dass dieser Timing-Fehler systematisch vermieden wird.

Für v-show ist das Verhalten anders als für v-if. Bei v-if wird das Element beim Entfernen tatsächlich aus dem DOM genommen, nachdem die Leave-Transition abgeschlossen ist. Bei v-show wird statt DOM-Entfernung nur display: none gesetzt. Vue Transition behandelt beide korrekt, aber die Leave-Animation bei v-show kollidiert manchmal mit bereits definierten display-Properties im CSS — ein häufiger Bug, der zu sofortigem Verschwinden statt Animation führt.

2. Die sechs CSS-Klassen und wann sie gesetzt werden

Die Vue Transition-Komponente setzt sechs CSS-Klassen: drei für das Einblenden (Enter) und drei für das Ausblenden (Leave). Das Muster ist [name]-enter-from, [name]-enter-active, [name]-enter-to und entsprechend für Leave. -from definiert den Ausgangszustand, -to den Zielzustand und -active enthält die eigentliche transition-Deklaration — also Dauer, Easing-Funktion und die zu animierenden Properties. Die häufigste Fehlerquelle: transition in -active vergessen. Dann wechselt das Element sofort von -from zu -to, ohne Animation.

Vue setzt die Klassen in dieser Reihenfolge für Enter: Zuerst -from und -active gleichzeitig, dann wartet Vue einen Frame, entfernt -from und fügt -to hinzu. Der Browser sieht jetzt den Unterschied zwischen -from-Werten und -to-Werten und startet die CSS-Transition. Nach Abschluss der Transition — erkannt über das transitionend-Event — entfernt Vue alle Enter-Klassen. Das ist wichtig zu verstehen: Vue wartet auf das transitionend-Event des Browsers. Wenn dieses Event nicht feuert — z.B. weil kein transition-Property definiert ist oder das Element sofort aus dem DOM entfernt wurde — hängt Vue in einem undefinierten Zustand.


/* Correct Vue Transition CSS — all 6 classes defined */
/* The -active class MUST contain the transition property */

/* Fade transition — opacity only (compositor-layer, 60fps) */
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

.fade-enter-active,
.fade-leave-active {
  /* transition defined here — duration, property, easing */
  transition: opacity 250ms ease-in-out;
}

/* -enter-to and -leave-from inherit the component's default style */
/* (opacity: 1 by default) — no need to declare them explicitly */

/* Slide-up transition — transform only (compositor, no layout) */
.slide-up-enter-from {
  transform: translateY(16px);
  opacity: 0;
}
.slide-up-leave-to {
  transform: translateY(-16px);
  opacity: 0;
}
.slide-up-enter-active,
.slide-up-leave-active {
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1),
              opacity 300ms ease;
}

3. Was wirklich smooth ist: opacity, transform, clip-path

Nicht alle CSS-Properties sind gleich für Vue Transitionen. Der Browser rendert CSS-Transitions auf zwei Wegen: Properties, die einen Layout Reflow auslösen (width, height, margin, padding, top, left), müssen in jedem Frame vom Browser neu berechnet werden. Das ist CPU-intensiv und führt auf schwächeren Geräten zu spürbarem Ruckeln. Properties, die nur den Compositor-Layer betreffen (opacity, transform), können direkt von der GPU animiert werden — kein Layout Reflow, 60fps auch auf mobilen Geräten.

Die goldene Regel für smooth Vue Transitionen: Nur opacity und transform animieren. Wenn ein Element größer werden soll, statt width zu animieren lieber mit transform: scale() arbeiten. Wenn ein Element von oben hereingleiten soll, statt top: -100px zu top: 0 zu animieren lieber mit transform: translateY(-100px) starten. clip-path ist eine weitere Ausnahme, die auf modernen Browsers ohne Layout-Reflow animiert werden kann und für reveal-Effekte interessant ist. filter (blur, brightness) kann ebenfalls smooth animiert werden, ist aber auf mobilen Geräten teurer als opacity und transform.

4. Transition-Modes: out-in und in-out richtig einsetzen

Wenn ein Element unter <Transition> durch ein anderes ersetzt wird — z.B. durch den Wechsel eines :key-Attributs oder eines v-if/v-else-Paares — laufen standardmäßig Leave- und Enter-Transition gleichzeitig. Das bedeutet: Das alte Element blendet aus, während das neue einblendet. In vielen Layouts führt das zu einem visuellen "Sprung", weil beide Elemente kurzzeitig Platz beanspruchen. Der Vue Transition-Mode out-in löst das: Erst das alte Element ausblenden, dann das neue einblenden. Das ist die sicherste Option für die meisten Anwendungsfälle.

Der Mode in-out ist das Gegenteil: Das neue Element blendet zuerst ein, dann blendet das alte aus. Das klingt seltsam, ist aber für Slide-Transitionen nützlich, bei denen das neue Element von der Seite hereingleitet und das alte gleichzeitig in dieselbe Richtung weitergleitet. Ohne Mode ist Vue Transition bei einem key-Wechsel auf demselben Element manchmal schwer vorhersehbar — gerade bei schnellen Wechseln, bei denen eine laufende Enter-Transition von einer neuen Leave-Transition unterbrochen wird. Mode out-in verhindert diesen Race-Condition-Effekt vollständig.

5. TransitionGroup: Listentransitionen ohne Layout-Sprünge

Vue TransitionGroup ist für Animationen von Listen-Elementen zuständig, die ihre Positionen wechseln, hinzugefügt oder entfernt werden. Das Feature, das Vue TransitionGroup besonders macht, ist die FLIP-Animation (First, Last, Invert, Play): Wenn ein Element seine Position in der Liste ändert, berechnet Vue die Differenz zwischen alter und neuer Position und animiert das Element mit transform: translateX()/translateY() von der alten zur neuen Position. Das geschieht ohne Layout-Animation — der Browser sieht immer nur die Endposition, die Animation ist eine reine Transform-Animation.

FLIP in Vue TransitionGroup wird über die -move-Klasse gesteuert: [name]-move { transition: transform 300ms ease; }. Ohne diese Klasse gibt es keine FLIP-Animation — Elemente springen sofort zur neuen Position. Das tag-Attribut von Vue TransitionGroup definiert das Wrapper-Element im DOM. Standard ist span, was für Block-Elemente falsch ist — für Listenelemente sollte tag="ul" oder tag="div" explizit gesetzt werden. Wichtig: Jedes Kind von Vue TransitionGroup muss einen eindeutigen :key haben — ohne Key funktioniert weder Transition noch FLIP korrekt.


/* TransitionGroup with FLIP move transition */
/* All three types: enter, leave, move */

.list-enter-from {
  opacity: 0;
  transform: translateX(-20px);
}
.list-leave-to {
  opacity: 0;
  transform: translateX(20px);
}
.list-enter-active,
.list-leave-active {
  transition: opacity 250ms ease, transform 250ms ease;
}

/* FLIP: Vue calculates position delta and applies as transform */
/* Without this class: elements jump instantly to new position */
.list-move {
  transition: transform 350ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Critical: leaving elements must be taken out of flow
   during the move transition, otherwise FLIP breaks */
.list-leave-active {
  position: absolute;  /* remove from flow while leaving */
}

/* Usage in template:
   <TransitionGroup name="list" tag="ul">
     <li v-for="item in sortedItems" :key="item.id">...</li>
   </TransitionGroup>
*/

6. Route-Transitionen mit Vue Router

Route-Transitionen in Vue 3 mit dem Vue Router werden über den <RouterView>-Slot realisiert. Das Muster: <RouterView v-slot="{ Component }"> gibt die aktuelle Route-Komponente als Slot-Variable heraus, die dann in ein <Transition>-Wrapper gepackt wird. Das :key="route.fullPath" auf dem <component>-Tag stellt sicher, dass Vue Transition ausgelöst wird, auch wenn nur Query-Parameter sich ändern — ohne :key bleibt die gleiche Komponenten-Instanz bestehen und Transition wird nicht aktiviert.

Ein häufiger Fehler bei Route-Vue Transitionen: Der Transition-Wrapper direkt auf <RouterView> statt im Slot. Vue Router 4 hat die API geändert — der Wrapper muss innerhalb des Slots sitzen, nicht als Elternelement. Ein zweiter häufiger Fehler: Verschiedene Route-Transitionen je nach Navigationsrichtung (vorwärts/rückwärts) implementieren. Das erfordert, die Navigationsrichtung im Router-Hook zu erkennen, in einem Ref zu speichern und als dynamischen Transition-Namen zu übergeben. Die Koordination zwischen Router-Navigation und Transition-Timing ist der schwierigste Teil dieser Architektur.

7. JavaScript-Hooks für komplexe Animationen

Vue Transition unterstützt JavaScript-Hooks als Alternative zu reinen CSS-Transitions: @before-enter, @enter, @after-enter und die entsprechenden Leave-Hooks. In diesen Hooks können beliebige JavaScript-Animationsbibliotheken verwendet werden — GSAP, Anime.js oder die native Web Animations API. Der @enter-Hook erhält das DOM-Element als erstes Argument und eine done-Callback-Funktion als zweites. done() muss aufgerufen werden, wenn die Animation abgeschlossen ist, damit Vue das Lifecycle-Management korrekt fortsetzen kann. Ohne done()-Call bleibt die Komponente im Enter-Zustand hängen.

Das wichtige Attribut bei JavaScript-Hooks ist :css="false" auf dem <Transition>-Tag. Damit weist man Vue an, keine CSS-Klassen zu setzen und auch nicht auf transitionend-Events zu warten. Ohne :css="false" überschneiden sich CSS-Klassen und JavaScript-Animationen, was zu undefiniertem Verhalten führt. JavaScript-Hooks sind besonders sinnvoll für sequenzielle Animationen (mehrere Elemente nacheinander), physikbasierte Animationen (Federdynamik) und für Animationen, die Zustandsdaten aus dem Vue-Store benötigen — was in CSS nicht möglich ist.

8. Performance-Fallen und wie man sie erkennt

Die häufigste Performance-Falle bei Vue Transitionen ist das Animieren von Properties, die Layout Reflow auslösen. Wenn im DevTools-Performance-Profiler "Layout" und "Recalculate Style" während einer Animation erscheinen, werden Layout-triggernde Properties animiert. Die Lösung ist fast immer, stattdessen transform und opacity zu verwenden. Eine hilfreiche Ressource ist CSS Triggers (csstriggers.com), die dokumentiert, welche Properties Reflow, Repaint oder nur Composite auslösen. Nur Composite-Operationen laufen smooth auf dem GPU-Compositor.

Die zweite Performance-Falle ist zu viele gleichzeitig animierte Elemente in Vue TransitionGroup. FLIP ist teuer bei großen Listen, weil Vue für jedes Element die Position vor und nach der Änderung berechnet. Bei Listen über 100 Elementen sollte man entweder auf Virtualisierung setzen (vue-virtual-scroller) oder die FLIP-Animation deaktivieren und nur Enter/Leave animieren. Die dritte Falle: Transitions auf Elementen, die Kinder mit komplexem Painting haben. Ein Schatten auf einem animierten Element erzeugt bei jeder Frame-Aktualisierung ein neues Layer-Composite. will-change: transform promotet ein Element auf eine eigene Compositor-Layer, was Animationen beschleunigt, aber Speicher kostet — sparsam einsetzen.

9. Vergleich: smooth vs. ruckelig

Dieser Vergleich zeigt die häufigsten Ursachen von ruckelnden Vue Transitionen und die jeweilige Lösung für smooth Animationen.

Problem Ruckelig (blinkt) Smooth Grund
Größe animieren width/height transition transform: scale() scale() nur Compositor, kein Reflow
Position animieren top/left transition transform: translate() translate() kein Layout-Reflow
Element-Wechsel Kein mode → beide gleichzeitig mode="out-in" Kein doppeltes Layout
Liste umordnen Kein -move Klasse TransitionGroup + FLIP Transform-Animation statt Sprung
Route-Transition Wrapper um RouterView (vue router 4) RouterView v-slot + Transition Korrekte API in Vue Router 4

Ein Sonderfall sind Transitionen bei Modals mit Vue Teleport. Das Modal-DOM wird zu body teleportiert, aber die Vue Transition-Klassen werden korrekt auf das teleportierte Element angewendet. Das bedeutet: Vue Transition und Vue Teleport sind vollständig kompatibel und können direkt kombiniert werden. Die Transition-Klassen müssen in einem globalen CSS-File oder im Component-Scoped-CSS mit :deep() definiert sein, wenn das Modal-Element außerhalb des Scoped-CSS-Kontexts liegt.

Mironsoft

Vue 3 UI-Qualität und Animation-Performance

Ruckelnde Vue Transitionen durch smooth Animationen ersetzen?

Wir analysieren bestehende Vue-Animationen auf Layout-Reflow-Probleme und refactorieren sie zu compositor-only Transitions — messbar smooth auf allen Geräten.

Animation-Audit

DevTools-Performance-Analyse aller aktiven Transitionen auf Layout-Reflow

FLIP-Integration

TransitionGroup mit FLIP-Animationen für smooth Listensortierung und -filterung

Route-Transitions

Direktionale Route-Transitionen mit Navigationsrichtungs-Erkennung im Vue Router

10. Zusammenfassung

Vue Transitionen sind dann smooth, wenn sie ausschließlich opacity und transform animieren, den richtigen Mode verwenden und bei Listentransitionen mit FLIP arbeiten. Die wichtigsten Punkte: Die -active-Klasse muss immer die transition-Deklaration enthalten, sonst passiert kein sichtbarer Übergang. Mode out-in ist die sichere Standardwahl für Element-Wechsel. Vue TransitionGroup braucht die -move-Klasse und position: absolute auf -leave-active für korrekte FLIP-Animationen. Route-Transitionen in Vue Router 4 werden im v-slot von <RouterView> definiert.

Was blinkt statt smooth zu sein: Layout-Properties wie width, height, margin und top/left, die in jedem Frame Layout Reflow auslösen. Transitions ohne expliziten Mode bei Element-Wechseln, die zu doppeltem Layout führen. Fehlende -move-Klasse in Vue TransitionGroup, die Elemente springen lässt. JavaScript-Hooks ohne :css="false", die mit CSS-Klassen kollidieren. Wer diese Fallgruben kennt und mit den richtigen CSS-Properties arbeitet, baut Vue Transitionen, die auf jedem Gerät flüssig laufen.

Vue Transitionen — Das Wichtigste auf einen Blick

Smooth Properties

Nur opacity und transform animieren. Scale statt width/height. Translate statt top/left. Kein Layout Reflow = 60fps auf mobilen Geräten.

CSS-Klassen-Struktur

-from: Ausgangszustand. -active: transition-Deklaration (Pflicht!). -to: Zielzustand (oft default). Ohne transition in -active: kein sichtbarer Übergang.

TransitionGroup & FLIP

-move Klasse für FLIP-Positionsanimation. position:absolute auf -leave-active. Eindeutige :key auf jedem Kind. Tag explizit auf div oder ul setzen.

JavaScript-Hooks

:css="false" wenn JS-Hooks verwendet werden. done()-Callback immer aufrufen. Für sequenzielle Animationen, Federdynamik und Store-abhängige Animationen.

11. FAQ: Vue Transitionen

1Warum startet meine Vue Transition nicht?
transition-Property fehlt in der -active-Klasse. Oder keine CSS-Unterschiede zwischen -from und -to. Bei v-show: display:none-Kollision prüfen.
2out-in vs. in-out?
out-in: Alt ausblenden, dann neu einblenden. Sicherster Standard. in-out: Neu einblenden während alt noch sichtbar ist. Für gleichzeitige Slide-Effekte.
3Ruckelt auf mobilen Geräten?
Layout-Properties werden animiert: width, height, margin, top, left. Lösung: transform und opacity verwenden — kein Layout Reflow, 60fps auf GPU.
4Was ist FLIP in TransitionGroup?
Position vor und nach der Änderung berechnen, Differenz als transform animieren. Aktiviert über die -move CSS-Klasse. Kein Layout-Reflow bei Listenumordnung.
5position:absolute auf -leave-active?
Ohne es beansprucht das ausblendende Element noch Platz im Layout und stört FLIP-Berechnung der anderen Elemente. absolute nimmt es aus dem Fluss.
6Transition mit Vue Router 4?
RouterView v-slot nutzen, Transition darin wickeln, :key="route.fullPath" auf component setzen. Ohne key: keine Transition bei gleicher Komponente mit anderen Params.
7JavaScript-Hooks wann sinnvoll?
Sequenzielle Animationen, Federdynamik (GSAP), Store-abhängige Animationen. Immer :css="false" und done()-Callback aufrufen.
8Was macht :css="false"?
Deaktiviert CSS-Klassen-Handling und transitionend-Warten. Ohne es bei JS-Hooks: CSS und JavaScript kollidieren zu undefiniertem Verhalten.
9Transition mit Teleport kombinieren?
Vollständig kompatibel. CSS-Definitionen in globalem CSS oder :deep() in Scoped CSS, da das Element außerhalb des Scoped-Kontexts im DOM sitzt.
10will-change: transform — wofür?
Promotet Element auf eigene GPU-Layer. Beschleunigt häufige Animationen, kostet aber Speicher. Nur für Elemente die häufig und lange animiert werden.