Appearance
Drawer
Shows content that needs more room than a dialog, in a full-height panel beside the page: a record to read or edit, a list's filters.
vue
<script setup lang="ts">
import { FsButton, FsDrawer, FsField, FsInput } from '@fivespark/ui'
import { ref } from 'vue'
const name = ref('Jansen Bouw B.V.')
const city = ref('Utrecht')
</script>
<template>
<div class="grid min-h-[28rem] place-items-center">
<FsDrawer title="Klant bewerken">
<template #trigger="{ props }">
<FsButton v-bind="props" label="Klant bewerken" />
</template>
<div class="flex flex-col gap-4">
<FsField label="Bedrijfsnaam"><FsInput v-model="name" autofocus /></FsField>
<FsField label="Plaats"><FsInput v-model="city" /></FsField>
</div>
<template #footer="{ close }">
<FsButton label="Annuleren" @click="close" />
<FsButton variant="primary" label="Opslaan" @click="close" />
</template>
</FsDrawer>
</div>
</template>Usage
vue
<script setup lang="ts">
import { FsButton, FsDrawer } from '@fivespark/ui'
</script>
<template>
<FsDrawer title="Filters">
<template #trigger="{ props }">
<FsButton v-bind="props" label="Filters" />
</template>
…
</FsDrawer>
</template>The drawer sits at the end of the page and closes only by Esc, its close button or its own buttons. For a question to answer or a short task, use a Dialog.
Examples
Filters
"Toepassen" applies and closes; "Wissen" clears and leaves the drawer open. Closing any other way should discard what was changed, which your page does by resetting the fields.
vue
<script setup lang="ts">
import { FsButton, FsDrawer, FsField, FsInput } from '@fivespark/ui'
import { ref, watch } from 'vue'
const open = ref(false)
const klant = ref('')
const toegepast = ref('')
watch(open, (isOpen) => {
if (!isOpen) klant.value = toegepast.value
})
</script>
<template>
<div class="grid min-h-[28rem] place-items-center">
<FsDrawer v-model:open="open" title="Filters">
<template #trigger="{ props }">
<FsButton v-bind="props" label="Filters" leading-icon="funnel" />
</template>
<FsField label="Klant"><FsInput v-model="klant" autofocus /></FsField>
<template #footer="{ close }">
<FsButton variant="ghost" label="Wissen" @click="klant = ''" />
<FsButton variant="primary" label="Toepassen" @click="toegepast = klant; close()" />
</template>
</FsDrawer>
</div>
</template>A record to read
Without a form, leave out autofocus and the footer. Focus starts on the close button.
vue
<script setup lang="ts">
import { FsButton, FsDescriptionList, FsDrawer } from '@fivespark/ui'
const items = [
{ label: 'Klant', content: 'Jansen Bouw B.V.' },
{ label: 'Totaal', content: '€ 4.180,00' },
{ label: 'Leverdatum', content: '12 december 2026' },
]
</script>
<template>
<div class="grid min-h-[28rem] place-items-center">
<FsDrawer title="Order 2026-0318" description="Geplaatst op 4 november 2026.">
<template #trigger="{ props }">
<FsButton v-bind="props" label="Order bekijken" />
</template>
<FsDescriptionList :items />
</FsDrawer>
</div>
</template>Controlled with v-model
Without a trigger, open it from anywhere with v-model:open.
vue
<script setup lang="ts">
import { FsButton, FsDrawer } from '@fivespark/ui'
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<div class="grid min-h-[28rem] place-items-center">
<FsButton label="Hulp bij plannen" @click="open = true" />
<FsDrawer v-model:open="open" title="Hulp bij plannen" description="Kies een order en een dag, en wijs er een monteur aan toe.">
<template #footer="{ close }">
<FsButton label="Sluiten" @click="close" />
</template>
</FsDrawer>
</div>
</template>A confirmation inside a drawer is a Dialog with alert, opened from the drawer. Place the drawer outside any <form>.
Keyboard
| Key | Does |
|---|---|
| Esc | Closes the drawer. A popover or select open inside it takes the key first |
| Tab, Shift + Tab | Moves between the controls inside; focus stays in the drawer |
| Enter, Space | On the trigger, opens the drawer; on the close button, closes it |
API Reference
FsDrawer
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | required | Shown at the top as a heading, and names the drawer. |
description | string | A sentence at the top of the body, read out with the name. | |
open | boolean | false | True while the drawer shows, however it was opened. Set it to open or close the drawer. |
| Event | Payload | Description |
|---|---|---|
update:open | [value: boolean] | Sent with the new `open`, for `v-model:open`. |
| Slot | Props | Description |
|---|---|---|
trigger | { props: Record<string, unknown>; } | The button that opens it: bind `props` on it, `<FsButton v-bind="props" label="Filters" />`. Focus returns to it on close. |
default | { close: () => void; } | The body, below the description. `close()` closes the drawer. |
footer | { close: () => void; } | The actions, at the bottom. `close()` closes the drawer, for a Cancel button. |