Usage
Use a Button or any other component in the default slot of the Slideover.
Then, use the #content
slot to add the content displayed when the Slideover is open.
<template>
<USlideover>
<UButton label="Open" color="neutral" variant="subtle" />
<template #content>
<Placeholder class="h-full m-4" />
</template>
</USlideover>
</template>
You can also use the #header
, #body
and #footer
slots to customize the Slideover's content.
Title
Use the title
prop to set the title of the Slideover's header.
<template>
<USlideover title="Slideover with title">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>
Description
Use the description
prop to set the description of the Slideover's header.
<template>
<USlideover
title="Slideover with description"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
>
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>
Close
Use the close
prop to customize or hide the close button (with false
value) displayed in the Slideover's header.
You can pass all the props of the Button component to customize it.
<template>
<USlideover
title="Slideover with close button"
:close="{
color: 'primary',
variant: 'outline',
class: 'rounded-full'
}"
>
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>
#content
slot is used as it's a part of the header.Close Icon
Use the close-icon
prop to customize the close button Icon. Defaults to i-lucide-x
.
<template>
<USlideover title="Slideover with close button" close-icon="i-lucide-arrow-right">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>
Side
Use the side
prop to set the side of the screen where the Slideover will slide in from. Defaults to right
.
<template>
<USlideover side="left" title="Slideover with side">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full min-h-48" />
</template>
</USlideover>
</template>
Overlay
Use the overlay
prop to control whether the Slideover has an overlay or not. Defaults to true
.
<template>
<USlideover :overlay="false" title="Slideover without overlay">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>
Transition
Use the transition
prop to control whether the Slideover is animated or not. Defaults to true
.
<template>
<USlideover :transition="false" title="Slideover without transition">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>
Prevent close
Use the prevent-close
prop to prevent the Slideover from being closed when clicking outside of it.
<template>
<USlideover prevent-close title="Slideover prevent close">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>
Examples
Control open state
You can control the open state by using the default-open
prop or the v-model:open
directive.
<script setup lang="ts">
const open = ref(false)
defineShortcuts({
o: () => open.value = !open.value
})
</script>
<template>
<USlideover v-model:open="open">
<UButton label="Open" color="neutral" variant="subtle" />
<template #content>
<Placeholder class="h-full m-4" />
</template>
</USlideover>
</template>
Programmatic usage
You can use the useSlideover
composable to open a Slideover programatically.
First, create a slideover component that will be opened programatically:
<script setup lang="ts">
const slideover = useSlideover()
defineProps<{
count: number
}>()
</script>
<template>
<USlideover :description="`This slideover was opened programmatically ${count} times`">
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer>
<UButton color="neutral" label="Close" @click="slideover.close()" />
</template>
</USlideover>
</template>
Then, use it in your app:
<script setup lang="ts">
import { LazySlideoverExample } from '#components'
const count = ref(0)
const slideover = useSlideover()
function open() {
count.value++
slideover.open(LazySlideoverExample, {
title: 'Slideover',
count: count.value
})
}
</script>
<template>
<UButton label="Open" color="neutral" variant="subtle" @click="open" />
</template>
slideover.close()
.Nested slideovers
You can nest slideovers within each other.
<script setup lang="ts">
const first = ref(false)
const second = ref(false)
</script>
<template>
<USlideover v-model:open="first" title="First slideover" :ui="{ footer: 'justify-end' }">
<UButton color="neutral" variant="subtle" label="Open" />
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer>
<UButton label="Close" color="neutral" variant="outline" @click="first = false" />
<USlideover v-model:open="second" title="Second slideover" :ui="{ footer: 'justify-end' }">
<UButton label="Open second" color="neutral" />
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer>
<UButton label="Close" color="neutral" variant="outline" @click="second = false" />
</template>
</USlideover>
</template>
</USlideover>
</template>
With footer slot
Use the #footer
slot to add content after the Slideover's body.
<script setup lang="ts">
const open = ref(false)
</script>
<template>
<USlideover v-model:open="open" title="Slideover with footer" description="This is useful when you want a form in a Slideover." :ui="{ footer: 'justify-end' }">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer>
<UButton label="Cancel" color="neutral" variant="outline" @click="open = false" />
<UButton label="Submit" color="neutral" />
</template>
</USlideover>
</template>
API
Props
Prop | Default | Type |
---|---|---|
title |
| |
description |
| |
content |
The content of the slideover. | |
overlay |
|
Render an overlay behind the slideover. |
transition |
|
Animate the slideover when opening or closing. |
side |
|
|
portal |
|
Render the slideover in a portal. |
close |
|
Display a close button to dismiss the slideover.
|
closeIcon |
|
The icon displayed in the close button. |
preventClose |
|
When |
open |
The controlled open state of the dialog. Can be binded as | |
defaultOpen |
The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. | |
modal |
|
The modality of the dialog When set to |
ui |
|
Slots
Slot | Type |
---|---|
default |
|
content |
|
header |
|
title |
|
description |
|
close |
|
body |
|
footer |
|
Emits
Event | Type |
---|---|
update:open |
|
Theme
export default defineAppConfig({
ui: {
slideover: {
slots: {
overlay: 'fixed inset-0 bg-[var(--ui-bg-elevated)]/75',
content: 'fixed bg-[var(--ui-bg)] divide-y divide-[var(--ui-border)] sm:ring ring-[var(--ui-border)] sm:shadow-lg flex flex-col focus:outline-none',
header: 'px-4 py-5 sm:px-6',
body: 'flex-1 overflow-y-auto p-4 sm:p-6',
footer: 'flex items-center gap-1.5 p-4 sm:px-6',
title: 'text-[var(--ui-text-highlighted)] font-semibold',
description: 'mt-1 text-[var(--ui-text-muted)] text-sm',
close: 'absolute top-4 end-4'
},
variants: {
side: {
top: {
content: 'inset-x-0 top-0 max-h-full'
},
right: {
content: 'right-0 inset-y-0 w-full max-w-md'
},
bottom: {
content: 'inset-x-0 bottom-0 max-h-full'
},
left: {
content: 'left-0 inset-y-0 w-full max-w-md'
}
},
transition: {
true: {
overlay: 'data-[state=open]:animate-[fade-in_200ms_ease-out] data-[state=closed]:animate-[fade-out_200ms_ease-in]'
}
}
},
compoundVariants: [
{
transition: true,
side: 'top',
class: {
content: 'data-[state=open]:animate-[slide-in-from-top_200ms_ease-in-out] data-[state=closed]:animate-[slide-out-to-top_200ms_ease-in-out]'
}
},
{
transition: true,
side: 'right',
class: {
content: 'data-[state=open]:animate-[slide-in-from-right_200ms_ease-in-out] data-[state=closed]:animate-[slide-out-to-right_200ms_ease-in-out]'
}
},
{
transition: true,
side: 'bottom',
class: {
content: 'data-[state=open]:animate-[slide-in-from-bottom_200ms_ease-in-out] data-[state=closed]:animate-[slide-out-to-bottom_200ms_ease-in-out]'
}
},
{
transition: true,
side: 'left',
class: {
content: 'data-[state=open]:animate-[slide-in-from-left_200ms_ease-in-out] data-[state=closed]:animate-[slide-out-to-left_200ms_ease-in-out]'
}
}
]
}
}
})