Imperative snackbar API for toast notifications from anywhere in the app.
Introduction
useSnackbarAction opens toast notifications imperatively. Call open() with title, description, and optional actions instead of declaring <Snackbar> in your template.
Stack position, max visible count, and default timeout are configured on BridgeSnackbarHost via BridgeUIHosts.
Use the framework selector in the site header to switch between React and Vue.
Import
import { BridgeUIHosts } from "@bridge-ui/vue/Actions";
import { useSnackbarAction } from "@bridge-ui/vue/Actions";import { BridgeUIHosts } from "@bridge-ui/react/Actions";
import { useSnackbarAction } from "@bridge-ui/react/Actions";Setup
Mount BridgeUIHosts with the snackbar host inside BridgeUIProvider or snackbars will not appear.
Configure host defaults on BridgeUIHosts inside BridgeUIProvider:
<BridgeUIHosts :snackbar="{ max: 3, timeout: 5000, position: 'bottom-center' }">
<!-- routes / pages -->
</BridgeUIHosts><BridgeUIHosts snackbar={{ max: 3, timeout: 5000, position: "bottom-center" }}>
{children}
</BridgeUIHosts>Basic usage
<script setup lang="ts">
import { useSnackbarAction } from "@bridge-ui/vue/Actions";
import { Button } from "@bridge-ui/vue/Components/Button";
const snackbar = useSnackbarAction();
const open = () => {
snackbar.open({
title: "Saved",
description: "Changes stored successfully.",
});
};
</script>
<template>
<Button v-on:click="open">Open snackbar</Button>
</template>
import { useSnackbarAction } from "@bridge-ui/react/Actions";
import { Button } from "@bridge-ui/react/Components/Button";
const Basic = () => {
const snackbar = useSnackbarAction();
const open = () => {
snackbar.open({
title: "Saved",
description: "Changes stored successfully.",
});
};
return <Button onClick={open}>Open snackbar</Button>;
};
export default Basic;
Options
Override per-entry color, padding, rounded, and transition on open().
<script setup lang="ts">
import { useSnackbarAction } from "@bridge-ui/vue/Actions";
import { Button } from "@bridge-ui/vue/Components/Button";
const snackbar = useSnackbarAction();
const open = () => {
snackbar.open({
rounded: "2xl",
color: "success",
padding: "small",
transition: "fade",
title: "Custom shell",
description: "color, padding, rounded, and transition from open().",
});
};
</script>
<template>
<Button v-on:click="open">Open with options</Button>
</template>
import { useSnackbarAction } from "@bridge-ui/react/Actions";
import { Button } from "@bridge-ui/react/Components/Button";
const Options = () => {
const snackbar = useSnackbarAction();
const open = () => {
snackbar.open({
rounded: "2xl",
color: "success",
padding: "small",
transition: "fade",
title: "Custom shell",
description: "color, padding, rounded, and transition from open().",
});
};
return <Button onClick={open}>Open with options</Button>;
};
export default Options;
Actions
Footer actions support accept and reject buttons with optional onClick handlers.
<script setup lang="ts">
import { useSnackbarAction } from "@bridge-ui/vue/Actions";
import { Button } from "@bridge-ui/vue/Components/Button";
const snackbar = useSnackbarAction();
const open = () => {
snackbar.open({
color: "error",
title: "Item deleted",
actions: {
reject: { label: "Dismiss" },
accept: {
label: "Undo",
onClick: () => {
snackbar.open({
color: "success",
title: "Restored",
});
},
},
},
});
};
</script>
<template>
<Button v-on:click="open">Open with actions</Button>
</template>
import { useSnackbarAction } from "@bridge-ui/react/Actions";
import { Button } from "@bridge-ui/react/Components/Button";
const Actions = () => {
const snackbar = useSnackbarAction();
const open = () => {
snackbar.open({
color: "error",
title: "Item deleted",
actions: {
reject: { label: "Dismiss" },
accept: {
label: "Undo",
onClick: () => {
snackbar.open({
color: "success",
title: "Restored",
});
},
},
},
});
};
return <Button onClick={open}>Open with actions</Button>;
};
export default Actions;
rightButtons
Set rightButtons: true to render actions in a vertical column on the right side of the snackbar.
<script setup lang="ts">
import { useSnackbarAction } from "@bridge-ui/vue/Actions";
import { Button } from "@bridge-ui/vue/Components/Button";
const snackbar = useSnackbarAction();
const open = () => {
snackbar.open({
color: "warning",
rightButtons: true,
title: "Right actions",
description: "Actions render in a vertical column on the right.",
actions: {
accept: { label: "Retry" },
reject: { label: "Cancel" },
},
});
};
</script>
<template>
<Button v-on:click="open">Open rightButtons</Button>
</template>
import { useSnackbarAction } from "@bridge-ui/react/Actions";
import { Button } from "@bridge-ui/react/Components/Button";
const RightButtons = () => {
const snackbar = useSnackbarAction();
const open = () => {
snackbar.open({
color: "warning",
rightButtons: true,
title: "Right actions",
description: "Actions render in a vertical column on the right.",
actions: {
accept: { label: "Retry" },
reject: { label: "Cancel" },
},
});
};
return <Button onClick={open}>Open rightButtons</Button>;
};
export default RightButtons;
close / closeTop / closeAll / stack
Snackbars stack up to the host max. Use close(id), closeTop(), or closeAll() to dismiss programmatically.
<script setup lang="ts">
import { useSnackbarAction } from "@bridge-ui/vue/Actions";
import { Button } from "@bridge-ui/vue/Components/Button";
import { ref } from "vue";
const snackbar = useSnackbarAction();
const lastId = ref<null | string>(null);
const openStack = () => {
snackbar.open({
color: "info",
title: "Stack layer 1",
});
const secondId = snackbar.open({
color: "primary",
title: "Stack layer 2",
});
lastId.value = secondId;
};
</script>
<template>
<div class="flex flex-wrap gap-2">
<Button
variant="outline"
:disabled="!lastId"
v-on:click="lastId && snackbar.close(lastId)"
>
close(lastId)
</Button>
<Button variant="outline" v-on:click="snackbar.closeTop()">
closeTop()
</Button>
<Button variant="outline" v-on:click="snackbar.closeAll()">
closeAll()
</Button>
<Button variant="outline" v-on:click="openStack">
Open 2-layer stack
</Button>
</div>
</template>
import { useSnackbarAction } from "@bridge-ui/react/Actions";
import { Button } from "@bridge-ui/react/Components/Button";
import { useState } from "react";
const Stack = () => {
const snackbar = useSnackbarAction();
const [lastId, setLastId] = useState<null | string>(null);
const openStack = () => {
snackbar.open({
color: "info",
title: "Stack layer 1",
});
const secondId = snackbar.open({
color: "primary",
title: "Stack layer 2",
});
setLastId(secondId);
};
return (
<div className="flex flex-wrap gap-2">
<Button
variant="outline"
disabled={!lastId}
onClick={() => {
if (!lastId) {
return;
}
snackbar.close(lastId);
}}
>
close(lastId)
</Button>
<Button variant="outline" onClick={() => snackbar.closeTop()}>
closeTop()
</Button>
<Button variant="outline" onClick={() => snackbar.closeAll()}>
closeAll()
</Button>
<Button variant="outline" onClick={openStack}>
Open 2-layer stack
</Button>
</div>
);
};
export default Stack;
Callbacks
onClose and onClosed fire when a snackbar is dismissed by the user, timer, or imperative API.
<script setup lang="ts">
import { useSnackbarAction } from "@bridge-ui/vue/Actions";
import { Button } from "@bridge-ui/vue/Components/Button";
import { ref } from "vue";
const onCloseCount = ref(0);
const onClosedCount = ref(0);
const snackbar = useSnackbarAction();
const open = () => {
snackbar.open({
title: "Callbacks",
onClose: () => {
onCloseCount.value += 1;
},
onClosed: () => {
onClosedCount.value += 1;
},
description:
"Dismiss via close button or timer for onClose, then onClosed after animation.",
});
};
</script>
<template>
<div class="flex flex-col gap-4">
<p class="text-secondary-500 text-sm">
onClose / onClosed counts: {{ onCloseCount }} / {{ onClosedCount }}
</p>
<Button v-on:click="open">Open with callbacks</Button>
</div>
</template>
import { useSnackbarAction } from "@bridge-ui/react/Actions";
import { Button } from "@bridge-ui/react/Components/Button";
import { useState } from "react";
const Callbacks = () => {
const snackbar = useSnackbarAction();
const [onCloseCount, setOnCloseCount] = useState(0);
const [onClosedCount, setOnClosedCount] = useState(0);
const open = () => {
snackbar.open({
title: "Callbacks",
onClose: () => {
setOnCloseCount((count) => count + 1);
},
onClosed: () => {
setOnClosedCount((count) => count + 1);
},
description:
"Dismiss via close button or timer for onClose, then onClosed after animation.",
});
};
return (
<div className="flex flex-col gap-4">
<p className="text-secondary-500 text-sm">
onClose / onClosed counts: {onCloseCount} / {onClosedCount}
</p>
<Button onClick={open}>Open with callbacks</Button>
</div>
);
};
export default Callbacks;
update
Patch an open snackbar with update(id, { props }) to change title, color, or actions without reopening.
<script setup lang="ts">
import { useSnackbarAction } from "@bridge-ui/vue/Actions";
import { Button } from "@bridge-ui/vue/Components/Button";
import { ref } from "vue";
const snackbar = useSnackbarAction();
const updateDemoId = ref<null | string>(null);
const open = () => {
const id = snackbar.open({
color: "secondary",
title: "Before update",
description: "Use the buttons below to call update().",
});
updateDemoId.value = id;
};
const patchProps = () => {
if (!updateDemoId.value) {
return;
}
snackbar.update(updateDemoId.value, {
props: {
color: "success",
title: "After props update",
description: "Title and color patched via update({ props }).",
},
});
};
const patchActions = () => {
if (!updateDemoId.value) {
return;
}
snackbar.update(updateDemoId.value, {
props: {
actions: {
accept: { label: "Got it" },
},
},
});
};
</script>
<template>
<div class="flex flex-wrap gap-2">
<Button v-on:click="open">Open update demo</Button>
<Button variant="outline" v-on:click="patchProps" :disabled="!updateDemoId">
update props
</Button>
<Button
variant="outline"
:disabled="!updateDemoId"
v-on:click="patchActions"
>
update actions
</Button>
</div>
</template>
import { useSnackbarAction } from "@bridge-ui/react/Actions";
import { Button } from "@bridge-ui/react/Components/Button";
import { useState } from "react";
const Update = () => {
const snackbar = useSnackbarAction();
const [updateDemoId, setUpdateDemoId] = useState<null | string>(null);
const open = () => {
const id = snackbar.open({
color: "secondary",
title: "Before update",
description: "Use the buttons below to call update().",
});
setUpdateDemoId(id);
};
const patchProps = () => {
if (!updateDemoId) {
return;
}
snackbar.update(updateDemoId, {
props: {
color: "success",
title: "After props update",
description: "Title and color patched via update({ props }).",
},
});
};
const patchActions = () => {
if (!updateDemoId) {
return;
}
snackbar.update(updateDemoId, {
props: {
actions: {
accept: { label: "Got it" },
},
},
});
};
return (
<div className="flex flex-wrap gap-2">
<Button onClick={open}>Open update demo</Button>
<Button variant="outline" onClick={patchProps} disabled={!updateDemoId}>
update props
</Button>
<Button variant="outline" onClick={patchActions} disabled={!updateDemoId}>
update actions
</Button>
</div>
);
};
export default Update;
API
| Method / property | Description |
|---|---|
| open(options) | Opens a snackbar. Returns an entry id. |
| close(id) | Closes the entry with the given id. |
| closeTop() | Closes the topmost entry. |
| closeAll() | Closes every open snackbar. |
| update(id, patch) | Patches props on an open entry. |
| isOpen(id) | Whether the entry is currently open. |
| stackSize | Number of open snackbar entries. |
open options
| Option | Type | Description |
|---|---|---|
| title | string | Headline text. |
| description | string | Body text. |
| color | SnackbarColor | Semantic tint. |
| padding | SnackbarPadding | Content padding preset. |
| rounded | SnackbarRounded | Border radius of the snackbar panel. |
| transition | SnackbarTransition | Enter/leave animation. |
| actions | { accept?, reject? } | Footer action buttons. |
| rightButtons | boolean | Render actions in a vertical column on the right. |
| onClose | () => void | Called when a dismiss starts. |
| onClosed | () => void | Called after the leave animation. |