useStack
A composable for managing overlay z-index stacking with automatic calculation, scrim integration, and parent/child tracking for nested overlays.
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.
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.
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 overlayArchitecture
createStack extends createRegistry with z-index management and scrim coordination:
Examples
Overlay Stack
This example demonstrates overlay stacking with createStack. Each overlay gets an automatically calculated z-index, and the scrim appears below the topmost overlay.
File breakdown:
| File | Role |
|---|---|
context.ts | Defines overlay context with open/close methods |
StackProvider.vue | Provides stack context and renders scrim |
StackConsumer.vue | Displays buttons to open overlays at different stack levels |
overlays.vue | Entry point that composes Provider around Consumer |
Key patterns:
stack.register({ onDismiss })creates a ticket for the overlayticket.select()/ticket.unselect()activates/deactivates the overlayticket.globalTopdetermines if this overlay should handle escape keyticket.zIndexprovides the z-index valueScrim reads from the stack context to position below the top overlay
Click a button to open an overlay. Open multiple overlays to observe z-index layering.
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 | |
ticket zIndex | Computed from selection order | |
ticket globalTop | True if topmost | |
ticket isSelected | Overlay active state |
Functions
createStack
(_options?: StackOptions) => RCreates a new stack instance for managing overlay z-indexes.
createStackContext
(_options?: StackContextOptions) => ContextTrinity<R>Creates a stack context trinity for provide/inject usage.
createStackPlugin
(_options?: StackPluginOptions) => anyCreates a Vue plugin to provide stack context at app level.
Options
enroll
MaybeRefOrGetter<boolean>When true, newly registered items are automatically selected if not disabled. Useful for pre-selecting items in multi-select scenarios.
mandatory
MaybeRefOrGetter<boolean | "force">Controls mandatory selection behavior: - `false` (default): No mandatory selection enforcement - `true`: Prevents deselecting the last selected item (user must always have one selected) - `'force'`: Automatically selects the first non-disabled item on registration
Properties
Methods
seek
(direction?: "first" | "last", from?: number, predicate?: (ticket) => boolean) => Z | undefinedSeek for a ticket based on direction and optional predicate
on
<K extends Extensible<RegistryEventName>>(event: K, cb: EventHandler<Z, K>) => voidListen for registry events
off
<K extends Extensible<RegistryEventName>>(event: K, cb: EventHandler<Z, K>) => voidStop listening for registry events
emit
<K extends Extensible<RegistryEventName>>(event: K, data: EventPayload<Z, K>) => voidEmit an event with data
batch
<R>(fn: () => R) => RExecute operations in a batch, deferring cache invalidation and event emission until complete