Accordion

AccordionGitLab
Um conjunto empilhado de painéis recolhíveis.

Uso

Use o componente Accordion para exibir uma lista de itens recolhíveis.

Yes! Nitro UI is completely free and open source under the MIT license. All 125+ components are available to everyone.
<script setup lang="ts">
import type { AccordionItem } from '@nitro/ui'

const items = ref<AccordionItem[]>([
  {
    label: 'Is Nitro UI free to use?',
    content: 'Yes! Nitro UI is completely free and open source under the MIT license. All 125+ components are available to everyone.'
  },
  {
    label: 'Can I use Nitro UI with Vue without Nuxt?',
    content: 'Yes! While optimized for Nuxt, Nitro 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 Nitro UI production-ready?',
    content: 'Yes! Nitro UI is used in production by thousands of applications with extensive tests, regular updates, and active maintenance.'
  }
])
</script>

<template>
  <NAccordion :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from '@nitro/ui'

const items = ref<AccordionItem[]>([
  {
    label: 'Is Nitro UI free to use?',
    content: 'Yes! Nitro UI is completely free and open source under the MIT license. All 125+ components are available to everyone.'
  },
  {
    label: 'Can I use Nitro UI with Vue without Nuxt?',
    content: 'Yes! While optimized for Nuxt, Nitro 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 Nitro UI production-ready?',
    content: 'Yes! Nitro UI is used in production by thousands of applications with extensive tests, regular updates, and active maintenance.'
  }
])
</script>

<template>
  <NAccordion :items="items" />
</template>

Itens

Use a prop items como um array de objetos com as seguintes propriedades:

  • 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 '@nitro/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>
  <NAccordion :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from '@nitro/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>
  <NAccordion :items="items" />
</template>

Múltiplo

Defina a prop type como multiple para permitir que vários itens fiquem ativos ao mesmo tempo. O padrão é single.

<script setup lang="ts">
import type { AccordionItem } from '@nitro/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>
  <NAccordion type="multiple" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from '@nitro/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>
  <NAccordion type="multiple" :items="items" />
</template>

Recolhível

Quando type é single, você pode definir a prop collapsible como false para evitar que o item ativo seja recolhido.

<script setup lang="ts">
import type { AccordionItem } from '@nitro/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>
  <NAccordion :collapsible="false" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from '@nitro/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>
  <NAccordion :collapsible="false" :items="items" />
</template>

Desmontar

Use a prop unmount-on-hide para evitar que o conteúdo seja desmontado quando o accordion é recolhido. O padrão é true.

<script setup lang="ts">
import type { AccordionItem } from '@nitro/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>
  <NAccordion :unmount-on-hide="false" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from '@nitro/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>
  <NAccordion :unmount-on-hide="false" :items="items" />
</template>
Você pode inspecionar o DOM para ver o conteúdo de cada item sendo renderizado.

Desabilitado

Use a propriedade disabled para desabilitar o Accordion.

Você também pode desabilitar um item específico usando a propriedade disabled no objeto do item.

<script setup lang="ts">
import type { AccordionItem } from '@nitro/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>
  <NAccordion disabled :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from '@nitro/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>
  <NAccordion disabled :items="items" />
</template>

Ícone à direita

Use a prop trailing-icon para personalizar o Icon à direita de cada item. O padrão é i-lucide-chevron-down.

Você também pode definir um ícone para um item específico usando a propriedade trailingIcon no objeto do item.
<script setup lang="ts">
import type { AccordionItem } from '@nitro/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>
  <NAccordion trailing-icon="i-lucide-arrow-down" :items="items" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from '@nitro/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>
  <NAccordion trailing-icon="i-lucide-arrow-down" :items="items" />
</template>
Você pode personalizar esse ícone globalmente no seu app.config.ts na chave ui.icons.chevronDown.
Você pode personalizar esse ícone globalmente no seu vite.config.ts na chave ui.icons.chevronDown.

Exemplos

Controlar o(s) item(ns) ativo(s)

Você pode controlar o item ativo usando a prop default-value ou a diretiva v-model com o value do item. Se nenhum value for fornecido, o padrão é o índice como string.

Você não precisa fazer nada, o @nuxt/icon cuida disso automaticamente.
Use a prop value-key para alterar a chave usada para corresponder os itens quando um v-model ou default-value é fornecido.
Quando type="multiple", certifique-se de passar um array para a prop default-value ou para a diretiva v-model.

Com arrastar e soltar

Use o composable useSortable de @vueuse/integrations para habilitar a funcionalidade de arrastar e soltar no Accordion. Essa integração envolve o Sortable.js para oferecer uma experiência de arrastar e soltar fluida.

Com slot de corpo

Use o slot #body para personalizar o corpo de cada item.

O slot #body inclui alguns estilos predefinidos; use o slot #content se você quiser começar do zero.

Com slot de conteúdo

Use o slot #content para personalizar o conteúdo de cada item.

Com slot personalizado

Use a propriedade slot para personalizar um item específico.

Você terá acesso aos seguintes slots:

  • #{{ item.slot }}
  • #{{ item.slot }}-body

Com conteúdo markdown

Você pode usar o componente MDC do @nuxtjs/mdc para renderizar markdown nos itens do accordion.

Through Reka UI integration, Nitro 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.

API

Props

Prop Default Type
as'div'any

The element or component this component should render as.

items T[]
trailingIconappConfig.ui.icons.chevronDownany

The icon displayed on the right side of the trigger.

labelKey'label' keyof Extract<NestedItem<T>, object> & string | DotPathKeys<Extract<NestedItem<T>, object>>

The key used to get the label from the item.

collapsibletrueboolean

When type is "single", allows closing content when clicking trigger for an open item. When type is "multiple", this prop has no effect.

defaultValue string | string[]

The default active value of the item(s).

Use when you do not need to control the state of the item(s).

modelValue string | string[]

The controlled value of the active item(s).

Use this when you need to control the state of the items. Can be binded with v-model

type'single' "single" | "multiple"

Determines whether a "single" or "multiple" items can be selected at a time.

This prop will overwrite the inferred type from modelValue and defaultValue.

disabledfalseboolean

When true, prevents the user from interacting with the accordion and all its items

unmountOnHidetrueboolean

When true, the element will be unmounted on closed state.

ui { root?: SlotClass; item?: SlotClass; header?: SlotClass; trigger?: SlotClass; content?: SlotClass; body?: SlotClass; leadingIcon?: SlotClass; trailingIcon?: SlotClass; label?: SlotClass; }

Slots

Slot Type
leading{ item: T; index: number; open: boolean; ui: object; }
default{ item: T; index: number; open: boolean; }
trailing{ item: T; index: number; open: boolean; ui: object; }
content{ item: T; index: number; open: boolean; ui: object; }
body{ item: T; index: number; open: boolean; ui: object; }

Emits

Event Type
update:modelValue[value: string | string[] | undefined]

Tema

app.config.ts
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'
          }
        }
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nitro/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'
              }
            }
          }
        }
      }
    })
  ]
})

Changelog

No recent changes