Skip to content
                            Sponsored by

                            MagicToast

                            MagicToast let’s you trigger and display toasts from anywhere.

                            <template>
                              <m-button @click="onClick">Add Toast</m-button>
                              <magic-toast-provider id="magic-toast-demo" />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const { add } = useMagicToast('magic-toast-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              const props = {
                                message: count.value % 2 ? 'Magic as a Service™' : 'Vue Equipment',
                              }
                            
                              add({ component, props })
                            }
                            </script>

                            Overview

                            Anatomy

                            vue
                            <template>
                              <magic-toast-provider id="your-toast-id" />
                            </template>
                            
                            <script>
                            import { defineAsyncComponent } from 'vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const component = defineAsyncComponent(() => import('./YourToast.vue'))
                            const { add } = useMagicToast('your-toast-id')
                            
                            function handleClick() {
                              add({ component })
                            }
                            </script>

                            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 MagicToastPlugin to your app.

                            js
                            import { createApp } from 'vue'
                            import { MagicToastPlugin } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const app = createApp({})
                            
                            app.use(MagicToastPlugin)

                            Nuxt

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

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

                            Composable

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

                            js
                            const component = defineAsyncComponent(() => import('./YourToast.vue'))
                            const { add } = useMagicToast('your-toast-id')
                            
                            function handleClick() {
                              add({ component })
                            }

                            TIP

                            If you have installed the toaster 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 defu
                            sh
                            npm install @nuxt/kit @vueuse/core defu
                            sh
                            yarn add @nuxt/kit @vueuse/core defu
                            sh
                            bun install @nuxt/kit @vueuse/core defu

                            API Reference

                            MagicToastProvider

                            The MagicToastProvider wraps the toaster and configures it according to the provided options.

                            Props

                            PropTypeRequired
                            id
                            MaybeRef<string>true
                            options
                            MagicMenuOptionsfalse

                            Options

                            OptionTypeDefault
                            debug
                            booleanfalse
                            position
                            Positionbottom
                            duration
                            number0
                            draggable
                            booleantrue
                            drag.direction
                            string | string[]
                            auto
                            scrollLock
                            boolean | objectobject
                            scrollLock.padding
                            booleantrue
                            teleport.target
                            stringbody
                            teleport.disabled
                            booleanfalse
                            transition
                            stringmagic-toast
                            layout.expand
                            string | false
                            click
                            layout.max
                            number3
                            animation.snap.duration
                            number300
                            animation.snap.easing
                            function
                            initial.expanded
                            booleanfalse
                            threshold.lock
                            number8
                            threshold.distancenumber32
                            threshold.momentum
                            number1

                            CSS Variables

                            VariableDefault
                            --magic-toast-padding-y1rem
                            --magic-toast-padding-x1rem
                            --magic-toast-gap0.75rem
                            --magic-toast-animation-duration175ms
                            --magic-toast-scale-factor0.05
                            --magic-toast-overlap-y1rem
                            --magic-toast-positionfixed
                            --magic-toast-inset0
                            --magic-toast-width100%
                            --magic-toast-height100%
                            --magic-toast-z-index999

                            MagicToastView

                            Renders a single toast inside the toaster. Used internally.

                            VariableDefault
                            --magic-toast-view-transitionall var(--magic-toast-animation-duration) var(--ease-in-out)
                            --magic-toast-view-cursorgrab
                            --magic-toast-view-cursor-dragginggrabbing

                            useMagicToast

                            Interact with a toaster instance from anywhere in your app. Pass the same id used on the corresponding MagicToastProvider.

                            js
                            const { add, remove, clear, hide, show, expand, collapse, toasts, count } =
                              useMagicToast('your-toast-id')

                            add

                            Adds a toast to the stack and returns its id.

                            ArgumentTypeRequired
                            component
                            Componenttrue
                            props
                            Record<string, unknown>false
                            slots
                            Slotsfalse
                            duration
                            numberfalse
                            draggable
                            booleanfalse
                            id
                            stringfalse
                            js
                            const id = add({ component, props: { message: 'Saved!' } })

                            remove

                            Removes a toast by id.

                            js
                            remove(id)

                            clear

                            Removes all toasts, including any currently hidden via hide(). Optionally pass a transition name to use while clearing, instead of the toaster’s configured transition option.

                            js
                            clear()
                            clear('my-clear-transition')

                            hide / show

                            Temporarily hide the entire toast stack and restore it later, preserving each toast’s state (props, timers, etc.) in between. Auto-dismiss timers are paused while hidden and resume when shown again. Both optionally take a transition name to use instead of the toaster’s configured transition option. Calling hide() with nothing to hide, or show() with nothing hidden, is a no-op.

                            Calling add() while the stack is hidden reveals it again immediately, so a new toast is never silently queued out of sight — it’s equivalent to calling show() first.

                            js
                            hide()
                            show()
                            
                            hide('my-hide-transition')
                            show('my-show-transition')

                            expand / collapse

                            Expand or collapse the toast stack, mirroring what layout.expand: 'click' | 'hover' does on interaction.

                            js
                            expand()
                            collapse()

                            State

                            PropertyTypeDescription
                            toastsComputedRef<ToastView[]>The currently active toasts.
                            countComputedRef<number>Number of currently active toasts.
                            hiddenCountComputedRef<number>Number of toasts currently stashed via hide(). Useful for disabling hide()/show() controls when there’s nothing to hide or restore.

                            Errors

                            SourceError CodeMessage
                            MagicToastViewmissing_instance_idMagicToastView must be used within a MagicToastProvider

                            Examples

                            Position

                            Set the position option to anchor the toaster to any edge or corner of the viewport.

                            <template>
                              <div class="w-full flex gap-2">
                                <m-button @click="onClick">Add Toast</m-button>
                                <m-select
                                  v-model="position"
                                  :options="options"
                                  label="Position"
                                  placeholder="Position"
                                />
                              </div>
                              <magic-toast-provider
                                id="magic-toast-position-demo"
                                :options="{
                                  position,
                                }"
                              />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton, MSelect } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const { add } = useMagicToast('magic-toast-position-demo')
                            
                            const position = ref('bottom-right')
                            
                            const options = [
                              { value: 'top-right', label: 'Top Right' },
                              { value: 'top', label: 'Top' },
                              { value: 'top-left', label: 'Top Left' },
                              { value: 'left', label: 'Left' },
                              { value: 'bottom-left', label: 'Bottom Left' },
                              { value: 'bottom', label: 'Bottom' },
                              { value: 'bottom-right', label: 'Bottom Right' },
                              { value: 'right', label: 'Right' },
                            ]
                            
                            function onClick() {
                              const props = {
                                message: options.find((p) => p.value === position.value)?.label,
                              }
                            
                              add({ component, props })
                            }
                            </script>

                            Expanded

                            Set initial.expanded: true together with layout.expand: false to always render the stack fully expanded, instead of collapsed into an overlapping pile.

                            <template>
                              <m-button @click="onClick">Add Toast</m-button>
                              <magic-toast-provider id="magic-toast-expanded-demo" :options="options" />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const { add } = useMagicToast('magic-toast-expanded-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              const props = {
                                message: count.value % 2 ? 'Magic as a Service™' : 'Vue Equipment',
                              }
                            
                              add({ component, props })
                            }
                            
                            const options = {
                              layout: {
                                expand: false,
                              },
                              initial: {
                                expanded: true,
                              },
                            }
                            </script>

                            Collapsed

                            Set layout.expand: false to keep the stack collapsed permanently, with no click or hover interaction to expand it.

                            <template>
                              <m-button @click="onClick">Add Toast</m-button>
                              <magic-toast-provider id="magic-toast-collapsed-demo" :options="options" />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const { add } = useMagicToast('magic-toast-collapsed-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              const props = {
                                message: count.value % 2 ? 'Magic as a Service™' : 'Vue Equipment',
                              }
                            
                              add({ component, props })
                            }
                            
                            const options = {
                              layout: {
                                expand: false,
                              },
                            }
                            </script>

                            Hover

                            Set layout.expand: 'hover' to expand the stack while the pointer is over it, and collapse it again on mouse leave.

                            <template>
                              <m-button @click="onClick">Add Toast</m-button>
                              <magic-toast-provider id="magic-toast-hover-demo" :options="options" />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const { add } = useMagicToast('magic-toast-hover-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              const props = {
                                message: count.value % 2 ? 'Magic as a Service™' : 'Vue Equipment',
                              }
                            
                              add({ component, props })
                            }
                            
                            const options = {
                              layout: {
                                expand: 'hover',
                              },
                            }
                            </script>

                            No Drag

                            Set draggable: false on the provider to disable drag-to-dismiss for every toast in the stack.

                            <template>
                              <m-button @click="onClick">Add Toast</m-button>
                              <magic-toast-provider id="magic-toast-no-drag-demo" :options="options" />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const { add } = useMagicToast('magic-toast-no-drag-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              const props = {
                                message: count.value % 2 ? 'Magic as a Service™' : 'Vue Equipment',
                              }
                            
                              add({ component, props })
                            }
                            
                            const options = {
                              draggable: false,
                            }
                            </script>

                            Per-Toast Draggable

                            Pass draggable to add() to override the provider’s default for a single toast, without affecting the rest of the stack.

                            <template>
                              <div class="flex w-full gap-2">
                                <m-button @click="onClick">Add Toast</m-button>
                                <m-toggle v-model="draggable" label="Draggable" />
                              </div>
                              <magic-toast-provider id="magic-toast-per-toast-draggable-demo" />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton, MToggle } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const { add } = useMagicToast('magic-toast-per-toast-draggable-demo')
                            
                            const draggable = ref(true)
                            
                            function onClick() {
                              add({
                                component,
                                draggable: draggable.value,
                                props: {
                                  message: draggable.value
                                    ? 'Drag me to dismiss'
                                    : 'This toast is not draggable',
                                },
                              })
                            }
                            </script>

                            Drag Direction

                            Use drag.direction to pin the drag-out side ('left' | 'right' | 'up' | 'down') regardless of position, or pass a same-axis pair like ['left', 'right'] to allow dragging either way.

                            <template>
                              <div class="flex w-full gap-2">
                                <m-button @click="onClick">Add Toast</m-button>
                                <m-select
                                  v-model="position"
                                  :options="positionOptions"
                                  label="Position"
                                  placeholder="Position"
                                />
                                <m-select
                                  v-model="direction"
                                  :options="directionOptions"
                                  label="Drag direction"
                                  placeholder="Drag direction"
                                />
                              </div>
                              <magic-toast-provider
                                id="magic-toast-drag-direction-demo"
                                :options="{
                                  position,
                                  drag: { direction: resolvedDirection },
                                  layout: {
                                    expand: false,
                                    max: 10,
                                  },
                                  initial: {
                                    expanded: true,
                                  },
                                }"
                              />
                            </template>
                            
                            <script lang="ts" setup>
                            import { computed, defineAsyncComponent, ref } from 'vue'
                            import { MButton, MSelect } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const { add } = useMagicToast('magic-toast-drag-direction-demo')
                            
                            const position = ref('bottom-right')
                            const direction = ref('left-right')
                            
                            const positionOptions = [
                              { value: 'top-right', label: 'Top Right' },
                              { value: 'top', label: 'Top' },
                              { value: 'top-left', label: 'Top Left' },
                              { value: 'left', label: 'Left' },
                              { value: 'bottom-left', label: 'Bottom Left' },
                              { value: 'bottom', label: 'Bottom' },
                              { value: 'bottom-right', label: 'Bottom Right' },
                              { value: 'right', label: 'Right' },
                            ]
                            
                            const directionOptions = [
                              { value: 'auto', label: 'Auto' },
                              { value: 'left', label: 'Left' },
                              { value: 'right', label: 'Right' },
                              { value: 'up', label: 'Up' },
                              { value: 'down', label: 'Down' },
                              { value: 'left-right', label: 'Left & Right' },
                              { value: 'up-down', label: 'Up & Down' },
                            ]
                            
                            // MSelect only supports a single value, so the same-axis pairs are
                            // offered as presets here and expanded into an array for the option.
                            const resolvedDirection = computed(() => {
                              if (direction.value === 'left-right') {
                                return ['left', 'right'] as const
                              }
                            
                              if (direction.value === 'up-down') {
                                return ['up', 'down'] as const
                              }
                            
                              return direction.value
                            })
                            
                            // Mirrors the fallback in useToastDrag: when direction is 'auto',
                            // the actual drag axis/side is derived from the anchor position.
                            const messageDirection = computed(() => {
                              if (direction.value === 'left-right') {
                                return 'left or right'
                              }
                            
                              if (direction.value === 'up-down') {
                                return 'up or down'
                              }
                            
                              if (direction.value !== 'auto') {
                                return direction.value
                              }
                            
                              switch (position.value) {
                                case 'top':
                                case 'top-left':
                                case 'top-right':
                                  return 'up'
                                case 'bottom':
                                case 'bottom-left':
                                case 'bottom-right':
                                  return 'down'
                                case 'left':
                                  return 'left'
                                case 'right':
                                  return 'right'
                                default:
                                  return direction.value
                              }
                            })
                            
                            function onClick() {
                              add({
                                component,
                                props: { message: `Drag me ${messageDirection.value} to dismiss` },
                              })
                            }
                            </script>

                            Clear All

                            Call clear() to remove every toast in the stack at once, optionally with a custom transition name.

                            <template>
                              <div class="flex gap-2">
                                <m-button @click="onClick">Add Toast</m-button>
                                <m-button @click="clear">Clear All</m-button>
                              </div>
                              <magic-toast-provider id="magic-toast-clear-all-demo" />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const { add, clear } = useMagicToast('magic-toast-clear-all-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              const props = {
                                message: count.value % 2 ? 'Magic as a Service™' : 'Vue Equipment',
                              }
                            
                              add({ component, props })
                            }
                            </script>

                            Hide and Show

                            Call hide() to temporarily remove the whole stack from view, and show() to restore it exactly as it was — props, timers and all.

                            <template>
                              <div class="flex gap-2">
                                <m-button @click="onClick">Add Toast</m-button>
                                <m-button :disabled="toastCount === 0" @click="hide">Hide</m-button>
                                <m-button :disabled="hiddenCount === 0" @click="show">Show</m-button>
                              </div>
                              <magic-toast-provider
                                id="magic-toast-hide-show-demo"
                                :options="{
                                  layout: {
                                    expand: false,
                                    max: 10,
                                  },
                                  initial: {
                                    expanded: true,
                                  },
                                }"
                              />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const {
                              add,
                              hide,
                              show,
                              count: toastCount,
                              hiddenCount,
                            } = useMagicToast('magic-toast-hide-show-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              const props = {
                                message: count.value % 2 ? 'Magic as a Service™' : 'Vue Equipment',
                              }
                            
                              add({ component, props })
                            }
                            </script>

                            Hide and Show with a Custom Transition

                            Pass a transition name to hide()/show() to swap in a different animation just for that call, without changing the toaster’s configured transition option.

                            <template>
                              <div class="flex gap-2">
                                <m-button @click="onClick">Add Toast</m-button>
                                <m-button :disabled="toastCount === 0" @click="hide('zoom')">
                                  Hide (zoom)
                                </m-button>
                                <m-button :disabled="hiddenCount === 0" @click="show('zoom')">
                                  Show (zoom)
                                </m-button>
                              </div>
                              <magic-toast-provider
                                id="magic-toast-hide-show-transition-demo"
                                :options="{
                                  layout: {
                                    expand: false,
                                    max: 10,
                                  },
                                  initial: {
                                    expanded: true,
                                  },
                                }"
                              />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            import '@maas/vue-equipment/utils/css/keyframes/zoom-in.css'
                            import '@maas/vue-equipment/utils/css/keyframes/zoom-out.css'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            const {
                              add,
                              hide,
                              show,
                              count: toastCount,
                              hiddenCount,
                            } = useMagicToast('magic-toast-hide-show-transition-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              const props = {
                                message: count.value % 2 ? 'Magic as a Service™' : 'Vue Equipment',
                              }
                            
                              add({ component, props })
                            }
                            </script>
                            
                            <style>
                            /* Custom transition used only for hide('zoom') / show('zoom'),
                               passed as the transition name override on those calls. */
                            .zoom-enter-active {
                              animation: zoom-in var(--magic-toast-animation-duration) var(--ease-in-out);
                            }
                            
                            .zoom-leave-active {
                              animation: zoom-out var(--magic-toast-animation-duration) var(--ease-in-out);
                              position: absolute;
                            }
                            
                            .zoom-move {
                              transition: all var(--magic-toast-animation-duration) var(--ease-in-out);
                            }
                            </style>

                            Slot

                            Pass slots to add() to render arbitrary content inside the toast component via its default slot.

                            <template>
                              <m-button @click="onClick">Add Toast</m-button>
                              <magic-toast-provider id="magic-toast-slot-demo" />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref, h, type Slots } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            
                            const { add } = useMagicToast('magic-toast-slot-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              // Break reactivity for count
                              const mappedCount = count.value
                            
                              const slots: Slots = {
                                default: () => [h('span', `Message #${mappedCount}`)],
                              }
                            
                              add({ component, slots })
                            }
                            </script>

                            Slotted Component

                            Pass a component (instead of plain content) through slots, so the toast can render nested, composed markup.

                            <template>
                              <m-button @click="onClick">Add Toast</m-button>
                              <magic-toast-provider id="magic-toast-nested-slot-demo" />
                            </template>
                            
                            <script lang="ts" setup>
                            import { defineAsyncComponent, ref, h, type Slots } from 'vue'
                            import { MButton } from '@maas/mirror/vue'
                            import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
                            
                            const count = ref(0)
                            
                            const component = defineAsyncComponent(
                              () => import('./components/DemoToast.vue')
                            )
                            
                            const slot = defineAsyncComponent(() => import('./components/DemoSlot.vue'))
                            
                            const { add } = useMagicToast('magic-toast-nested-slot-demo')
                            
                            function onClick() {
                              count.value += 1
                            
                              // Break reactivity for count
                              const mappedCount = count.value
                            
                              const slots: Slots = {
                                default: () => [h(slot, { message: `Component #${mappedCount}` })],
                              }
                            
                              add({ component, slots })
                            }
                            </script>