Skip to content

Drawer Component

The Drawer component is a versatile sliding panel component built with Vue 3 and Headless UI. It provides a customizable slide-in interface perfect for forms, details panels, or any content that needs to appear from the side of the screen.

Basic Usage

Import and use Drawer in your Vue components:

vue
<template>
  <Drawer
    title="Example Drawer"
    :show="isOpen"
    @drawer:close="isOpen = false"
  >
    <p>Drawer content goes here</p>
  </Drawer>
</template>

<script setup>
import { ref } from 'vue';
import { Drawer } from '@robuust-digital/vue-components';

const isOpen = ref(false);
</script>

Props

PropTypeDefaultDescription
asString'form'Element type to render (form or div)
idStringRequiredUnique identifier for the drawer
titleStringRequiredTitle displayed in the drawer header
showCloseBooleanfalseShows close button in header when true
scrolledDownBooleanfalseReverses content/footer order when scrolled
headerStickyBooleanfalseMakes the header stick to top when scrolling
spinningBooleanfalseShows loading state in submit button
submitLabelString'Save'Text for the submit button
cancelLabelString'Cancel'Text for the cancel button
panelClassString'max-w-xl'CSS class for drawer panel width

Events

EventArgumentsDescription
drawer:open-Emitted when drawer starts opening
drawer:close-Emitted when drawer close is requested
drawer:saveeventEmitted when form is submitted (if as="form")
drawer:closed-Emitted when Transition finishes

Slots

Default Slot

The main content area of the drawer.

vue
<template>
  <Drawer title="Example">
    <p>Main content here</p>
  </Drawer>
</template>

Header Slot

Customize the header section:

vue
<template>
  <Drawer title="Example">
    <template #header="{ title, showClose }">
      <div class="custom-header">
        <h2>{{ title }}</h2>
        <button v-if="showClose" @click="$emit('drawer:close')">Close</button>
      </div>
    </template>
  </Drawer>
</template>

Customize the footer actions:

vue
<template>
  <Drawer title="Example">
    <template #footer="{ loading }">
      <ButtonBase label="Custom Cancel" @click="$emit('drawer:close')" />
      <ButtonBase 
        type="submit"
        label="Custom Save"
        :spinning="loading"
      />
    </template>
  </Drawer>
</template>

Examples

Basic Form Drawer

vue
<template>
  <ButtonBase label="Open Drawer" @click="isOpen = true" />
  
  <Drawer
    title="Create User"
    :show="isOpen"
    @drawer:close="isOpen = false"
    @drawer:save="handleSave"
  >
    <div class="space-y-4">
      <input v-model="formData.name" type="text" placeholder="Name" />
      <input v-model="formData.email" type="email" placeholder="Email" />
    </div>
  </Drawer>
</template>

<script setup>
import { ref } from 'vue';

const isOpen = ref(false);
const formData = ref({
  name: '',
  email: '',
});

const handleSave = (e) => {
  console.log('Form submitted:', formData.value);
  isOpen.value = false;
};
</script>

Custom Width Drawer

vue
<template>
  <Drawer
    title="Wide Drawer"
    panel-class="max-w-2xl"
    :show="isOpen"
    @drawer:close="isOpen = false"
  >
    <p>This drawer uses a custom width class.</p>
  </Drawer>
</template>

Loading State

vue
<template>
  <Drawer
    title="Saving..."
    :show="isOpen"
    :spinning="isSaving"
    @drawer:close="isOpen = false"
    @drawer:save="handleSave"
  >
    <p>The footer submit button will show a spinner when spinning is true.</p>
  </Drawer>
</template>

Customization with Tailwind CSS

To customize the drawer styles globally, extend your Tailwind configuration. Example:

javascript
// tailwind.config.js
export default {
  theme: {
    extend: {
      components: (theme) => ({
        drawer: {
          // Available variables you can override:
          '--rvc-drawer-backdrop-bg-color': 'rgba(15, 23, 42, 0.5)',
          '--rvc-drawer-padding-x': theme('padding.4'),
          '--rvc-drawer-padding-y': theme('padding.4'),
          '--rvc-drawer-border-color': theme('colors.slate.200'),
          '--rvc-drawer-border-width': theme('borderWidth.DEFAULT'),
          '--rvc-drawer-border-style': 'solid',
          '--rvc-drawer-header-bg-color': theme('colors.white'),
          '--rvc-drawer-title-font-size': theme('fontSize.2xl'),
          '--rvc-drawer-title-font-weight': theme('fontWeight.bold'),
          '--rvc-drawer-title-color': theme('colors.slate.900'),
          '--rvc-drawer-close-size': theme('width.6'),
          '--rvc-drawer-close-color': theme('colors.slate.700'),
          '--rvc-drawer-close-color-hover': theme('colors.slate.900'),
          '--rvc-drawer-content-bg-color': theme('colors.white'),
          '--rvc-drawer-footer-bg-color': theme('colors.slate.50'),
          '--rvc-drawer-footer-gap': theme('gap.3'),
          '--rvc-drawer-footer-justify-content': 'space-between',

          // From 'sm' breakpoint and up
          'screen-sm': {
            '--rvc-drawer-padding-x': theme('padding.12'),
            '--rvc-drawer-padding-y': theme('padding.12'),
          },

          // You can also customize elements with CSS
          '.drawer-header': {

            // Apply Tailwind classes
            '@apply bg-white': {},

            // or just CSS
            backgroundColor: theme('colors.white'),
          },

          // ...
        },
      }),
    },
  },
};