Skip to content
Sponsored by

MagicModal

MagicModal is a flexible, unstyled modal component. Useful for things like confirmation dialogs, detail views, and forms.

<template>
  <m-button @click="modalApi.open">Open Modal</m-button>
  <magic-modal id="magic-modal-default-demo" :options="{ focusTrap: false }">
    <div class="bg-surface-high aspect-4/3 w-[40rem] rounded-md" />
  </magic-modal>
</template>

<script lang="ts" setup>
import { MButton } from '@maas/mirror/vue'
import { useMagicModal } from '@maas/vue-equipment/plugins/MagicModal'

const modalApi = useMagicModal('magic-modal-default-demo')
</script>

Overview

Anatomy

MagicModal can be used as a single self-contained component or composed from its individual parts for full control.

Simple

vue
<template>
  <magic-modal id="your-modal-id">
    <!-- your content -->
  </magic-modal>
</template>

<script setup>
const { open } = useMagicModal('your-modal-id')
</script>

Composed

vue
<template>
  <magic-modal-provider id="your-modal-id">
    <magic-modal-trigger as-child>
      <button>Open</button>
    </magic-modal-trigger>
    <magic-modal-teleport>
      <magic-modal-backdrop />
      <magic-modal-content>
        <!-- your content -->
      </magic-modal-content>
    </magic-modal-teleport>
  </magic-modal-provider>
</template>

TIP

MagicModalTeleport is optional. Omit it to keep the modal mounted at all times, with visibility controlled via v-show.

Installation

CLI

Add @maas/vue-equipment to your dependencies.

sh
pnpm install @maas/vue-equipment
sh
npm install @maas/vue-equipment
sh
yarn add @maas/vue-equipment
sh
bun install @maas/vue-equipment

Vue

If you are using Vue, import and add MagicModalPlugin to your app.

js
import { createApp } from 'vue'
import { MagicModalPlugin } from '@maas/vue-equipment/plugins/MagicModal'

const app = createApp({})

app.use(MagicModalPlugin)

Nuxt

The modal is available as a Nuxt module. In your Nuxt config file add @maas/vue-equipment/nuxt to your modules and add MagicModal to the plugins in your configuration.

js
export default defineNuxtConfig({
  modules: ['@maas/vue-equipment/nuxt'],
  vueEquipment: {
    plugins: ['MagicModal'],
  },
})

Direct Import

If you prefer a more granular approach, components can be directly imported.

vue
<script setup>
import {
  MagicModalProvider,
  MagicModalTeleport,
  MagicModalBackdrop,
  MagicModalContent,
  MagicModalTrigger,
} from '@maas/vue-equipment/plugins/MagicModal'
</script>

Composable

In order to interact with the modal from anywhere within your app, we provide a useMagicModal composable. Import it directly when needed.

js
import { onMounted } from 'vue'
import { useMagicModal } from '@maas/vue-equipment/plugins/MagicModal'

const { open } = useMagicModal('your-modal-id')

onMounted(() => {
  open()
})

TIP

If you have installed the modal as a Nuxt module, the composable will be auto-imported and is automatically available in your Nuxt app.

Peer Dependencies

If you haven’t installed the required peer dependencies automatically, you’ll need to install the following packages manually.

Installation

sh
pnpm install @nuxt/kit @vueuse/core @vueuse/integrations defu focus-trap
sh
npm install @nuxt/kit @vueuse/core @vueuse/integrations defu focus-trap
sh
yarn add @nuxt/kit @vueuse/core @vueuse/integrations defu focus-trap
sh
bun install @nuxt/kit @vueuse/core @vueuse/integrations defu focus-trap

API Reference

MagicModalProvider

The MagicModalProvider wraps the modal and configures all child components according to the provided options.

Props

PropTypeRequired
id
MaybeRef<string>true
options
MagicModalOptionsfalse

Options

To customize the modal, override the necessary options. Any custom options will be merged with the default options.

OptionTypeDefault
tag
string
'dialog'
focusTrap
boolean | FocusTrapOptionsobject
scrollLock
boolean | objectobject
scrollLock.padding
booleantrue
teleport.target
string'body'
teleport.disabled
booleanfalse
transition.content
string'magic-modal-content'
transition.backdrop
string'magic-modal-backdrop'
keyListener.close
string[] | false['Escape']

MagicModalTeleport

Teleports all child components to a target in the DOM. Uses v-if to mount and unmount its contents when the modal opens and closes — keeping the DOM clean when the modal is inactive. Omit this component if you want the modal to remain mounted at all times.

Props

PropTypeRequired
to
string | RendererElementfalse
disabled
booleanfalse

MagicModalBackdrop

Renders a full-viewport overlay behind the modal panel. Closes the modal when clicked.

CSS Variables

VariableDefault
--magic-modal-backdrop-positionfixed
--magic-modal-backdrop-top0
--magic-modal-backdrop-left0
--magic-modal-backdrop-right0
--magic-modal-backdrop-bottom0
--magic-modal-backdrop-width100%
--magic-modal-backdrop-height100%
--magic-modal-backdrop-colorrgba(0, 0, 0, 0.5)
--magic-modal-backdrop-filterunset
--magic-modal-backdrop-z-index998

MagicModalContent

Renders the modal panel with focus trap and scroll lock support.

CSS Variables

VariableDefault
--magic-modal-positionfixed
--magic-modal-inset0
--magic-modal-width100%
--magic-modal-height100%
--magic-modal-displayflex
--magic-modal-justify-contentcenter
--magic-modal-align-itemscenter
--magic-modal-z-index999
--magic-modal-content-max-height100%
--magic-modal-content-width100%
--magic-modal-content-displayflex
--magic-modal-content-align-itemscenter
--magic-modal-content-justify-contentcenter
--magic-modal-content-overflow-yauto

MagicModalTrigger

Opens or closes the modal on click.

Props

PropTypeRequired
id
MaybeRef<string>false
disabled
booleanfalse
asChild
booleanfalse

Slot Props

PropTypeDescription
activebooleanWhether the modal is currently open.
disabledbooleanWhether the trigger is currently disabled.

MagicModal

A self-contained component that composes all primitives internally. Use this for simple cases where you don’t need to customise the markup.

Props

PropTypeRequired
id
MaybeRef<string>true
options
MagicModalOptionsfalse

Slots

SlotDescription
defaultContent rendered inside the modal panel.
backdropContent rendered inside the backdrop element.

Errors

SourceError CodeMessage
MagicModalTeleportmissing_instance_idMagicModalTeleport must be nested inside MagicModalProvider
MagicModalBackdropmissing_instance_idMagicModalBackdrop must be nested inside MagicModalProvider
MagicModalContentmissing_instance_idMagicModalContent must be nested inside MagicModalProvider
MagicModalTriggermissing_instance_idMagicModalTrigger must be nested inside MagicModalProvider or an id must be provided

Examples

Default

<template>
  <m-button @click="modalApi.open">Open Modal</m-button>
  <magic-modal id="magic-modal-default-demo" :options="{ focusTrap: false }">
    <div class="bg-surface-high aspect-4/3 w-[40rem] rounded-md" />
  </magic-modal>
</template>

<script lang="ts" setup>
import { MButton } from '@maas/mirror/vue'
import { useMagicModal } from '@maas/vue-equipment/plugins/MagicModal'

const modalApi = useMagicModal('magic-modal-default-demo')
</script>

Composed

<template>
  <magic-modal-provider id="magic-modal-composed-demo" :options="{ focusTrap: false }">
    <magic-modal-trigger as-child>
      <m-button>Open Modal</m-button>
    </magic-modal-trigger>
    <magic-modal-teleport>
      <magic-modal-backdrop />
      <magic-modal-content>
        <div class="bg-surface-high aspect-[4/3] w-[40rem] rounded-md" />
      </magic-modal-content>
    </magic-modal-teleport>
  </magic-modal-provider>
</template>

<script lang="ts" setup>
import { MButton } from '@maas/mirror/vue'
import {
  MagicModalProvider,
  MagicModalTeleport,
  MagicModalBackdrop,
  MagicModalContent,
  MagicModalTrigger,
} from '@maas/vue-equipment/plugins/MagicModal'
</script>