Troubleshooting
Troubleshooting
Error messages
[@kolirt/vue-modal] openModal() requires a 'group' option…
Cause. Neither options.group was passed to openModal / replaceModal, nor does the component carry defineOptions({ modalGroup: '…' }).
Fix. Add the group explicitly:
openModal(MyModal, { group: 'default' })
or declare it on the component so it can be omitted at call sites:
// MyModal.vue
defineOptions({ modalGroup: 'default' })
[@kolirt/vue-modal] <ModalRoot> must be used inside a modal opened via openModal/useModal
Cause. <ModalRoot> reads modalContextKey from inject. If the component is rendered outside the package's internal mount path (e.g., dropped directly in a template without going through openModal), the injection is absent.
Fix. Only render <ModalRoot> inside components that are opened via openModal or useModal. Do not place it in static templates.
[@kolirt/vue-modal] <ModalRoot> must be rendered inside a <ModalTarget> tree
Cause. The modal was opened for a group that has no mounted <ModalTarget>. <ModalRoot> looks for modalGroupConfigKey which <ModalTarget> provides.
Fix. Add a <ModalTarget> for that group somewhere in your app layout:
<ModalTarget group="default" />
Confirm the target is actually mounted (not hidden behind a v-if) when the modal opens.
[@kolirt/vue-modal] <ModalContent> must be used inside <ModalRoot>
Cause. <ModalContent> injects modalRootContextKey which <ModalRoot> provides. Using <ModalContent> outside <ModalRoot>, or swapping the nesting order, breaks the injection.
Fix. Ensure the hierarchy is <ModalRoot> → <ModalContent> with nothing between them that breaks the provide chain (e.g., async boundaries).
[@kolirt/vue-modal] useModalContext() must be called inside a modal component
Cause. useModalContext was called in a component that was not opened through the package (no modalContextKey injection present).
Fix. Call useModalContext only inside components opened via openModal or useModal.
Behavioral problems
Modal opens but never shows / appears blank
Most likely <ModalContent> is missing from the modal component. <ModalRoot> renders a <DialogRoot> wrapper; visible content and transitions live inside <ModalContent>. Without it, nothing is shown and the exit animation never fires, leaving the modal stuck in the stack.
Modal opens but never closes / hangs in closing state
The exit animation never completed, so finalizeModal was never called. This happens when:
<ModalContent>is absent (itsonAfterLeavehook triggersfinalize).- A CSS transition is defined but the
transitionendevent never fires (e.g.,display: noneapplied instead of opacity).
Check that <ModalContent> is present and that any transitions end normally.
z-index stacking issues
<ModalTarget> renders a div[data-modal-region] with position: fixed; inset: 0 at zero specificity (:where()). If another fixed element on the page has a higher stacking context, it may overlay the modal.
Options:
- Move
<ModalTarget>later in the DOM (later siblings paint on top in the same stacking context). - Set an explicit
z-indexby targeting[data-modal-region]in your CSS. - Wrap
<ModalTarget>in a<div style="position: fixed; inset: 0; z-index: 1000">.
Body scroll-lock causes layout shift
When scroll lock engages, the scrollbar disappears and content shifts right. useScrollLock automatically compensates by adding paddingRight equal to the scrollbar width. If you still see a shift:
- Ensure no other code resets
body.paddingRight. - If your layout uses
overflow: hiddenon a wrapper instead of<body>, the padding compensation may apply to the wrong element — target[data-modal-region]with a matching padding instead.
Modal flickers on rapid open/close
When a modal is opened and closed faster than its enter animation completes, the enter and exit can visually collide.
Use instantEnter or instantExit flags to skip animations for programmatic flows:
openModal(MyModal, { group: 'default', instantEnter: true })
// or close immediately:
handle.close({ instantExit: true })
TypeScript errors
Type '"foo"' is not assignable to type 'never' on group
Cause. ModalGroupRegistry has no entries, so ModalGroup resolves to never.
Fix. Declare your groups in a .d.ts file included by tsconfig.json:
import type { DefineGroups } from '@kolirt/vue-modal'
declare module '@kolirt/vue-modal' {
interface ModalGroupRegistry extends DefineGroups<['default', 'confirm']> {}
}
Props type error on openModal
openModal infers prop types from the component's $props. If props are not typed with defineProps<{…}>(), inference falls back to Record<string, unknown>. Add explicit prop types to the modal component to get autocomplete and type checking on options.props.
