Migration from v1
Migration from v1
v2 is a ground-up rewrite. The imperative promise-based approach stays, but nearly every API surface changed. Plan an afternoon — the migration is mechanical, not architectural.
Breaking changes at a glance
| Area | v1 | v2 |
|---|---|---|
| Source layout | lib/ flat | packages/core/src/ |
| Plugin options | createModal(Options) (animation, style) | createModal({ groups }) (behavior per group) |
| Groups | optional 'default' implicit | mandatory; declared via ModalGroupRegistry |
openModal signature | openModal(Component, props, options?) | openModal(Component, options?) — props inside options.props |
| Confirm / close inside modal | global confirmModal(data) / closeModal() | useModalContext().confirm(data) / .close() |
| Event bus | $on / $off / $emit from event.ts | removed; replaced by options.on map and ModalHandle.on/off |
| Template component | <ModalTarget group="…"> + scoped slot / BaseModal | <ModalTarget group="…"> wrapping your <ModalRoot>…<ModalContent> |
| Built-in layout | SimpleModal shipped | removed; headless only |
| TypeScript groups | untyped strings | declare module '@kolirt/vue-modal' { interface ModalGroupRegistry { … } } |
| Peer deps | none | reka-ui >=2, vue >=3.4 |
Step-by-step checklist
1. Install the new peer dependency
npm install reka-ui@^2
2. Update the plugin registration
import { createModal } from '@kolirt/vue-modal'
app.use(createModal({
transitionTime: 300,
animationType: 'slideDown',
modalStyle: { align: 'center' }
}))
import { createModal } from '@kolirt/vue-modal'
app.use(createModal({
groups: {
default: { disableCloseOnEscape: false }
}
}))
Animation and style options are removed. Apply CSS transitions directly on <ModalContent>.
3. Declare groups in TypeScript
Add a global augmentation file (e.g., src/modal-groups.d.ts):
import type { DefineGroups } from '@kolirt/vue-modal'
declare module '@kolirt/vue-modal' {
interface ModalGroupRegistry extends DefineGroups<['default', 'confirm']> {}
}
Every group value in openModal, <ModalTarget>, and createModal is now checked at compile time.
4. Update openModal call sites
await openModal(ConfirmDialog, { title: 'Delete?' }, { group: 'confirm' })
await openModal(ConfirmDialog, {
group: 'confirm',
props: { title: 'Delete?' }
})
Props are now inside options.props. The standalone second argument is gone.
5. Replace confirmModal / closeModal inside modal components
import { confirmModal, closeModal } from '@kolirt/vue-modal'
async function onConfirm() {
await confirmModal(true)
}
async function onCancel() {
await closeModal()
}
import { useModalContext } from '@kolirt/vue-modal'
const modal = useModalContext<boolean>()
function onConfirm() {
modal.confirm(true)
}
function onCancel() {
modal.close()
}
6. Replace $on / $off event bus usage
import { $on, $off } from '@kolirt/vue-modal'
import { Events } from '@kolirt/vue-modal'
$on(Events.Closed, handler)
const handle = openModal(MyModal, {
group: 'default',
on: { myEvent: handler }
})
// or subscribe after open:
handle.on('myEvent', handler)
The global event bus ($on, $off, $emit, Events enum) is removed.
7. Rewrite modal template components
v1 used BaseModal internally with a scoped slot. v2 is fully headless — you compose <ModalRoot> → <ModalContent> yourself.
<ModalTarget group="default">
<template #default="{ modal, close }">
<div class="modal-wrapper">
<component :is="modal.component" v-bind="modal.props" @close="close" />
</div>
</template>
</ModalTarget>
<ModalTarget group="default" />
The <ModalTarget> in v2 renders modals automatically from the <ModalRoot> / <ModalContent> in each modal component. No scoped slot needed.
Modal components must now include <ModalRoot> and <ModalContent>:
<script setup lang="ts">
import { ModalContent, ModalRoot, useModalContext } from '@kolirt/vue-modal'
defineOptions({ modalGroup: 'default' })
const modal = useModalContext<boolean>()
</script>
<template>
<ModalRoot>
<ModalContent>
<button @click="modal.confirm(true)">OK</button>
<button @click="modal.close()">Cancel</button>
</ModalContent>
</ModalRoot>
</template>
8. Remove SimpleModal
SimpleModal was the only built-in layout in v1. It is not included in v2. Copy its template into your own component or use a design-system dialog.
9. Replace useLock
useLock (scroll lock composable) is no longer exported. Scroll locking is automatic — <ModalTarget> handles it via useScrollLock. If you needed it for other purposes, implement it with document.body.style.overflow.
Quick reference — renamed / removed exports
| v1 export | v2 equivalent |
|---|---|
openModal(C, props, opts) | openModal(C, { props, ...opts }) |
closeModal() | useModalContext().close() or closeModal({ group }) |
confirmModal(data) | useModalContext().confirm(data) |
closeAllModals() | closeAllModals() ✓ (same name) |
ModalTarget | ModalTarget ✓ (different behavior) |
BaseModal | ModalRoot + ModalContent |
SimpleModal | removed |
useLock | removed (automatic) |
$on / $off / $emit | handle.on / handle.off |
Events enum | removed |
modals | modals ✓ (same name, computed ref) |
isOpened | isOpened ✓ |
openModal with a bare props argument; v2 infers props from options.props. If you hit Type … is not assignable to type 'never', check that your group is declared in ModalGroupRegistry and that the augmentation file is included in tsconfig.json.