Usage
Use the Accordion component to display a list of collapsible items.
<script setup lang="ts">
const items = ref([
  {
    label: 'Is Nuxt UI free to use?',
    content: 'Yes! Nuxt UI is completely free and open source under the MIT license. All 100+ components are available to everyone.'
  },
  {
    label: 'Can I use Nuxt UI with Vue without Nuxt?',
    content: 'Yes! While optimized for Nuxt, Nuxt UI works perfectly with standalone Vue projects via our Vite plugin. You can follow the [installation guide](/docs/getting-started/installation/vue) to get started.'
  },
  {
    label: 'Is Nuxt UI production-ready?',
    content: 'Yes! Nuxt UI is used in production by thousands of applications with extensive tests, regular updates, and active maintenance.'
  }
])
</script>
<template>
  <UAccordion :items="items" />
</template>
Items
Use the items prop as an array of objects with the following properties:
- label?: string
- icon?: string
- trailingIcon?: string
- content?: string
- value?: string
- disabled?: boolean
- slot?: string
- class?: any
- ui?: { item?: ClassNameValue, header?: ClassNameValue, trigger?: ClassNameValue, leadingIcon?: ClassNameValue, label?: ClassNameValue, trailingIcon?: ClassNameValue, content?: ClassNameValue, body?: ClassNameValue }
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>
<template>
  <UAccordion :items="items" />
</template>
Multiple
Set the type prop to multiple to allow multiple items to be active at the same time. Defaults to single.
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>
<template>
  <UAccordion type="multiple" :items="items" />
</template>
Collapsible
When type is single, you can set the collapsible prop to false to prevent the active item from collapsing.
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>
<template>
  <UAccordion :collapsible="false" :items="items" />
</template>
Unmount
Use the unmount-on-hide prop to prevent the content from being unmounted when the accordion is collapsed. Defaults to true.
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>
<template>
  <UAccordion :unmount-on-hide="false" :items="items" />
</template>
Disabled
Use the disabled property to disable the Accordion.
You can also disable a specific item by using the disabled property in the item object.
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.',
    disabled: true
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>
<template>
  <UAccordion disabled :items="items" />
</template>
Trailing Icon
Use the trailing-icon prop to customize the trailing Icon of each item. Defaults to i-lucide-chevron-down.
trailingIcon property in the item object.<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.',
    trailingIcon: 'i-lucide-plus'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>
<template>
  <UAccordion trailing-icon="i-lucide-arrow-down" :items="items" />
</template>
Examples
Control active item(s)
You can control the active item(s) by using the default-value prop or the v-model directive with the index of the item.
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items: AccordionItem[] = [
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
]
const active = ref('0')
// Note: This is for demonstration purposes only. Don't do this at home.
onMounted(() => {
  setInterval(() => {
    active.value = String((Number(active.value) + 1) % items.length)
  }, 2000)
})
</script>
<template>
  <UAccordion v-model="active" :items="items" />
</template>
value of one of the items if provided.type="multiple", ensure to pass an array to the default-value prop or the v-model directive.With drag and drop
Use the useSortable composable from @vueuse/integrations to enable drag and drop functionality on the Accordion. This integration wraps Sortable.js to provide a seamless drag and drop experience.
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
import { useSortable } from '@vueuse/integrations/useSortable'
const items = shallowRef<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
const accordion = useTemplateRef<HTMLElement>('accordion')
useSortable(accordion, items, {
  animation: 150
})
</script>
<template>
  <UAccordion ref="accordion" :items="items" />
</template>
With body slot
Use the #body slot to customize the body of each item.
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items: AccordionItem[] = [
  {
    label: 'Icons',
    icon: 'i-lucide-smile'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box'
  }
]
</script>
<template>
  <UAccordion :items="items">
    <template #body="{ item }">
      This is the {{ item.label }} panel.
    </template>
  </UAccordion>
</template>
#body slot includes some pre-defined styles, use the #content slot if you want to start from scratch.With content slot
Use the #content slot to customize the content of each item.
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items: AccordionItem[] = [
  {
    label: 'Icons',
    icon: 'i-lucide-smile'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box'
  }
]
</script>
<template>
  <UAccordion :items="items">
    <template #content="{ item }">
      <p class="pb-3.5 text-sm text-muted">
        This is the {{ item.label }} panel.
      </p>
    </template>
  </UAccordion>
</template>
With custom slot
Use the slot property to customize a specific item.
You will have access to the following slots:
- #{{ item.slot }}
- #{{ item.slot }}-body
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
const items = [
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    slot: 'colors' as const,
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
] satisfies AccordionItem[]
</script>
<template>
  <UAccordion :items="items">
    <template #colors="{ item }">
      <p class="text-sm pb-3.5 text-primary">
        {{ item.content }}
      </p>
    </template>
  </UAccordion>
</template>
With markdown content
You can use the MDC component from @nuxtjs/mdc to render markdown in the accordion items.
<script setup lang="ts">
const items = [
  {
    label: 'Is Nuxt UI free to use?',
    icon: 'i-lucide-circle-help',
    content: 'Yes! Nuxt UI is completely free and open source under the MIT license. All 100+ components are available to everyone.'
  },
  {
    label: 'Can I use Nuxt UI with Vue without Nuxt?',
    icon: 'i-lucide-circle-help',
    content: 'Yes! While optimized for Nuxt, Nuxt UI works perfectly with standalone Vue projects via our Vite plugin. You can follow the [installation guide](/docs/getting-started/installation/vue) to get started.'
  },
  {
    label: 'Will Nuxt UI work with other CSS frameworks like UnoCSS?',
    icon: 'i-lucide-circle-help',
    content: 'No. Nuxt UI is designed exclusively for Tailwind CSS. UnoCSS support would require significant architecture changes due to different class naming conventions.'
  },
  {
    label: 'How does Nuxt UI handle accessibility?',
    icon: 'i-lucide-circle-help',
    content: 'Through [Reka UI](https://reka-ui.com/docs/overview/accessibility) integration, Nuxt UI provides automatic ARIA attributes, keyboard navigation, focus management, and screen reader support. While offering a strong foundation, testing in your specific use case remains important.'
  },
  {
    label: 'How is Nuxt UI tested?',
    icon: 'i-lucide-circle-help',
    content: 'Nuxt UI ensures reliability with 1000+ Vitest tests covering core functionality and accessibility.'
  },
  {
    label: 'Is Nuxt UI production-ready?',
    icon: 'i-lucide-circle-help',
    content: 'Yes! Nuxt UI is used in production by thousands of applications with extensive tests, regular updates, and active maintenance.'
  }
]
</script>
<template>
  <UAccordion
    type="multiple"
    :items="items"
    :unmount-on-hide="false"
    :default-value="['3']"
    :ui="{
      trigger: 'text-base',
      body: 'text-base text-muted'
    }"
  >
    <template #body="{ item }">
      <MDC :value="item.content" unwrap="p" />
    </template>
  </UAccordion>
</template>
API
Props
| Prop | Default | Type | 
|---|---|---|
| as | 
 | 
 The element or component this component should render as. | 
| items | 
 
 | |
| trailingIcon | 
 | 
 The icon displayed on the right side of the trigger. | 
| labelKey | 
 | 
 The key used to get the label from the item. | 
| collapsible | 
 | 
 When type is "single", allows closing content when clicking trigger for an open item. When type is "multiple", this prop has no effect. | 
| defaultValue | 
 The default active value of the item(s). Use when you do not need to control the state of the item(s). | |
| modelValue | 
 The controlled value of the active item(s). Use this when you need to control the state of the items. Can be binded with  | |
| type | 
 | 
 Determines whether a "single" or "multiple" items can be selected at a time. This prop will overwrite the inferred type from  | 
| disabled | 
 | 
 When  | 
| unmountOnHide | 
 | 
 When  | 
| ui | 
 | 
Slots
| Slot | Type | 
|---|---|
| leading | 
 | 
| default | 
 | 
| trailing | 
 | 
| content | 
 | 
| body | 
 | 
Emits
| Event | Type | 
|---|---|
| update:modelValue | 
 | 
Theme
export default defineAppConfig({
  ui: {
    accordion: {
      slots: {
        root: 'w-full',
        item: 'border-b border-default last:border-b-0',
        header: 'flex',
        trigger: 'group flex-1 flex items-center gap-1.5 font-medium text-sm py-3.5 focus-visible:outline-primary min-w-0',
        content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none',
        body: 'text-sm pb-3.5',
        leadingIcon: 'shrink-0 size-5',
        trailingIcon: 'shrink-0 size-5 ms-auto group-data-[state=open]:rotate-180 transition-transform duration-200',
        label: 'text-start break-words'
      },
      variants: {
        disabled: {
          true: {
            trigger: 'cursor-not-allowed opacity-75'
          }
        }
      }
    }
  }
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        accordion: {
          slots: {
            root: 'w-full',
            item: 'border-b border-default last:border-b-0',
            header: 'flex',
            trigger: 'group flex-1 flex items-center gap-1.5 font-medium text-sm py-3.5 focus-visible:outline-primary min-w-0',
            content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none',
            body: 'text-sm pb-3.5',
            leadingIcon: 'shrink-0 size-5',
            trailingIcon: 'shrink-0 size-5 ms-auto group-data-[state=open]:rotate-180 transition-transform duration-200',
            label: 'text-start break-words'
          },
          variants: {
            disabled: {
              true: {
                trigger: 'cursor-not-allowed opacity-75'
              }
            }
          }
        }
      }
    })
  ]
})