VNavigationDrawer
The VNavigationDrawer component is used to display navigation links in a sidebar. It supports multiple display modes including permanent, temporary, and rail variants, with optional touch gestures and expand-on-hover behavior.
Usage
<template>
<v-navigation-drawer v-model="drawer">
<v-list>
<v-list-item
v-for="item in items"
:key="item.title"
:to="item.to"
>
<template v-slot:prepend>
<v-icon>{{ item.icon }}</v-icon>
</template>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
</template>
<script setup>
import { ref } from 'vue'
const drawer = ref(true)
const items = [
{ title: 'Dashboard', icon: 'mdi-view-dashboard', to: '/dashboard' },
{ title: 'Account', icon: 'mdi-account', to: '/account' },
{ title: 'Settings', icon: 'mdi-cog', to: '/settings' },
]
</script>
Props
modelValue
boolean | null
default:"null"
Controls the visibility of the navigation drawer.<v-navigation-drawer v-model="drawer">
<!-- Content -->
</v-navigation-drawer>
location
'start' | 'end' | 'left' | 'right' | 'top' | 'bottom'
default:"'start'"
Sets the position of the navigation drawer.<v-navigation-drawer location="end">
<!-- Drawer on the right side -->
</v-navigation-drawer>
width
number | string
default:"256"
Sets the width of the navigation drawer in pixels.<v-navigation-drawer :width="300">
<!-- Content -->
</v-navigation-drawer>
rail
boolean | null
default:"null"
Condenses navigation drawer width to show only icons.<v-navigation-drawer rail>
<!-- Mini variant showing only icons -->
</v-navigation-drawer>
railWidth
number | string
default:"56"
Sets the width of the drawer when in rail mode.<v-navigation-drawer rail :rail-width="72">
<!-- Content -->
</v-navigation-drawer>
When in rail mode, expands the drawer on hover.<v-navigation-drawer rail expand-on-hover>
<!-- Expands on hover -->
</v-navigation-drawer>
Makes the drawer permanent (always visible, no overlay).<v-navigation-drawer permanent>
<!-- Always visible -->
</v-navigation-drawer>
Makes the drawer temporary (overlays content, shows scrim).<v-navigation-drawer temporary>
<!-- Temporary overlay -->
</v-navigation-drawer>
Keeps drawer open when clicking the scrim.<v-navigation-drawer temporary persistent>
<!-- Clicking scrim won't close -->
</v-navigation-drawer>
Makes the drawer float above the content without pushing it.<v-navigation-drawer floating>
<!-- Floating drawer -->
</v-navigation-drawer>
scrim
boolean | string
default:"true"
Controls the scrim overlay. Can be a boolean or color string.<!-- No scrim -->
<v-navigation-drawer :scrim="false">
<!-- Content -->
</v-navigation-drawer>
<!-- Custom scrim color -->
<v-navigation-drawer scrim="rgba(0,0,0,0.8)">
<!-- Content -->
</v-navigation-drawer>
Specifies a background image for the drawer.<v-navigation-drawer image="/path/to/image.jpg">
<!-- Content -->
</v-navigation-drawer>
Applies specified color to the drawer background.<v-navigation-drawer color="primary">
<!-- Content -->
</v-navigation-drawer>
Disables touch gestures for opening/closing the drawer.<v-navigation-drawer touchless>
<!-- No touch gestures -->
</v-navigation-drawer>
Disables the automatic opening/closing based on screen size.
Disables automatic closing when route changes.
Makes the drawer stick to the top of the viewport when scrolling.
Sets the elevation (box-shadow) of the drawer.<v-navigation-drawer elevation="8">
<!-- Content -->
</v-navigation-drawer>
border
boolean | string | number
Applies a border to the drawer.
rounded
boolean | string | number
Applies border radius to the drawer.
Specifies the custom tag to use for the root element.
Applies a specific theme to the drawer.
Overrides the mobile breakpoint detection.
mobileBreakpoint
number | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'
Sets the breakpoint at which the drawer becomes temporary.
Assigns a specific name for layout registration.
order
number | string
default:"0"
Adjusts the order in which the drawer appears in the layout.
Applies position: absolute to the drawer.
Traps focus within the drawer when open.
Delay (in ms) before opening when using expand-on-hover.
Delay (in ms) before closing when using expand-on-hover.
Events
Emitted when the drawer visibility changes.<v-navigation-drawer @update:modelValue="onDrawerChange">
<!-- Content -->
</v-navigation-drawer>
Emitted when the rail state changes (typically from expand-on-hover).<v-navigation-drawer
rail
expand-on-hover
@update:rail="onRailChange"
>
<!-- Content -->
</v-navigation-drawer>
Slots
The default slot for drawer content.
Slot for content at the top of the drawer (before default content).<v-navigation-drawer>
<template v-slot:prepend>
<v-list-item title="User Name" subtitle="[email protected]">
<template v-slot:prepend>
<v-avatar color="primary">
<v-icon>mdi-account</v-icon>
</v-avatar>
</template>
</v-list-item>
</template>
<v-list>
<!-- Navigation items -->
</v-list>
</v-navigation-drawer>
Slot for content at the bottom of the drawer (after default content).<v-navigation-drawer>
<v-list>
<!-- Navigation items -->
</v-list>
<template v-slot:append>
<div class="pa-2">
<v-btn block>
Logout
</v-btn>
</div>
</template>
</v-navigation-drawer>
Slot for custom background image content. Receives { image: string } as slot props.<v-navigation-drawer image="/bg.jpg">
<template v-slot:image="{ image }">
<v-img :src="image" gradient="to top, rgba(0,0,0,.7), rgba(0,0,0,.7)" />
</template>
</v-navigation-drawer>
Examples
Basic Drawer
<template>
<v-app>
<v-app-bar>
<v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
<v-toolbar-title>Application</v-toolbar-title>
</v-app-bar>
<v-navigation-drawer v-model="drawer">
<v-list>
<v-list-item
v-for="item in items"
:key="item.title"
:to="item.to"
>
<template v-slot:prepend>
<v-icon>{{ item.icon }}</v-icon>
</template>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-main>
<!-- Content -->
</v-main>
</v-app>
</template>
<script setup>
import { ref } from 'vue'
const drawer = ref(true)
const items = [
{ title: 'Home', icon: 'mdi-home', to: '/' },
{ title: 'About', icon: 'mdi-information', to: '/about' },
{ title: 'Contact', icon: 'mdi-email', to: '/contact' },
]
</script>
Permanent Drawer
<template>
<v-app>
<v-navigation-drawer permanent>
<v-list>
<v-list-item title="Dashboard" prepend-icon="mdi-view-dashboard"></v-list-item>
<v-list-item title="Settings" prepend-icon="mdi-cog"></v-list-item>
</v-list>
</v-navigation-drawer>
<v-main>
<!-- Content automatically adjusts -->
</v-main>
</v-app>
</template>
Rail Mode with Expand on Hover
<template>
<v-app>
<v-navigation-drawer rail expand-on-hover>
<v-list density="compact" nav>
<v-list-item
prepend-icon="mdi-home"
title="Home"
value="home"
></v-list-item>
<v-list-item
prepend-icon="mdi-account"
title="Account"
value="account"
></v-list-item>
<v-list-item
prepend-icon="mdi-account-group"
title="Users"
value="users"
></v-list-item>
</v-list>
</v-navigation-drawer>
<v-main>
<!-- Content -->
</v-main>
</v-app>
</template>
When using rail mode with expand-on-hover, the drawer shows only icons by default and expands to show text when hovering.
With Image Background
<template>
<v-navigation-drawer
image="https://picsum.photos/1920/1080?random"
theme="dark"
>
<template v-slot:prepend>
<v-list-item
title="John Doe"
subtitle="[email protected]"
>
<template v-slot:prepend>
<v-avatar color="primary">
<v-icon>mdi-account</v-icon>
</v-avatar>
</template>
</v-list-item>
</template>
<v-divider></v-divider>
<v-list density="compact" nav>
<v-list-item prepend-icon="mdi-home" title="Home"></v-list-item>
<v-list-item prepend-icon="mdi-email" title="Messages"></v-list-item>
<v-list-item prepend-icon="mdi-cog" title="Settings"></v-list-item>
</v-list>
</v-navigation-drawer>
</template>
Right-Side Drawer
<template>
<v-app>
<v-app-bar>
<v-toolbar-title>Application</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon @click="drawer = !drawer">
<v-icon>mdi-menu</v-icon>
</v-btn>
</v-app-bar>
<v-navigation-drawer v-model="drawer" location="end" temporary>
<v-list>
<v-list-item title="Settings" prepend-icon="mdi-cog"></v-list-item>
<v-list-item title="Help" prepend-icon="mdi-help-circle"></v-list-item>
</v-list>
</v-navigation-drawer>
<v-main>
<!-- Content -->
</v-main>
</v-app>
</template>
<script setup>
import { ref } from 'vue'
const drawer = ref(false)
</script>
With Prepend and Append
<template>
<v-navigation-drawer>
<template v-slot:prepend>
<v-list-item
title="My Application"
subtitle="v1.0.0"
>
<template v-slot:prepend>
<v-avatar color="primary">
<v-icon>mdi-application</v-icon>
</v-avatar>
</template>
</v-list-item>
</template>
<v-divider></v-divider>
<v-list density="compact" nav>
<v-list-item prepend-icon="mdi-view-dashboard" title="Dashboard"></v-list-item>
<v-list-item prepend-icon="mdi-account-multiple" title="Users"></v-list-item>
<v-list-item prepend-icon="mdi-chart-line" title="Analytics"></v-list-item>
</v-list>
<template v-slot:append>
<div class="pa-2">
<v-btn block color="primary">
Logout
</v-btn>
</div>
</template>
</v-navigation-drawer>
</template>
By default, temporary drawers close when the route changes. Use disable-route-watcher to prevent this behavior.
Behavior Modes
Temporary
- Overlays content
- Shows scrim (overlay)
- Automatically becomes temporary on mobile devices
- Closes on route change (unless disabled)
Permanent
- Always visible
- No scrim
- Content adjusts to make space
- Does not close on route change
Persistent
- Similar to temporary
- Doesn’t close when clicking scrim
- Must be closed explicitly