How to build an accordion in Vue

Accordions organize large amounts of content into collapsible sections, improving page scannability and reducing initial visual complexity. As the creator of CoreUI with over 12 years of Vue.js experience since 2014, I’ve implemented accordion components in countless enterprise applications. A Vue accordion requires reactive state to track which items are open, methods to toggle items, and transitions for smooth expand/collapse animations. This approach creates a reusable accordion without external dependencies.

Build an accordion using reactive state, v-for for items, and transitions for smooth animations.

<template>
  <div class='accordion'>
    <div
      v-for='(item, index) in items'
      :key='index'
      class='accordion-item'
    >
      <div
        class='accordion-header'
        @click='toggle(index)'
      >
        <span>{{ item.title }}</span>
        <span class='accordion-icon'>{{ isOpen(index) ? '−' : '+' }}</span>
      </div>
      <Transition name='accordion'>
        <div v-if='isOpen(index)' class='accordion-content'>
          <div class='accordion-body'>
            {{ item.content }}
          </div>
        </div>
      </Transition>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const props = defineProps({
  items: {
    type: Array,
    required: true
  },
  multiple: {
    type: Boolean,
    default: false
  }
})

const openItems = ref([0])

const toggle = (index) => {
  if (props.multiple) {
    const idx = openItems.value.indexOf(index)
    if (idx > -1) {
      openItems.value.splice(idx, 1)
    } else {
      openItems.value.push(index)
    }
  } else {
    openItems.value = openItems.value.includes(index) ? [] : [index]
  }
}

const isOpen = (index) => {
  return openItems.value.includes(index)
}
</script>

<style scoped>
.accordion {
  border: 1px solid #ddd;
  border-radius: 4px;
  overflow: hidden;
}

.accordion-item {
  border-bottom: 1px solid #ddd;
}

.accordion-item:last-child {
  border-bottom: none;
}

.accordion-header {
  display: flex;
  justify-content: space-between;
  padding: 15px;
  background: #f5f5f5;
  cursor: pointer;
  user-select: none;
}

.accordion-header:hover {
  background: #e9e9e9;
}

.accordion-icon {
  font-weight: bold;
  font-size: 20px;
}

.accordion-content {
  overflow: hidden;
}

.accordion-body {
  padding: 15px;
}

.accordion-enter-active,
.accordion-leave-active {
  transition: all 0.3s ease;
}

.accordion-enter-from,
.accordion-leave-to {
  max-height: 0;
  opacity: 0;
}

.accordion-enter-to,
.accordion-leave-from {
  max-height: 500px;
  opacity: 1;
}
</style>

Use it in your component:

<template>
  <Accordion :items='faqItems' :multiple='true' />
</template>

<script setup>
import { ref } from 'vue'
import Accordion from './Accordion.vue'

const faqItems = ref([
  { title: 'What is Vue?', content: 'Vue is a progressive JavaScript framework...' },
  { title: 'How to install Vue?', content: 'You can install Vue using npm...' },
  { title: 'What are components?', content: 'Components are reusable Vue instances...' }
])
</script>

Best Practice Note

The multiple prop controls whether multiple items can be open simultaneously. For better animations, consider using height transitions with actual calculated heights instead of max-height. Add keyboard navigation (Enter/Space to toggle, Arrow keys to navigate) for accessibility. This is the pattern we use in CoreUI for Vue—building accessible accordion components with smooth animations and flexible configuration options for enterprise admin panels and documentation sites.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
How to limit items in a .map loop in JavaScript
How to limit items in a .map loop in JavaScript

What is the difference between sort and toSorted in JavaScript?
What is the difference between sort and toSorted in JavaScript?

How to concatenate a strings in JavaScript?
How to concatenate a strings in JavaScript?

What is JavaScript Array.pop() Method?
What is JavaScript Array.pop() Method?

Answers by CoreUI Core Team