Overlays
Prerequisite: All overlays require <UApp> wrapper in app root.
Important: v4.4.0 includes security fixes for XSS vulnerabilities in Banner and CommandPalette components. Upgrade recommended.
Toast (Notifications)
Basic Usage
<script setup>
const toast = useToast()
function showToast() {
toast.add({
title: 'Success!',
description: 'Your changes have been saved.',
color: 'success',
icon: 'i-heroicons-check-circle'
})
}
</script>
<template>
<UButton @click="showToast">Show Toast</UButton>
</template>Toast Options
toast.add({
id: 'unique-id', // Custom ID (auto-generated if omitted)
title: 'Title', // Toast title
description: 'Details', // Toast body
color: 'success', // primary, success, error, warning, info
icon: 'i-heroicons-check', // Left icon
avatar: { src: '...' }, // Avatar instead of icon
timeout: 5000, // Auto-dismiss (0 = never)
actions: [{ // Action buttons
label: 'Undo',
click: () => {}
}],
callback: () => {} // Called on dismiss
})Toast Types
// Success
toast.add({ title: 'Saved', color: 'success', icon: 'i-heroicons-check-circle' })
// Error
toast.add({ title: 'Error', color: 'error', icon: 'i-heroicons-x-circle' })
// Warning
toast.add({ title: 'Warning', color: 'warning', icon: 'i-heroicons-exclamation-triangle' })
// Info
toast.add({ title: 'Info', color: 'info', icon: 'i-heroicons-information-circle' })
// Remove toast
toast.remove('toast-id')
// Clear all
toast.clear()Modal
Basic Modal
<script setup>
const isOpen = ref(false)
</script>
<template>
<UButton @click="isOpen = true">Open Modal</UButton>
<UModal v-model:open="isOpen">
<template #header>
<h3>Modal Title</h3>
</template>
<p>Modal content goes here...</p>
<template #footer>
<UButton variant="ghost" @click="isOpen = false">Cancel</UButton>
<UButton @click="save">Save</UButton>
</template>
</UModal>
</template>Modal Props
<UModal
v-model:open="isOpen"
title="Modal Title" <!-- Alternative to #header slot -->
description="Subtitle" <!-- Below title -->
:close="true" <!-- Show close button -->
:close-icon="'i-heroicons-x-mark'"
:overlay="true" <!-- Show backdrop -->
:transition="true" <!-- Enable animation -->
:prevent-close="false" <!-- Prevent close on overlay click -->
fullscreen <!-- Full screen mode -->
>Programmatic Modal (useOverlay)
<script setup>
const overlay = useOverlay()
async function openConfirm() {
const modal = overlay.create(ConfirmModal, {
props: { title: 'Confirm action?' },
events: {
confirm: () => modal.close(true),
cancel: () => modal.close(false)
}
})
const result = await modal.result
if (result) {
// User confirmed
}
}
</script>Slideover
Side panel overlay (from edge of screen).
<script setup>
const isOpen = ref(false)
</script>
<template>
<UButton @click="isOpen = true">Open Slideover</UButton>
<USlideover v-model:open="isOpen" title="Settings" side="right">
<div class="p-4">
Settings content...
</div>
</USlideover>
</template>Slideover Props
<USlideover
v-model:open="isOpen"
title="Title"
description="Subtitle"
side="right" <!-- left, right, top, bottom -->
:overlay="true"
:transition="true"
:prevent-close="false"
>Drawer
Bottom sheet overlay (vaul-vue).
<script setup>
const isOpen = ref(false)
</script>
<template>
<UButton @click="isOpen = true">Open Drawer</UButton>
<UDrawer v-model:open="isOpen">
<div class="p-4">
Drawer content...
</div>
</UDrawer>
</template>Drawer Props
<UDrawer
v-model:open="isOpen"
title="Drawer Title"
description="Subtitle"
handle <!-- Show drag handle -->
:should-scale-background="true"
:close-threshold="0.25" <!-- Swipe threshold to close -->
>Popover
<UPopover>
<UButton>Open Popover</UButton>
<template #content>
<div class="p-4">
Popover content
</div>
</template>
</UPopover>Popover Props
<UPopover
:open="isOpen"
side="bottom" <!-- top, right, bottom, left -->
align="center" <!-- start, center, end -->
:arrow="true"
:delay="{ open: 0, close: 0 }"
>Tooltip
<UTooltip text="Helpful tip">
<UButton icon="i-heroicons-question-mark-circle" />
</UTooltip>
<!-- With slot content -->
<UTooltip>
<UButton>Hover me</UButton>
<template #content>
<p>Rich tooltip content</p>
</template>
</UTooltip>DropdownMenu
<script setup>
const items = [
{ label: 'Edit', icon: 'i-heroicons-pencil', click: () => {} },
{ label: 'Duplicate', icon: 'i-heroicons-document-duplicate' },
{ type: 'separator' },
{ label: 'Delete', icon: 'i-heroicons-trash', color: 'error' }
]
</script>
<template>
<UDropdownMenu :items="items">
<UButton icon="i-heroicons-ellipsis-vertical" variant="ghost" />
</UDropdownMenu>
</template>Nested Items
<script setup>
const items = [
{ label: 'New', children: [
{ label: 'File', click: () => {} },
{ label: 'Folder', click: () => {} }
]},
{ label: 'Delete' }
]
</script>ContextMenu
Right-click menu.
<UContextMenu :items="items">
<div class="h-40 border rounded flex items-center justify-center">
Right-click here
</div>
</UContextMenu>CommandPalette
Search-driven command menu (Fuse.js powered).
<script setup>
const isOpen = ref(false)
const groups = [{
key: 'actions',
label: 'Actions',
items: [
{ label: 'New file', icon: 'i-heroicons-document-plus', click: () => {} },
{ label: 'New folder', icon: 'i-heroicons-folder-plus', click: () => {} }
]
}, {
key: 'navigation',
label: 'Navigation',
items: [
{ label: 'Home', to: '/' },
{ label: 'Settings', to: '/settings' }
]
}]
</script>
<template>
<UCommandPalette
v-model:open="isOpen"
:groups="groups"
placeholder="Search..."
size="md"
/>
</template>CommandPalette Props (v4.4+)
<UCommandPalette
v-model:open="isOpen"
:groups="groups"
placeholder="Search..."
size="md" <!-- Size: sm, md, lg (v4.4+) -->
:input="{ /* props */ }" <!-- Custom input props (v4.4+) -->
/>Keyboard Shortcut
<script setup>
defineShortcuts({
meta_k: () => { isOpen.value = true }
})
</script>Best Practices
| Do | Don't |
|---|---|
| Use useToast for notifications | Build custom toast systems |
| Use UModal for dialogs | Use alerts for complex UI |
| Use Slideover for panels | Use modals for side content |
| Use Drawer for mobile sheets | Use slideover on mobile |
| Use CommandPalette for search | Build custom search UI |
| Use programmatic overlays for confirms | Create confirm components |