Usage
Use theUCollapsible component to create collapsible content with a custom trigger.
<script setup>
const isOpen = ref(false)
</script>
<template>
<UCollapsible v-model:open="isOpen">
<template #default="{ open }">
<button>{{ open ? 'Hide' : 'Show' }} Content</button>
</template>
<template #content>
<div>This is the collapsible content</div>
</template>
</UCollapsible>
</template>
Props
as
- Type:
any - Default:
'div'
defaultOpen
- Type:
boolean
<UCollapsible default-open>
<template #default>
<button>Toggle</button>
</template>
<template #content>
<div>This starts open</div>
</template>
</UCollapsible>
open
- Type:
boolean
<script setup>
const isOpen = ref(true)
</script>
<template>
<UCollapsible v-model:open="isOpen">
<!-- content -->
</UCollapsible>
</template>
disabled
- Type:
boolean
<UCollapsible disabled>
<template #default>
<button>Can't Toggle</button>
</template>
<template #content>
<div>Content</div>
</template>
</UCollapsible>
unmountOnHide
- Type:
boolean - Default:
true
class
- Type:
any
ui
- Type:
object
root- The root containercontent- The collapsible content wrapper
Events
Inherits all events from Reka UI’sCollapsibleRoot.
Slots
default
- Props:
{ open: boolean }
<UCollapsible>
<template #default="{ open }">
<button class="flex items-center gap-2">
<span>Toggle Content</span>
<div :class="open ? 'rotate-180' : ''" class="transition-transform">
↓
</div>
</button>
</template>
<template #content>
<div>Content here</div>
</template>
</UCollapsible>
content
The collapsible content.<UCollapsible>
<template #default>
<button>Show Details</button>
</template>
<template #content>
<div class="p-4 bg-elevated">
Detailed information here
</div>
</template>
</UCollapsible>
Examples
Basic Collapsible
<template>
<UCollapsible>
<template #default="{ open }">
<button class="font-medium">
{{ open ? 'Hide' : 'Show' }} Details
</button>
</template>
<template #content>
<div class="mt-2 p-4 bg-elevated rounded">
This is the detailed content that can be shown or hidden.
</div>
</template>
</UCollapsible>
</template>
With Icon
<template>
<UCollapsible>
<template #default="{ open }">
<button class="flex items-center gap-2">
<span>More Information</span>
<div
:class="open ? 'rotate-180' : 'rotate-0'"
class="transition-transform duration-200"
>
<div class="i-heroicons-chevron-down size-5" />
</div>
</button>
</template>
<template #content>
<p class="mt-2">Additional information appears here.</p>
</template>
</UCollapsible>
</template>
Controlled State
<script setup>
const isExpanded = ref(false)
function toggle() {
isExpanded.value = !isExpanded.value
}
</script>
<template>
<div>
<button @click="toggle" class="mb-2">
External Toggle
</button>
<UCollapsible v-model:open="isExpanded">
<template #default="{ open }">
<button>{{ open ? 'Collapse' : 'Expand' }}</button>
</template>
<template #content>
<div>Controlled content</div>
</template>
</UCollapsible>
</div>
</template>
Details Section
<template>
<div class="border border-default rounded-lg p-4">
<UCollapsible>
<template #default="{ open }">
<button class="flex justify-between items-center w-full text-left">
<span class="font-semibold">Product Specifications</span>
<div :class="open ? 'rotate-180' : ''" class="transition-transform">
<div class="i-heroicons-chevron-down size-5" />
</div>
</button>
</template>
<template #content>
<div class="mt-4 space-y-2 text-sm text-muted">
<div><strong>Weight:</strong> 1.5 kg</div>
<div><strong>Dimensions:</strong> 30 x 20 x 10 cm</div>
<div><strong>Material:</strong> Premium plastic</div>
<div><strong>Color:</strong> Black</div>
</div>
</template>
</UCollapsible>
</div>
</template>
Multiple Collapsibles
<script setup>
const sections = ref([
{ id: 1, title: 'Section 1', open: false, content: 'Content for section 1' },
{ id: 2, title: 'Section 2', open: false, content: 'Content for section 2' },
{ id: 3, title: 'Section 3', open: false, content: 'Content for section 3' }
])
</script>
<template>
<div class="space-y-2">
<UCollapsible
v-for="section in sections"
:key="section.id"
v-model:open="section.open"
>
<template #default="{ open }">
<button class="w-full text-left p-3 bg-elevated rounded">
<div class="flex justify-between items-center">
<span>{{ section.title }}</span>
<div :class="open ? 'rotate-180' : ''" class="transition-transform">
↓
</div>
</div>
</button>
</template>
<template #content>
<div class="p-3">
{{ section.content }}
</div>
</template>
</UCollapsible>
</div>
</template>
Read More
<script setup>
const showMore = ref(false)
const shortText = 'This is a preview of the content...'
const fullText = 'This is a preview of the content. When you click "Read More", you will see the full text with all the details and additional information that was initially hidden.'
</script>
<template>
<div>
<p>{{ showMore ? fullText : shortText }}</p>
<UCollapsible v-model:open="showMore">
<template #default="{ open }">
<button class="text-primary mt-2">
{{ open ? 'Read Less' : 'Read More' }}
</button>
</template>
<template #content>
<!-- Content shown inline above -->
</template>
</UCollapsible>
</div>
</template>
Filter Panel
<script setup>
const filtersOpen = ref(true)
</script>
<template>
<div class="border border-default rounded-lg">
<UCollapsible v-model:open="filtersOpen">
<template #default="{ open }">
<button class="w-full p-4 flex justify-between items-center">
<span class="font-semibold">Filters</span>
<div class="i-heroicons-chevron-down size-5" :class="open ? 'rotate-180' : ''" />
</button>
</template>
<template #content>
<div class="p-4 border-t border-default space-y-4">
<div>
<label class="block text-sm font-medium mb-2">Price Range</label>
<!-- Price range inputs -->
</div>
<div>
<label class="block text-sm font-medium mb-2">Category</label>
<!-- Category checkboxes -->
</div>
</div>
</template>
</UCollapsible>
</div>
</template>