JAVASCRIPT
Building a Reusable Expand/Collapse Transition Component in Vue 3
Create a flexible and reusable expand/collapse transition component in Vue 3 using the `<Transition>` component and JavaScript hooks for smooth, dynamic animations.
<!-- ExpandCollapseTransition.vue -->
<template>
<Transition
name="expand-collapse"
@enter="onEnter"
@leave="onLeave"
@before-enter="onBeforeEnter"
@after-enter="onAfterEnter"
@before-leave="onBeforeLeave"
@after-leave="onAfterLeave"
>
<slot></slot>
</Transition>
</template>
<script setup>
const onBeforeEnter = (el) => {
el.style.height = '0';
el.style.overflow = 'hidden';
};
const onEnter = (el) => {
el.style.height = 'auto'; // Temporarily set to auto to calculate natural height
const { height } = getComputedStyle(el);
el.style.height = '0'; // Reset to 0 for the transition start
// Force a reflow to ensure the height transition works from 0 to the target height
requestAnimationFrame(() => {
el.style.height = height; // Animate to auto-calculated height
});
};
const onAfterEnter = (el) => {
el.style.height = ''; // Remove fixed height after transition to allow content resizing
el.style.overflow = '';
};
const onBeforeLeave = (el) => {
el.style.height = getComputedStyle(el).height; // Set current height before transition
el.style.overflow = 'hidden';
};
const onLeave = (el) => {
requestAnimationFrame(() => {
el.style.height = '0'; // Animate to 0
});
};
const onAfterLeave = (el) => {
el.style.height = '';
el.style.overflow = '';
};
</script>
<style>
.expand-collapse-enter-active,
.expand-collapse-leave-active {
transition: height 0.3s ease-in-out;
}
</style>
<!-- App.vue (or any parent component) -->
<template>
<div>
<h1>Expand/Collapse Example</h1>
<button @click="showContent = !showContent">
{{ showContent ? 'Collapse' : 'Expand' }}
</button>
<ExpandCollapseTransition>
<div v-if="showContent" class="content-box">
<p>This is some content that will expand and collapse.</p>
<p>It can be dynamic in height and still animate smoothly.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</ExpandCollapseTransition>
<p style="margin-top: 20px;">Other content on the page.</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import ExpandCollapseTransition from './ExpandCollapseTransition.vue';
const showContent = ref(false);
</script>
<style>
.content-box {
border: 1px solid #ddd;
padding: 15px;
margin-top: 10px;
background-color: #f9f9f9;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
</style>
How it works: This snippet demonstrates creating a reusable `ExpandCollapseTransition` component in Vue 3 that uses the `<Transition>` component with JavaScript hooks to animate dynamic height changes. By manually setting `height: 0` and `overflow: hidden` on `before-enter` and then animating to the element's computed height on `enter`, and performing the reverse on `leave`, we achieve a smooth expand/collapse effect that works reliably even with content of unknown or changing height. This approach provides more control than pure CSS transitions for certain complex animation scenarios.