Api

Composables

useModal, useModalContext.

Composables

useModal<T, C>(component, defaults?)

Bind a single modal component to the current scope. Returns reactive isOpen, persistent event listeners that survive across multiple .open() calls, and auto-cleanup on scope unmount (configurable).

Signature

function useModal<T = unknown, C extends Component = Component>(
  component: C,
  defaults?: UseModalDefaults<C>
): {
  open(options?: OpenModalOptions<C>): Promise<T>
  close(opts?: CloseFlags): void
  on(event: string, handler: (...args: any[]) => void): void
  off(event: string, handler: (...args: any[]) => void): void
  isOpen: ComputedRef<boolean>
  isTop: ComputedRef<boolean>
  instanceId: Ref<number | null>
}

interface UseModalDefaults<C extends Component> extends OpenModalOptions<C> {
  closeOnUnmount?: boolean
}

Parameters

ParameterTypeRequiredDescription
componentC extends ComponentyesThe Vue component bound to this controller. All .open() calls mount this same component.
defaultsUseModalDefaults<C>noDefaults applied to every .open() call: inherits all of OpenModalOptions<C> (props, on, group, instantEnter) plus closeOnUnmount.

Type parameters identical to openModal.

Per-call merge. When you call .open(opts), defaults.props and opts.props are shallow-merged (per-call wins); listeners from both sources fire. group and instantEnter follow override semantics — per-call wins.

When to reach for useModal vs openModal

NeedUse
One-off call from a function/handleropenModal
Same modal opened multiple times from a componentuseModal
Reactive isOpen for binding to a button's disableduseModal
Persistent event listeners across open callsuseModal
Auto-close when the calling component unmountsuseModal

Example

<script setup lang="ts">
import { useModal } from '@kolirt/vue-modal'
import EditDialog from './EditDialog.vue'

const editModal = useModal<{ value: string }>(EditDialog, {
  group: 'default',
  props: { mode: 'inline' },
  on: {
    progress: (percent) => console.log(percent)
  },
  closeOnUnmount: true // default
})

async function edit(id: number) {
  const data = await editModal.open({ props: { id } }).catch(() => null)
  if (data) console.log('saved', data.value)
}
</script>

<template>
  <button :disabled="editModal.isOpen.value" @click="edit(42)">Edit</button>
</template>

open() merges defaults with the per-call options: props are shallow-merged (per-call wins), listeners from both sources fire. See Types for OpenModalOptions<C>.

Defaults

Same as OpenModalOptions<C> plus:

FieldTypeDefaultDescription
closeOnUnmountbooleantrueWhen the calling scope is disposed, close the open instance with ignoreGuard: true, instantExit: true.

Returned API

FieldTypeDescription
open(options?)(opts?: OpenModalOptions<C>) => Promise<T>Merge defaults + options (props shallow-merged with per-call wins, listeners merged), call openModal, await.
close(opts?)(opts?: { ignoreGuard?, instantExit? }) => voidClose current instance with success: false. No-op if not open.
on(event, handler)(event, handler) => voidAdd a persistent listener (survives across open calls).
off(event, handler)(event, handler) => voidRemove a persistent listener.
isOpenComputedRef<boolean>instanceId !== null — true while this instance is mounted in the stack (regardless of position).
isTopComputedRef<boolean>True only when this instance is the topmost in the global stack.
instanceIdRef<number | null>Current open instance id, or null.

useModalContext<T>()

Called inside a modal component (the one opened via openModal/useModal). Gives access to the close API, before-close guards, and effective behavior options.

Signature

function useModalContext<T = unknown>(): {
  id: number
  group: ModalGroup
  isClosing: ComputedRef<boolean>
  isTopmost: ComputedRef<boolean>
  isTopmostGlobal: ComputedRef<boolean>
  effectiveOptions: ComputedRef<ModalEffectiveOptions>
  close(opts?: CloseFlags): void
  confirm(data: T, opts?: CloseFlags): void
  onBeforeClose(handler: BeforeCloseHandler): void
}

Parameters

useModalContext takes no parameters. The single type parameter T (default unknown) describes the value confirm(data) resolves the modal's promise with — pass the same type you used when opening (openModal<T>(...) / useModal<T>(...)).

<script setup lang="ts">
import { useModalContext } from '@kolirt/vue-modal'

const { close, confirm, onBeforeClose, isTopmost, effectiveOptions } = useModalContext<boolean>()

onBeforeClose(async () => {
  if (formIsDirty.value) return await askConfirm()
})
</script>

Returned API

FieldTypeDescription
idnumberModal instance id.
groupModalGroupThis modal's group.
isClosingComputedRef<boolean>true from the moment close starts until DOM removal.
isTopmostComputedRef<boolean>Topmost in this modal's group.
isTopmostGlobalComputedRef<boolean>Topmost across all groups (last entry of modals).
effectiveOptionsComputedRef<ModalEffectiveOptions>Resolved positive-form options for this modal's group.
close(opts?)(opts?: { ignoreGuard?: boolean; instantExit?: boolean }) => voidReject the handle promise with ModalClosedError.
confirm(data, opts?)(data: T, opts?: { ignoreGuard?: boolean; instantExit?: boolean }) => voidResolve the handle promise with data.
onBeforeClose(handler)(handler: BeforeCloseHandler) => voidRegister a guard. Return false (or Promise<false>) to block close. Auto-unregistered on the component's onBeforeUnmount lifecycle hook.

effectiveOptions shape

interface ModalEffectiveOptions {
  interactOutside: boolean // false in modal mode
  closeOnInteractOutside: boolean // close on click outside the region
  closeOnInteractOverlay: boolean // close on click in the overlay/empty area
  lockBodyScroll: boolean
  closeOnEscape: boolean
}

Throws

If called outside a modal component (one opened via openModal/useModal). Exact message: [@kolirt/vue-modal] useModalContext() must be called inside a modal component (opened via openModal/useModal).

See Types for ModalEffectiveOptions, BeforeCloseHandler, and CloseModalOptions<T>.

Copyright © 2026