Getting Started
This guide will walk you through setting up @drawerly/vue in your Vue 3 application and creating your first drawer.
Installation
Install @drawerly/vue with your favorite package manager:
pnpm add @drawerly/vuenpm install @drawerly/vueyarn add @drawerly/vueINFO
@drawerly/vue automatically installs @drawerly/core as a dependency, so you don't need to install it separately.
Plugin Setup
Register the DrawerPlugin in your Vue application:
import { DrawerPlugin } from '@drawerly/vue'
import { createApp } from 'vue'
import App from './App.vue'
import '@drawerly/vue/style.css'
const app = createApp(App)
app.use(DrawerPlugin, {
// Optional configuration
defaultOptions: {
placement: 'right',
closeOnEscapeKey: true,
closeOnBackdropClick: true,
},
teleportTo: 'body',
headless: false,
})
app.mount('#app')Plugin Options
defaultOptions – Global defaults applied to all drawers:
placement: Default drawer position ('right','left','top','bottom')closeOnEscapeKey: Whether Escape key closes drawers (default:true)closeOnBackdropClick: Whether clicking backdrop closes drawers (default:true)- Any custom options you define
teleportTo – CSS selector or element where drawers are rendered (default: 'body')
headless – Disable built-in UI and animations (default: false)
Add the Container
Include the <DrawerlyContainer /> component in your root template:
<script setup lang="ts">
// Your app logic
</script>
<template>
<div id="app">
<YourAppContent />
<!-- Add the drawer container -->
<DrawerlyContainer />
</div>
</template>The container component:
- Renders all open drawers from the stack
- Handles animations and transitions
- Manages keyboard and backdrop interactions
- Teleports to the target element (default:
body)
TIP
You typically only need one <DrawerlyContainer /> per application. Place it in your root component (App.vue).
Creating Your First Drawer
Step 1: Create a Drawer Component
Create a Vue component that will be rendered inside the drawer:
<script setup lang="ts">
defineProps<{
userId: string
drawerKey: string // Automatically injected
onClose: () => void // Automatically injected
}>()
const userName = 'John Doe'
const userEmail = 'john@example.com'
</script>
<template>
<div class="user-profile">
<header>
<h2>User Profile</h2>
<button @click="onClose">✕</button>
</header>
<div class="content">
<p><strong>ID:</strong> {{ userId }}</p>
<p><strong>Name:</strong> {{ userName }}</p>
<p><strong>Email:</strong> {{ userEmail }}</p>
</div>
</div>
</template>
<style scoped>
.user-profile {
height: 100%;
display: flex;
flex-direction: column;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
border-bottom: 1px solid #e5e7eb;
}
.content {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
}
button {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #6b7280;
}
</style>Step 2: Open the Drawer
Use the useDrawerContext composable to open your drawer:
<script setup lang="ts">
import { useDrawerContext } from '@drawerly/vue'
import UserProfile from './UserProfile.vue'
const { open } = useDrawerContext()
function showUserProfile(userId: string) {
open({
drawerKey: `user-${userId}`,
component: UserProfile,
componentParams: {
userId,
},
ariaLabel: 'User Profile Drawer',
})
}
</script>
<template>
<div>
<h1>Home Page</h1>
<button @click="showUserProfile('123')">
View User Profile
</button>
</div>
</template>That's it! When you click the button, the drawer will slide in from the right with your UserProfile component rendered inside.
Accessing the Manager Globally
The drawer manager is also available via this.$drawerly in Options API components:
<script>
import SettingsPanel from './SettingsPanel.vue'
export default {
methods: {
openSettings() {
this.$drawerly.open({
drawerKey: 'settings',
component: SettingsPanel,
})
},
},
}
</script>Passing Data to Drawers
Use componentParams to pass props to your drawer component:
open({
drawerKey: 'edit-product',
component: ProductEditor,
componentParams: {
productId: '123',
mode: 'edit',
onSave: (data) => {
console.log('Product saved:', data)
},
onCancel: () => {
close('edit-product')
},
},
})Your drawer component receives these props plus the injected ones:
<script setup lang="ts">
defineProps<{
// Your custom props
productId: string
mode: 'create' | 'edit'
onSave?: (data: any) => void
onCancel?: () => void
// Injected by the container
drawerKey: string
onClose: () => void
}>()
</script>Styling Your Drawers
The package includes default styles that you can customize with CSS variables:
/* Custom drawer styles */
[data-drawerly-root] {
--drawerly-panel-bg: #1f2937;
--drawerly-panel-width: 500px;
--drawerly-backdrop-bg: rgba(0, 0, 0, 0.7);
--drawerly-transition-duration: 250ms;
}See the Styling Guide for detailed customization options.
Multiple Drawers
You can open multiple drawers, and they'll stack on top of each other:
// Open first drawer
open({
drawerKey: 'main-menu',
component: MainMenu,
})
// Open second drawer on top
open({
drawerKey: 'settings',
component: Settings,
})
// Open third drawer on top
open({
drawerKey: 'profile',
component: Profile,
})
// Stack: ['main-menu', 'settings', 'profile']
// 'profile' is visible on topClose the topmost drawer:
close() // Closes 'profile'Or close a specific drawer by key:
close('settings') // Removes 'settings' from stackReactive Drawer State
Use useDrawerInstance to create reactive bindings to a specific drawer:
<script setup lang="ts">
import { useDrawerInstance } from '@drawerly/vue'
const { isOpen, placement, close } = useDrawerInstance('my-drawer')
// isOpen automatically updates when drawer opens/closes
// placement can be read and written reactively
</script>
<template>
<div v-if="isOpen">
Drawer is open at {{ placement }}
<button @click="close">Close</button>
</div>
</template>TypeScript Support
Define custom drawer options with full type safety:
import type { VueDrawerOptions } from '@drawerly/vue'
interface ProductDrawerOptions extends VueDrawerOptions {
productId: string
productName: string
price: number
}
const { open } = useDrawerContext<ProductDrawerOptions>()
// TypeScript ensures all fields are provided
open({
drawerKey: 'product-123',
component: ProductDetail,
productId: '123', // Required
productName: 'Laptop', // Required
price: 999.99, // Required
})Next Steps
Now that you have the basics working, explore more advanced features:
- Headless Mode – Build custom UI without default styles
- Styling – Customize appearance with CSS variables
- useDrawerContext Composable – Detailed API for managing drawers
- useDrawerInstance Composable – Reactive bindings to specific drawers
- API Reference – Complete API documentation
Common Patterns
Close from Inside the Drawer
You can use the injected onClose prop:
<script setup lang="ts">
const { onClose } = defineProps<{
onClose: () => void
}>()
function handleSave() {
// Save data...
onClose() // Close the drawer
}
</script>
<template>
<button @click="handleSave">Save & Close</button>
</template>Or emit a close event:
<script setup lang="ts">
const emit = defineEmits<{
close: []
}>()
function handleSave() {
// Save data...
emit('close') // Close the drawer
}
</script>
<template>
<button @click="handleSave">Save & Close</button>
</template>Conditional Close Behavior
open({
drawerKey: 'important-form',
component: ImportantForm,
closeOnEscapeKey: false, // Prevent accidental close
closeOnBackdropClick: false,
})Drawer with Loading State
<script setup lang="ts">
import { ref, onMounted } from 'vue'
defineProps<{
userId: string
onClose: () => void
}>()
const loading = ref(true)
const userData = ref(null)
onMounted(async () => {
userData.value = await fetchUser()
loading.value = false
})
</script>
<template>
<div v-if="loading">Loading...</div>
<div v-else>{{ userData }}</div>
</template>