useStack
Overlay z-index coordinator with automatic stacking order and parent-child nesting support.
Installation
Install the Stack plugin in your app’s entry point:
import { createApp } from 'vue'
import { createStackPlugin } from '@vuetify/v0'
import App from './App.vue'
const app = createApp(App)
app.use(createStackPlugin())
app.mount('#app') For client-side only apps, you can skip plugin installation and use the default stack singleton directly. The plugin is required for SSR to ensure each request gets its own stack instance.
Usage
Use the useStack composable to register an overlay and receive its z-index and position in the stack:
import { shallowRef, watch } from 'vue'
import { useStack } from '@vuetify/v0'
const isOpen = shallowRef(false)
const stack = useStack()
const ticket = stack.register({
onDismiss: () => { isOpen.value = false }
})
// Activate when opening, deactivate when closing
watch(isOpen, open => {
if (open) ticket.select()
else ticket.unselect()
})
// ticket.zIndex.value = 2000 when first overlay
// ticket.zIndex.value = 2010 when second overlay
// ticket.globalTop.value = true when this is the topmost overlayContext / DI
Use createStackContext when you need a separate z-index namespace (e.g., overlays inside a modal):
import { createStackContext } from '@vuetify/v0'
const [useModalStack, provideModalStack, modalStack] =
createStackContext({ namespace: 'my:modal-stack', baseZIndex: 3000 })
// In parent component
provideModalStack()
// In child overlay component
const stack = useModalStack()
const ticket = stack.register({ id: 'tooltip-1' })
ticket.zIndex.value // z-index for this overlayArchitecture
createStack extends createRegistry with z-index management and scrim coordination:
Reactivity
Stack state and ticket properties are reactive for automatic UI updates.
| Property | Reactive | Notes |
|---|---|---|
isActive | Any overlays selected | |
top | Topmost overlay ticket | |
scrimZIndex | Z-index for scrim element | |
isBlocking | Top overlay blocks dismissal | |
topElement | Element of the topmost open modal (<dialog>), or null; consumed by Portal/Snackbar.Portal via teleport="top-layer" | |
ticket zIndex | Computed from selection order | |
ticket globalTop | True if topmost | |
ticket isSelected | Overlay active state |
Examples
Recipes
Top-Layer Teleport
Pass el when registering a modal overlay so useStack().topElement resolves to its DOM element. Portal and Snackbar.Portal read topElement when teleport="top-layer" (the Snackbar.Portal default), teleporting overlays into the topmost open modal’s subtree so they share its top-layer context and stay interactive:
const stack = useStack()
const ticket = stack.register({
el: () => dialogRef.value?.element,
onDismiss: () => { isOpen.value = false },
})Dialog and AlertDialog pass their <dialog> element automatically — this pattern is only needed when building a custom modal component from scratch.
Scrim Integration
Use the Scrim component alongside useStack to provide a backdrop for your overlays. The Scrim automatically positions itself below the topmost overlay:
<script setup lang="ts">
import { Scrim } from '@vuetify/v0'
</script>
<template>
<Scrim class="fixed inset-0 bg-black/50" />
</template>The Scrim reads from the same stack context, so its z-index is always coordinated with your registered overlays.
FAQ
Not for client-only apps — you can use the default stack singleton directly. Install createStackPlugin for SSR, where it ensures each request gets its own isolated stack instance instead of sharing one across requests.
Use createStackContext({ namespace, baseZIndex }) to create a separate stacking namespace — e.g. overlays opened inside a modal — then provide it and register tickets against that context instead of the global stack.
Register it with blocking: true. The overlay stays topmost, but the stack’s isBlocking flag tells the Scrim to ignore pointer events, so clicking the backdrop won’t dismiss it.
It assigns them from selection order: the first activated overlay gets the base z-index (2000), and each one stacked on top steps up from there (2010, …). Pass baseZIndex to createStackContext to shift the starting point for a separate namespace.