Toast notification portaled to the viewport.
Introduction
Snackbars (also known as toasts) appear temporarily and float above the UI to provide brief, non-critical updates—saved successfully, message sent, or undo available.
Snackbar vs Alert: Snackbars have a fixed viewport position, a high stacking order, and auto-dismiss by default—they break out of the document flow. Alert messages are usually inline, part of the page layout, and remain visible until removed. Use Snackbar for transient process feedback; use Alert for persistent status on a form, page section, or settings panel. You can nest an Alert inside a Snackbar when you need both toast behavior and semantic severity styling.
Import
import { Snackbar } from "@bridge-ui/vue/Components/Snackbar";import { Snackbar } from "@bridge-ui/react/Components/Snackbar";Basic usage
Control visibility with v-model or :model-value with @show-change. Pair with a trigger such as a Button to open the snackbar.
Control visibility with show and onShowChange. Pair with a trigger such as a Button to open the snackbar.
<script setup lang="ts">
import { ref } from "vue";
import { Button } from "@bridge-ui/vue/Components/Button";
import { Snackbar } from "@bridge-ui/vue/Components/Snackbar";
const open = ref(false);
</script>
<template>
<div class="w-full">
<Button v-on:click="open = true">Show snackbar</Button>
<Snackbar
title="Saved"
v-model="open"
description="Your changes were saved successfully."
/>
</div>
</template>
import { useState } from "react";
import { Button } from "@bridge-ui/react/Components/Button";
import { Snackbar } from "@bridge-ui/react/Components/Snackbar";
export default function SnackbarBasic() {
const [open, setOpen] = useState(false);
return (
<div className="w-full">
<Button onClick={() => setOpen(true)}>Show snackbar</Button>
<Snackbar
show={open}
title="Saved"
onShowChange={setOpen}
description="Your changes were saved successfully."
/>
</div>
);
}
Colors
Use the color prop to tint the default icon and progress bar. Available colors: primary, secondary, success, info, warning, error, and dark.
<script setup lang="ts">
import { ref } from "vue";
import { Button } from "@bridge-ui/vue/Components/Button";
import { Snackbar } from "@bridge-ui/vue/Components/Snackbar";
type Color =
"dark" | "info" | "error" | "primary" | "success" | "warning" | "secondary";
const open = ref(false);
const color = ref<Color>("primary");
function show(nextColor: Color) {
color.value = nextColor;
open.value = true;
}
</script>
<template>
<div class="flex w-full flex-wrap gap-2">
<Button
size="sm"
variant="flat"
color="primary"
v-on:click="show('primary')"
>
primary
</Button>
<Button
size="sm"
variant="flat"
color="secondary"
v-on:click="show('secondary')"
>
secondary
</Button>
<Button
size="sm"
variant="flat"
color="success"
v-on:click="show('success')"
>
success
</Button>
<Button size="sm" color="info" variant="flat" v-on:click="show('info')">
info
</Button>
<Button
size="sm"
variant="flat"
color="warning"
v-on:click="show('warning')"
>
warning
</Button>
<Button size="sm" color="error" variant="flat" v-on:click="show('error')">
error
</Button>
<Button size="sm" color="dark" variant="flat" v-on:click="show('dark')">
dark
</Button>
<Snackbar
v-model="open"
:color="color"
:title="`${color} snackbar`"
description="Semantic color applied to the icon and progress bar."
/>
</div>
</template>
import { useState } from "react";
import { Button } from "@bridge-ui/react/Components/Button";
import { Snackbar } from "@bridge-ui/react/Components/Snackbar";
type Color =
"dark" | "info" | "error" | "primary" | "success" | "warning" | "secondary";
export default function SnackbarColors() {
const [open, setOpen] = useState(false);
const [color, setColor] = useState<Color>("primary");
return (
<div className="flex w-full flex-wrap gap-2">
<Button
size="sm"
variant="flat"
color="primary"
onClick={() => {
setColor("primary");
setOpen(true);
}}
>
primary
</Button>
<Button
size="sm"
variant="flat"
color="secondary"
onClick={() => {
setColor("secondary");
setOpen(true);
}}
>
secondary
</Button>
<Button
size="sm"
variant="flat"
color="success"
onClick={() => {
setColor("success");
setOpen(true);
}}
>
success
</Button>
<Button
size="sm"
color="info"
variant="flat"
onClick={() => {
setColor("info");
setOpen(true);
}}
>
info
</Button>
<Button
size="sm"
variant="flat"
color="warning"
onClick={() => {
setColor("warning");
setOpen(true);
}}
>
warning
</Button>
<Button
size="sm"
color="error"
variant="flat"
onClick={() => {
setColor("error");
setOpen(true);
}}
>
error
</Button>
<Button
size="sm"
color="dark"
variant="flat"
onClick={() => {
setColor("dark");
setOpen(true);
}}
>
dark
</Button>
<Snackbar
show={open}
color={color}
onShowChange={setOpen}
title={`${color} snackbar`}
description="Semantic color applied to the icon and progress bar."
/>
</div>
);
}
Positions
Anchor the snackbar on the viewport with the position prop. Available positions: top-start, top-center, top-end, bottom-start, bottom-center (default), and bottom-end. Ignored when teleportTo={false}.
<script setup lang="ts">
import { ref } from "vue";
import { Button } from "@bridge-ui/vue/Components/Button";
import { Snackbar } from "@bridge-ui/vue/Components/Snackbar";
type Position =
| "top-end"
| "top-start"
| "bottom-end"
| "top-center"
| "bottom-start"
| "bottom-center";
const open = ref(false);
const position = ref<Position>("bottom-center");
function show(nextPosition: Position) {
position.value = nextPosition;
open.value = true;
}
</script>
<template>
<div class="flex w-full flex-wrap gap-2">
<Button size="sm" variant="flat" v-on:click="show('top-start')">
top-start
</Button>
<Button size="sm" variant="flat" v-on:click="show('top-center')">
top-center
</Button>
<Button size="sm" variant="flat" v-on:click="show('top-end')">
top-end
</Button>
<Button size="sm" variant="flat" v-on:click="show('bottom-start')">
bottom-start
</Button>
<Button size="sm" variant="flat" v-on:click="show('bottom-center')">
bottom-center
</Button>
<Button size="sm" variant="flat" v-on:click="show('bottom-end')">
bottom-end
</Button>
<Snackbar
v-model="open"
:position="position"
title="Positioned snackbar"
:description="`Anchored at ${position}.`"
/>
</div>
</template>
import { useState } from "react";
import { Button } from "@bridge-ui/react/Components/Button";
import { Snackbar } from "@bridge-ui/react/Components/Snackbar";
type Position =
| "top-end"
| "top-start"
| "bottom-end"
| "top-center"
| "bottom-start"
| "bottom-center";
export default function SnackbarPositions() {
const [open, setOpen] = useState(false);
const [position, setPosition] = useState<Position>("bottom-center");
return (
<div className="flex w-full flex-wrap gap-2">
<Button
size="sm"
variant="flat"
onClick={() => {
setPosition("top-start");
setOpen(true);
}}
>
top-start
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setPosition("top-center");
setOpen(true);
}}
>
top-center
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setPosition("top-end");
setOpen(true);
}}
>
top-end
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setPosition("bottom-start");
setOpen(true);
}}
>
bottom-start
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setPosition("bottom-center");
setOpen(true);
}}
>
bottom-center
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setPosition("bottom-end");
setOpen(true);
}}
>
bottom-end
</Button>
<Snackbar
show={open}
position={position}
onShowChange={setOpen}
title="Positioned snackbar"
description={`Anchored at ${position}.`}
/>
</div>
);
}
Duration
Set duration (in milliseconds) to auto-dismiss the snackbar. Pass false to disable the timer. Enable progressbar to show a countdown indicator while the timer is active.
<script setup lang="ts">
import { ref } from "vue";
import { Button } from "@bridge-ui/vue/Components/Button";
import { Snackbar } from "@bridge-ui/vue/Components/Snackbar";
const open = ref(false);
const duration = ref<false | number>(5000);
function show(nextDuration: false | number) {
duration.value = nextDuration;
open.value = true;
}
</script>
<template>
<div class="flex w-full flex-wrap gap-2">
<Button size="sm" variant="flat" v-on:click="show(3000)">3 seconds</Button>
<Button size="sm" variant="flat" v-on:click="show(8000)">8 seconds</Button>
<Button size="sm" variant="flat" v-on:click="show(false)">
No auto-dismiss
</Button>
<Snackbar
progressbar
color="info"
v-model="open"
:duration="duration"
title="Auto dismiss"
:description="
duration === false
? 'Close manually — the timer is disabled.'
: `Dismisses after ${duration / 1000} seconds.`
"
/>
</div>
</template>
import { useState } from "react";
import { Button } from "@bridge-ui/react/Components/Button";
import { Snackbar } from "@bridge-ui/react/Components/Snackbar";
export default function SnackbarDuration() {
const [open, setOpen] = useState(false);
const [duration, setDuration] = useState<false | number>(5000);
return (
<div className="flex w-full flex-wrap gap-2">
<Button
size="sm"
variant="flat"
onClick={() => {
setDuration(3000);
setOpen(true);
}}
>
3 seconds
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setDuration(8000);
setOpen(true);
}}
>
8 seconds
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setDuration(false);
setOpen(true);
}}
>
No auto-dismiss
</Button>
<Snackbar
progressbar
show={open}
color="info"
duration={duration}
title="Auto dismiss"
onShowChange={setOpen}
description={
duration === false
? "Close manually — the timer is disabled."
: `Dismisses after ${duration / 1000} seconds.`
}
/>
</div>
);
}
Transitions
Set transition to slide (default), fade, or none for enter/leave animation.
<script setup lang="ts">
import { ref } from "vue";
import { Button } from "@bridge-ui/vue/Components/Button";
import { Snackbar } from "@bridge-ui/vue/Components/Snackbar";
type Transition = "fade" | "none" | "slide";
const transitionOpen = ref<null | Transition>(null);
</script>
<template>
<div class="flex flex-col gap-4">
<div class="flex flex-wrap gap-2">
<Button size="sm" variant="outline" v-on:click="transitionOpen = 'none'">
none
</Button>
<Button size="sm" variant="outline" v-on:click="transitionOpen = 'fade'">
fade
</Button>
<Button size="sm" variant="outline" v-on:click="transitionOpen = 'slide'">
slide
</Button>
</div>
<Snackbar
v-if="transitionOpen"
:show="!!transitionOpen"
:transition="transitionOpen"
description="Enter/leave animation preset."
:title="`transition="${transitionOpen}"`"
v-on:show-change="(show) => !show && (transitionOpen = null)"
/>
</div>
</template>
import { useState } from "react";
import { Button } from "@bridge-ui/react/Components/Button";
import { Snackbar } from "@bridge-ui/react/Components/Snackbar";
type Transition = "fade" | "none" | "slide";
const SnackbarTransitions = () => {
const [transitionOpen, setTransitionOpen] = useState<null | Transition>(null);
return (
<div className="flex flex-col gap-4">
<div className="flex flex-wrap gap-2">
<Button
size="sm"
variant="outline"
onClick={() => setTransitionOpen("none")}
>
none
</Button>
<Button
size="sm"
variant="outline"
onClick={() => setTransitionOpen("fade")}
>
fade
</Button>
<Button
size="sm"
variant="outline"
onClick={() => setTransitionOpen("slide")}
>
slide
</Button>
</div>
<Snackbar
show={!!transitionOpen}
transition={transitionOpen ?? "slide"}
title={`transition="${transitionOpen}"`}
description="Enter/leave animation preset."
onShowChange={(show) => !show && setTransitionOpen(null)}
/>
</div>
);
};
export default SnackbarTransitions;
Rounded
Use the rounded prop to control border radius. Available values: none, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, and full (default: lg).
<script setup lang="ts">
import { ref } from "vue";
import type { SnackbarRounded } from "@bridge-ui/core";
import { Button } from "@bridge-ui/vue/Components/Button";
import { Snackbar } from "@bridge-ui/vue/Components/Snackbar";
type Rounded = keyof SnackbarRounded;
const open = ref(false);
const rounded = ref<Rounded>("lg");
function show(nextRounded: Rounded) {
rounded.value = nextRounded;
open.value = true;
}
</script>
<template>
<div class="flex w-full flex-wrap gap-2">
<Button size="sm" variant="flat" v-on:click="show('none')">none</Button>
<Button size="sm" variant="flat" v-on:click="show('sm')">sm</Button>
<Button size="sm" variant="flat" v-on:click="show('md')">md</Button>
<Button size="sm" variant="flat" v-on:click="show('lg')">lg</Button>
<Button size="sm" variant="flat" v-on:click="show('2xl')">2xl</Button>
<Button size="sm" variant="flat" v-on:click="show('full')">full</Button>
<Snackbar
v-model="open"
:duration="false"
:rounded="rounded"
:title="`rounded=${rounded}`"
description="Border radius applied to the snackbar panel."
/>
</div>
</template>
import { useState } from "react";
import type { SnackbarRounded } from "@bridge-ui/core";
import { Button } from "@bridge-ui/react/Components/Button";
import { Snackbar } from "@bridge-ui/react/Components/Snackbar";
type Rounded = keyof SnackbarRounded;
export default function SnackbarRounded() {
const [open, setOpen] = useState(false);
const [rounded, setRounded] = useState<Rounded>("lg");
return (
<div className="flex w-full flex-wrap gap-2">
<Button
size="sm"
variant="flat"
onClick={() => {
setRounded("none");
setOpen(true);
}}
>
none
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setRounded("sm");
setOpen(true);
}}
>
sm
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setRounded("md");
setOpen(true);
}}
>
md
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setRounded("lg");
setOpen(true);
}}
>
lg
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setRounded("2xl");
setOpen(true);
}}
>
2xl
</Button>
<Button
size="sm"
variant="flat"
onClick={() => {
setRounded("full");
setOpen(true);
}}
>
full
</Button>
<Snackbar
show={open}
duration={false}
rounded={rounded}
onShowChange={setOpen}
title={`rounded="${rounded}"`}
description="Border radius applied to the snackbar panel."
/>
</div>
);
}
Slots
Override icon, title, and description with slots for custom content.
<script setup lang="ts">
import { ref } from "vue";
import { Button } from "@bridge-ui/vue/Components/Button";
import { Icon } from "@bridge-ui/vue/Components/Icon";
import { Snackbar } from "@bridge-ui/vue/Components/Snackbar";
import { Info } from "@lucide/vue";
const slotsOpen = ref(false);
</script>
<template>
<div class="flex flex-col gap-4">
<Button v-on:click="slotsOpen = true">Show snackbar with slots</Button>
<Snackbar color="info" padding="large" v-model="slotsOpen">
<template #icon>
<Icon size="md" :icon="Info" />
</template>
<template #title>
<span class="font-bold">Custom title slot</span>
</template>
<template #description>
Override icon, title, and description with slots.
</template>
</Snackbar>
</div>
</template>
import { useState } from "react";
import { Button } from "@bridge-ui/react/Components/Button";
import { Icon } from "@bridge-ui/react/Components/Icon";
import { Snackbar } from "@bridge-ui/react/Components/Snackbar";
import { Info } from "lucide-react";
const SnackbarSlots = () => {
const [slotsOpen, setSlotsOpen] = useState(false);
return (
<div className="flex flex-col gap-4">
<Button onClick={() => setSlotsOpen(true)}>
Show snackbar with slots
</Button>
<Snackbar
color="info"
padding="large"
show={slotsOpen}
onShowChange={setSlotsOpen}
slots={{
icon: <Icon size="md" icon={Info} />,
title: <span className="font-bold">Custom title slot</span>,
description: "Override icon, title, and description with slots.",
}}
/>
</div>
);
};
export default SnackbarSlots;
Related components
Accessibility
- Snackbar sets role="status" and aria-live="polite" by default so screen readers announce messages without interrupting current tasks.
- Users can dismiss the snackbar with the close button when closeButton is enabled (default).
- Provide enough duration for users to read the message before auto-dismiss.
- Keep titles and descriptions concise. Avoid relying on color alone to convey meaning—pair color with clear text.
Anatomy
<div> <!-- root (portaled, role="status") -->
<div> <!-- content -->
<svg> <!-- icon or img -->
<div> <!-- title -->
<div> <!-- description -->
<div> <!-- actions slot (optional) -->
<button> <!-- close button -->
</div>
<div> <!-- progressbar (when duration is set) -->
</div>Target internal parts with the classes prop and override slots for custom icon, title, description, or action content.
API
| Prop / event | Type | Default | Description |
|---|---|---|---|
| v-model | boolean | — | Two-way binding for visibility. |
| @close | — | — | Emitted when the snackbar requests to close (close button, Escape, or timer). Not emitted when the parent sets v-model to false directly. |
| @show-change | (show: boolean) => void | — | Emitted when visibility should change. Use with :model-value for controlled state. |
| @leave-complete | — | — | Emitted after the leave transition when v-model is already false. Used internally by BridgeSnackbarHost. |
| Prop | Type | Default | Description |
|---|---|---|---|
| show | boolean | — | Whether the overlay is visible. Use with onShowChange for controlled state. |
| onShowChange | (show: boolean) => void | — | Called when visibility changes. |
| onClose | () => void | — | Called when the snackbar requests to close (close button, Escape, or timer). Not called when the parent sets show={false} directly. |
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | — | Headline text. |
| description | string | — | Body text below the title. |
| color | SnackbarColor | "primary" | Tint color for the default icon. |
| position | SnackbarPosition | "bottom-center" | Viewport anchor when portaled. |
| duration | number | false | 5000 | Auto-dismiss delay in ms. false disables the timer. |
| progressbar | boolean | true | Show countdown progress bar when duration is set. |
| closeButton | boolean | true | Whether to show the close button. |
| icon | IconSource | null | — | Custom icon or null to hide. |
| img | string | — | Avatar image URL (shown instead of icon when set). |
| padding | SnackbarPadding | "medium" | Padding for the content area. |
| rounded | SnackbarRounded | "lg" | Border radius of the snackbar panel. |
| transition | SnackbarTransition | "slide" | Enter/leave animation preset. |
| teleportTo | string | false | "body" | Portal target. false renders inline. |
| classes | SnackbarClasses | — | Classes for internal parts. |
| customProps | SnackbarCustomProps | — | Props forwarded to internal parts. |
| slots | SnackbarSlots | — | React slots: icon, title, description, actions. |