Toast

Toast is used to display messages in an overlay.


npx volt-vue add toast


import Toast from '@/volt/Toast.vue';

Toast component is controlled via the ToastService that needs to be installed as an application plugin.


import {createApp} from 'vue';
import ToastService from 'primevue/toastservice';

const app = createApp(App);
app.use(ToastService);

Recommended location of a Toast is the main application template so that the component can be accessed in a shared manner.


<template>
    <Toast />
</template>

The useToast composable can be accessed anywhere within your application to interact with the component.


<script setup lang="ts">
import { useToast } from 'primevue/usetoast';

const toast = useToast();   // instance to create messages
</script>

A single message is represented by the Message interface that defines properties such as severity, summary and detail.


<template>
    <Toast />
    <div class="card flex justify-center">
        <Button label="Show" @click="show()" />
    </div>
</template>

<script setup lang="ts">
import Toast from '@/volt/Toast.vue';
import Button from '@/volt/Button.vue';
import { useToast } from 'primevue/usetoast';

const toast = useToast();

const show = () => {
    toast.add({ severity: 'info', summary: 'Info', detail: 'Message Content', life: 3000 });
};
</script>

The severity option specifies the type of the message.


<template>
    <Toast />
     <div class="card flex flex-wrap gap-2 justify-center">
        <SecondaryButton label="Success" @click="showSuccess" />
        <SecondaryButton label="Info" @click="showInfo" />
        <SecondaryButton label="Warn" @click="showWarn" />
        <SecondaryButton label="Error" @click="showError" />
        <SecondaryButton label="Secondary" @click="showSecondary" />
        <SecondaryButton label="Contrast" @click="showContrast" />
    </div>
</template>

<script setup lang="ts">
import Toast from '@/volt/Toast.vue';
import SecondaryButton from '@/volt/Button.vue';
import { useToast } from "primevue/usetoast";

const toast = useToast();

const showSuccess = () => {
    toast.add({ severity: 'success', summary: 'Success Message', detail: 'Message Content', life: 3000 });
};

const showInfo = () => {
    toast.add({ severity: 'info', summary: 'Info Message', detail: 'Message Content', life: 3000 });
};

const showWarn = () => {
    toast.add({ severity: 'warn', summary: 'Warn Message', detail: 'Message Content', life: 3000 });
};

const showError = () => {
    toast.add({ severity: 'error', summary: 'Error Message', detail: 'Message Content', life: 3000 });
};

const showSecondary = () => {
    toast.add({ severity: 'secondary', summary: 'Secondary Message', detail: 'Message Content', life: 3000 });
};

const showContrast = () => {
    toast.add({ severity: 'contrast', summary: 'Contrast Message', detail: 'Message Content', life: 3000 });
};
</script>

A message can be targeted to a certain Toast component by matching the group keys whereas location is customized with the position.


<template>
    <Toast position="top-left" group="tl" />
    <Toast position="bottom-left" group="bl" />
    <Toast position="bottom-right" group="br" />
    <div class="card flex flex-wrap gap-2 justify-center">
        <Button label="Top Left" @click="showTopLeft" />
        <Button label="Bottom Left" @click="showBottomLeft" />
        <Button label="Bottom Right" @click="showBottomRight" />
    </div>
</template>

<script setup lang="ts">
import Toast from '@/volt/Toast.vue';
import Button from '@/volt/Button.vue';
import { useToast } from 'primevue/usetoast';

const toast = useToast();

const showTopLeft = () => {
    toast.add({ severity: 'info', summary: 'Info Message', detail: 'Message Content', group: 'tl', life: 3000 });
};

const showBottomLeft = () => {
    toast.add({ severity: 'warn', summary: 'Warn Message', detail: 'Message Content', group: 'bl', life: 3000 });
};

const showBottomRight = () => {
    toast.add({ severity: 'success', summary: 'Success Message', detail: 'Message Content', group: 'br', life: 3000 });
};
</script>

Multiple messages are displayed by passing an array to the show method.


<template>
    <Toast />
    <div class="card flex justify-center">
        <Button label="Multiple" @click="showMultiple()" />
    </div>
</template>

<script setup lang="ts">
import Toast from '@/volt/Toast.vue';
import Button from '@/volt/Button.vue';
import { useToast } from 'primevue/usetoast';

const toast = useToast();

const showMultiple = () => {
    toast.add({ severity: 'success', summary: 'Success', detail: 'Message Content', life: 3000 });
    toast.add({ severity: 'info', summary: 'Info', detail: 'Message Content', life: 3050 });
    toast.add({ severity: 'warn', summary: 'Warn', detail: 'Message Content', life: 3100 });
    toast.add({ severity: 'error', summary: 'Error', detail: 'Message Content', life: 3150 });
};
</script>

A message disappears after the number of milliseconds defined in the life option. Omit the life option to make the message sticky.


<template>
    <Toast />
    <div class="card flex flex-wrap gap-2 justify-center">
        <Button @click="showSticky" label="Sticky" />
        <SecondaryButton label="Clear" @click="clear()" />
    </div>
</template>

<script setup lang="ts">
import Toast from '@/volt/Toast.vue';
import Button from '@/volt/Button.vue';
import SecondaryButton from '@/volt/SecondaryButton.vue';
import { useToast } from 'primevue/usetoast';

const toast = useToast();

const showSticky = () => {
    toast.add({ severity: 'info', summary: 'Sticky Message', detail: 'Message Content'});
}

const clear = () => {
    toast.removeAllGroups();
}
</script>

Custom content inside a message is defined with the message template.


<template>
    <Toast position="bottom-center" group="bc" @close="onClose">
        <template #message="slotProps">
            <div class="flex flex-col items-start flex-auto">
                <div class="flex items-center gap-2">
                    <Avatar image="https://primefaces.org/cdn/primevue/images/avatar/amyelsner.png" shape="circle" />
                    <span class="font-bold">Amy Elsner</span>
                </div>
                <div class="font-medium text-lg my-4">{{ slotProps.message.summary }}</div>
                <Button size="small" label="Reply" severity="success" @click="onReply()"></Button>
            </div>
        </template>
    </Toast>
    <div class="card flex justify-center">
        <Button @click="showTemplate" label="View" />
    </div>
</template>

<script setup lang="ts">
import Toast from '@/volt/Toast.vue';
import Avatar from '@/volt/Avatar.vue';
import Button from '@/volt/Button.vue';
import { useToast } from 'primevue/usetoast';
import { ref } from 'vue';

const toast = useToast();
const visible = ref(false);

const showTemplate = () => {
    if (!visible.value) {
        toast.add({ severity: 'success', summary: 'Can you send me the report?', group: 'bc' });
        visible.value = true;
    }
};

const onReply = () => {
    toast.removeGroup('bc');
    visible.value = false;
};

const onClose = () => {
    visible.value = false;
};
</script>

Headless mode is enabled by defining a container slot along with the custom severity that lets you implement entire toast UI instead of the default elements.


<template>
    <Toast position="top-center" group="headless" @close="visible = false">
        <template #container="{ message, closeCallback }">
            <section class="flex flex-col p-4 gap-4 w-full bg-primary/70 rounded-xl">
                <div class="flex items-center gap-5">
                    <i class="pi pi-cloud-upload text-white dark:text-black text-2xl"></i>
                    <span class="font-bold text-base text-white dark:text-black">{{ message.summary }}</span>
                </div>
                <div class="flex flex-col gap-2">
                    <ProgressBar :value="progress" :showValue="false" :style="{ height: '4px' }" pt:value:class="!bg-primary-50 dark:!bg-primary-900" class="!bg-primary/80"></ProgressBar>
                    <label class="text-sm font-bold text-white dark:text-black">{{ progress }}% uploaded</label>
                </div>
                <div class="flex gap-4 mb-4 justify-end">
                    <Button label="Another Upload?" size="small" @click="closeCallback"></Button>
                    <Button label="Cancel" size="small" @click="closeCallback"></Button>
                </div>
            </section>
        </template>
    </Toast>
    <div class="card flex justify-center">
        <Button @click="show" label="View" />
    </div>
</template>

<script setup lang="ts">
import Toast from '@/volt/Toast.vue';
import Button from '@/volt/Button.vue';
import ProgressBar from '@/volt/ProgressBar.vue';
import { useToast } from 'primevue/usetoast';

const toast = useToast();
const visible = ref(false);
const progress = ref(0);
const interval = ref();

onUnmounted(() => {
    if (interval.value) {
        clearInterval(interval.value);
    }
})

const show = () => {
    if (!visible.value) {
        toast.add({ severity: 'custom', summary: 'Uploading your files.', group: 'headless', styleClass: 'backdrop-blur-lg rounded-2xl' });
        visible.value = true;
        progress.value = 0;

        if (interval.value) {
            clearInterval(interval.value);
        }

        interval.value = setInterval(() => {
            if (progress.value <= 100) {
                progress.value = progress.value + 20;
            }

            if (progress.value >= 100) {
                progress.value = 100;
                clearInterval(interval.value);
            }
        }, 1000);
    }
};
</script>