Files
agent-skills/skills/vue-best-practices/reference/transition-mode-out-in.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

213 lines
5.2 KiB
Markdown

---
title: Use mode="out-in" for Sequential Element Transitions
impact: MEDIUM
impactDescription: Without transition mode, entering and leaving elements animate simultaneously, causing overlap and layout issues
type: best-practice
tags: [vue3, transition, animation, mode, out-in, in-out]
---
# Use mode="out-in" for Sequential Element Transitions
**Impact: MEDIUM** - By default, Vue's `<Transition>` runs enter and leave animations simultaneously. This causes the old and new elements to overlap during the transition, often resulting in visual glitches, layout jumping, or elements appearing stacked. Use `mode="out-in"` to ensure the old element fully animates out before the new one enters.
## Task Checklist
- [ ] Use `mode="out-in"` when transitioning between mutually exclusive elements
- [ ] This is especially important for buttons, tabs, or content that shouldn't overlap
- [ ] Consider `mode="in-out"` only for specific overlapping effects (rare)
- [ ] Without a mode, both animations run in parallel (default behavior)
**Problematic Code:**
```vue
<template>
<!-- BAD: No mode - buttons overlap during transition! -->
<Transition name="fade">
<button v-if="isEditing" key="save" @click="save">Save</button>
<button v-else key="edit" @click="edit">Edit</button>
</Transition>
</template>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>
<!--
Result: Both buttons visible during transition
- Old button fading out
- New button fading in simultaneously
- Layout breaks as both take space
-->
```
**Correct Code:**
```vue
<template>
<!-- GOOD: out-in mode ensures sequential animation -->
<Transition name="fade" mode="out-in">
<button v-if="isEditing" key="save" @click="save">Save</button>
<button v-else key="edit" @click="edit">Edit</button>
</Transition>
</template>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
</style>
<!--
Result: Clean sequential animation
1. Edit button fades out completely
2. Save button fades in
No overlap, no layout issues
-->
```
## Transition Modes Explained
### No Mode (Default)
```
Time: |----- leave animation -----|
|----- enter animation -----|
Elements are animated simultaneously (parallel)
```
### mode="out-in" (Recommended for most cases)
```
Time: |----- leave animation -----|
|----- enter animation -----|
Old element leaves first, then new element enters (sequential)
```
### mode="in-out" (Rare use case)
```
Time: |----- leave animation -----|
|----- enter animation -----|
New element enters first, then old element leaves
```
## When to Use Each Mode
### Use `mode="out-in"` for:
- Toggle buttons (Edit/Save, Play/Pause)
- Tab content switching
- Multi-step forms
- State-based content (Loading/Error/Success)
- Any mutually exclusive content
```vue
<template>
<!-- Tab content switching -->
<Transition name="slide" mode="out-in">
<component :is="currentTabComponent" :key="currentTab" />
</Transition>
<!-- State-based UI -->
<Transition name="fade" mode="out-in">
<LoadingSpinner v-if="loading" key="loading" />
<ErrorMessage v-else-if="error" key="error" :message="error" />
<DataDisplay v-else key="data" :data="data" />
</Transition>
</template>
```
### Use `mode="in-out"` for:
- Card flip effects where new content appears behind old
- Specific design requirements where overlap is intentional
- Rarely used in practice
```vue
<template>
<!-- Card flip effect - new card slides in behind -->
<Transition name="flip" mode="in-out">
<div :key="cardId" class="card">{{ cardContent }}</div>
</Transition>
</template>
```
### Use No Mode for:
- Cross-fade effects where overlap is desired
- Image galleries with smooth blending
- Background transitions
```vue
<template>
<!-- Image cross-fade - overlap creates smooth blend -->
<Transition name="crossfade">
<img :key="currentImage" :src="currentImage" class="gallery-image" />
</Transition>
</template>
<style>
.gallery-image {
position: absolute; /* Stack images on top of each other */
}
.crossfade-enter-active,
.crossfade-leave-active {
transition: opacity 0.5s;
}
.crossfade-enter-from,
.crossfade-leave-to {
opacity: 0;
}
</style>
```
## Handling Absolute Positioning
If you must use the default mode (no mode) for non-overlapping elements, use absolute positioning:
```vue
<template>
<div class="container">
<Transition name="slide">
<div v-if="showA" key="a" class="panel">Panel A</div>
<div v-else key="b" class="panel">Panel B</div>
</Transition>
</div>
</template>
<style>
.container {
position: relative;
height: 200px; /* Fixed height needed */
}
.panel {
position: absolute;
width: 100%;
}
.slide-enter-active,
.slide-leave-active {
transition: transform 0.3s, opacity 0.3s;
}
.slide-enter-from {
transform: translateX(100%);
opacity: 0;
}
.slide-leave-to {
transform: translateX(-100%);
opacity: 0;
}
</style>
```
## Reference
- [Vue.js Transition Modes](https://vuejs.org/guide/built-ins/transition.html#transition-modes)