Skip to content

Toaster

Bake your Toast and let it fly! You should insert in into your <body>

The toaster uses the Popover API (popover="manual") so toasts always appear in the top-layer — even above open dialogs. Use toasterObserver to show/hide the toaster popover automatically as toasts are added and removed.

View Source 

Installation

Follow instructions for individual framework usage below

  • winduum 
  • winduum-elements 
  • winduum-stimulus 
  • winduum-vue 
  • winduum-react 

Styles

Variants

  • default 

Props

  • default 

Scripts

The Toaster script shows or hides the toaster popover as toasts are added or removed.

INFO

For winduum-elements and winduum-stimulus, toasts are expected to be appended into the Toaster from the backend, for example via ajax. You can also append the HTML manually, for example with insertAdjacentHTML.

Examples

Default

The toaster lives at the end of your <body> — toasts are inserted into it and the popover shows/hides automatically via toasterObserver.

html
<button class="x-button" id="showToast">Append toast</button>
<button class="x-button muted" command="--close" commandfor="toasterDefault">Close all</button>

<template id="toastTemplate">
    <x-toast class="x-toast" role="status" aria-live="assertive" aria-atomic="true">
        <div class="x-toast-content">
            <div class="flex-col">
                <div class="x-title">Hello toast</div>
                <div class="x-text">Amazing toast</div>
            </div>
            <button class="x-button muted ml-auto" command="--close">Close</button>
        </div>
    </x-toast>
</template>

<x-toaster class="x-toaster items-end" popover="manual" id="toasterDefault"></x-toaster>
liquid
<div data-controller="invoke">
<button class="x-button" id="showToast">Append toast</button>
<button class="x-button muted" data-action="click->invoke#action" data-invoke-action="x-toaster#close" data-invoke-target="#toasterDefault">Close all</button>

<template id="toastTemplate">
    <li class="x-toast" data-controller="x-toast" data-action="x-toast:connect->x-toast#show" role="status" aria-live="assertive" aria-atomic="true">
        <div class="x-toast-content">
            <div class="flex-col">
                <div class="x-title">Hello toast</div>
                <div class="x-text">Amazing toast</div>
            </div>
            <button class="x-button muted ml-auto" data-action="click->x-toast#close">Close</button>
        </div>
    </li>
</template>

<ol class="x-toaster items-end" popover="manual" id="toasterDefault" data-controller="x-toaster"></ol>
</div>
vue
<script setup lang="ts">
    import { ref } from 'vue'
    import { Button } from '@/components/button'
    import { Title } from '@/components/title'
    import { Text } from '@/components/text'
    import { Toast, ToastContent } from '@/components/toast'
    import { Toaster } from '@/components/toaster'

    const toasts = ref<number[]>([Date.now()])

    const closeToast = (toast: number) => {
        toasts.value = toasts.value.filter(item => item !== toast)
    }

    const closeToaster = () => {
        toasts.value = []
    }
</script>

<template>
    <Button class="muted" @click="closeToaster">Close all</Button>

    <Toaster class="items-end">
        <Toast v-for="toast in toasts" :key="toast" @close="closeToast(toast)">
            <ToastContent>
                <div class="flex-col">
                    <Title>Hello toast</Title>
                    <Text>Amazing toast</Text>
                </div>
                <Button class="muted ml-auto" data-action="closeToast">Close</Button>
            </ToastContent>
        </Toast>
    </Toaster>
</template>
tsx
import { useState } from "react"
import { Button } from "@/components/button"
import { Title } from "@/components/title"
import { Text } from "@/components/text"
import { Toast, ToastContent } from "@/components/toast"
import { Toaster } from "@/components/toaster"

export function Example() {
    const [toasts, setToasts] = useState<number[]>([Date.now()])

    const closeToast = (toast: number) => {
        setToasts(current => current.filter(item => item !== toast))
    }

    const closeToaster = () => {
        setToasts([])
    }

    return (
        <>
            <Button className="muted" onClick={closeToaster}>Close all</Button>

            <Toaster className="items-end">
                {toasts.map((toast) => (
                    <Toast key={toast} onClose={() => closeToast(toast)}>
                        <ToastContent>
                            <div className="flex-col">
                                <Title>Hello toast</Title>
                                <Text>Amazing toast</Text>
                            </div>
                            <Button className="muted ml-auto" data-action="closeToast">Close</Button>
                        </ToastContent>
                    </Toast>
                ))}
            </Toaster>
        </>
    )
}

JavaScript API

closeToaster

  • Type: (element: HTMLElement, options?: CloseToastOptions) => void
  • Kind: sync

All toasts are closed at once.

Example

js
import { closeToaster } from 'winduum/src/components/toaster'

document.querySelector('#closeToaster').addEventListener('click', () => {
    closeToaster(document.querySelector('.x-toaster'))
})

toasterObserver

  • Type: () => MutationObserver
  • Kind: sync

Returns a MutationObserver that shows the toaster popover when the first toast is inserted and hides it when the last toast is removed. Observe the toaster element with { childList: true }.

Example

js
import { toasterObserver } from 'winduum/src/components/toaster'

const observer = toasterObserver()

observer.observe(document.querySelector('.x-toaster'), {
    childList: true,
})

Released under the MIT License.