Modal context
Modal context
useModalContext<T>() is the primary API inside a modal component. It returns everything the component needs to interact with the modal system.
Signature
function useModalContext<T = unknown>(): {
id: number
group: ModalGroup
isClosing: ComputedRef<boolean>
isTopmost: ComputedRef<boolean>
isTopmostGlobal: ComputedRef<boolean>
effectiveOptions: ComputedRef<ModalEffectiveOptions>
close(opts?: { ignoreGuard?: boolean; instantExit?: boolean }): void
confirm(data: T, opts?: { ignoreGuard?: boolean; instantExit?: boolean }): void
onBeforeClose(handler: BeforeCloseHandler): void
}
Throws if called outside a modal opened via openModal / useModal.
id and group
Numeric id and group name for this instance. Stable for the modal's lifetime. Pass id outward when something else needs to close this modal:
const { id } = useModalContext()
// elsewhere:
closeModalById(id, { success: true, data: result })
isClosing
true once a close is requested and the exit animation begins. The component is still mounted — use it to disable interactions during exit:
<button :disabled="isClosing" @click="confirm(true)">OK</button>
isTopmost vs isTopmostGlobal
| Property | True when… |
|---|---|
isTopmost | This modal is the topmost within its own group. |
isTopmostGlobal | This modal is the topmost across all groups. |
Use isTopmost to gate group-local UI; isTopmostGlobal for app-wide concerns (e.g. updating document.title).
effectiveOptions
ComputedRef<ModalEffectiveOptions> — the resolved behavior for this modal, combining <ModalTarget> props and the registered group config.
interface ModalEffectiveOptions {
interactOutside: boolean // non-modal mode (no focus trap)
closeOnInteractOutside: boolean // click outside region closes
closeOnInteractOverlay: boolean // click on overlay area closes
lockBodyScroll: boolean // body scroll is locked
closeOnEscape: boolean // Esc closes
}
Values are in the positive form (already negated from the disable* flags). Read-only.
close(opts?)
Rejects the modal's promise with ModalClosedError. Runs onBeforeClose guards unless ignoreGuard: true.
close()
close({ ignoreGuard: true })
close({ instantExit: true })
confirm(data, opts?)
Resolves the modal's promise with data. Type must match the generic on useModalContext<T>().
const { confirm } = useModalContext<{ value: string }>()
confirm({ value: 'hello' })
close and confirm are no-ops if the modal is already closing.
onBeforeClose
Register a guard. Runs on every close path — close(), confirm(), Esc, overlay click. Return false (or Promise<false>) to veto.
type BeforeCloseHandler = () => boolean | void | Promise<boolean | void>
The guard is auto-unregistered when the component unmounts.
Dirty-form guard
<script setup lang="ts">
import { ref } from 'vue'
import { openModal, useModalContext } from '@kolirt/vue-modal'
import ConfirmLeave from './ConfirmLeave.vue'
const { onBeforeClose } = useModalContext<{ saved: boolean }>()
const isDirty = ref(false)
onBeforeClose(async () => {
if (!isDirty.value) return
const ok = await openModal<boolean>(ConfirmLeave).catch(() => false)
if (!ok) return false // veto
})
</script>
Bypassing guards
Pass ignoreGuard: true from the caller side — replaceModal does this internally; route guards typically pair it with instantExit:
closeAllModals({ ignoreGuard: true, instantExit: true })
handle.close({ ignoreGuard: true })
Full example
<script setup lang="ts">
import { ref } from 'vue'
import {
ModalContent,
ModalRoot,
ModalTitle,
useModalContext
} from '@kolirt/vue-modal'
defineOptions({ modalGroup: 'default' })
const props = defineProps<{ userId: number }>()
const { isClosing, close, confirm, onBeforeClose } = useModalContext<{ name: string }>()
const name = ref('')
const dirty = ref(false)
onBeforeClose(() => {
if (dirty.value) return false
})
</script>
<template>
<ModalRoot class="root">
<ModalContent class="card">
<ModalTitle>Edit profile #{{ props.userId }}</ModalTitle>
<input v-model="name" @input="dirty = true" />
<button :disabled="isClosing" @click="close()">Cancel</button>
<button :disabled="isClosing" @click="confirm({ name })">Save</button>
</ModalContent>
</ModalRoot>
</template>
