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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs