Varsel

An opinionated toast component for Svelte 5.

GitHub

Installation

Install Varsel with your package manager and import the bundled stylesheet once. The package ships its own design tokens, so varsel/styles.css is enough to get the default look.

Install package

pnpm install varsel

Import styles

@import "varsel/styles.css";

Usage

Render VarselToaster near the root of your app so every route shares one toast host. Call toast() with a string for quick feedback or with an object when you need a title, description, action, duration, variant, or callbacks.

<script lang="ts">
	import { VarselToaster } from "varsel";

	let { children } = $props();
</script>

{@render children?.()}
<VarselToaster />

Positions & layout

Toasts default to bottom-center, but each notification can choose any of the six viewport anchors. Stacks are calculated per position, and swipe gestures mirror automatically for the chosen edge.

<script lang="ts">
	import { toast } from "varsel";

	function showToast() {
		toast({
			title: "Toast pinned to top-left",
			description: "Each anchor keeps its own stack.",
			position: "top-left",
		});
	}
</script>

<button onclick={showToast}>top-left</button>

Variants & styling

Use default, success, warning, destructive, or info to match the tone of the event. The bundled CSS is driven by variables for colors, shadows, radius, and easing, so you can override the theme without changing the API.

<script lang="ts">
	import { toast } from "varsel";

	function showToast() {
		toast({
			title: "default toast",
			description: "Pick the tone that matches the event.",
			variant: "default",
		});
	}
</script>

<button onclick={showToast}>Try default</button>

Timing & lifecycle

Varsel auto-dismisses toasts after five seconds by default and pauses while users hover, focus, or swipe the stack. Set duration per toast, use 0 for persistent notifications, and keep the returned id when you need to dismiss a toast later.

<script lang="ts">
	import { toast } from "varsel";

	function showShortToast() {
		toast({
			title: "Saved",
			description: "Done!",
			duration: 1500,
		});
	}
</script>

<button onclick={showShortToast}>Short toast</button>

Actions & callbacks

Add one action button when a toast should let the user respond immediately. onAutoClose and onDismiss run after the exit animation, while showClose: false hides the close button and disables swipe dismissal.

<script lang="ts">
	import { toast } from "varsel";

	function showActionToast() {
		toast({
			title: "Email scheduled",
			description: "Will send in ten minutes.",
			action: {
				label: "Undo",
				onClick: () => {
					console.log("Email cancelled");
				},
			},
		});
	}
</script>

<button onclick={showActionToast}>Try the action toast</button>

Promises & async

Use toast.promise to keep users informed while an async task is loading, succeeds, or fails. The loading state stays open with a spinner, and each state can provide its own copy, variant, duration, action, and close behavior.

<script lang="ts">
	import { toast } from "varsel";

	const mockDeploy = () =>
		new Promise<string>((resolve) => {
			setTimeout(() => {
				resolve("Production now serves build #542.");
			}, 1200);
		});

	function runDeployment() {
		toast.promise(mockDeploy(), {
			loading: {
				title: "Deploying build",
				description: "Packing assets and running health checks...",
			},
			success: (message) => ({
				title: "Deployment complete",
				description: message,
				variant: "success",
			}),
			error: {
				title: "Deployment failed",
				description: "Something went wrong while talking to the server.",
			},
		});
	}
</script>

<button onclick={runDeployment}>Simulate success</button>

Headless & Custom

Use toast.custom when you want Varsel's lifecycle and animations with fully custom markup. Your component receives the toast id, the full toast object, and any componentProps, so it can render and dismiss itself however it needs.

<script lang="ts">
	import HeadlessToast from "$lib/components/HeadlessToast.svelte";
	import { toast } from "varsel";

	function showCustomToast() {
		toast.custom(HeadlessToast, {
			duration: 100000,
			componentProps: {
				title: "This is a headless toast",
				description:
					"You have full control of styles and markup, while still having the animations.",
				button: {
					label: "Reply",
					onClick: () => console.log("Reply clicked"),
				},
			},
		});
	}
</script>

<button onclick={showCustomToast}>Render toast</button>
<script lang="ts">
	import { toast as varselToast, type PositionedToast } from "varsel";

	type Props = {
		id: string;
		toast: PositionedToast;
		title: string;
		description: string;
		button: {
			label: string;
			onClick: () => void;
		};
	};

	let { id, title, description, button }: Props = $props();
</script>

<div
	class="mx-auto flex w-full max-w-91 items-center rounded-lg bg-white p-4 shadow-lg ring-1 ring-black/5 dark:bg-zinc-900 dark:ring-white/10"
>
	<div class="flex flex-1 items-center">
		<div class="w-full">
			<p class="text-sm font-medium text-gray-900 dark:text-gray-100">{title}</p>
			<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">{description}</p>
		</div>
	</div>
	<div class="ml-5 shrink-0">
		<button
			class="cursor-pointer rounded bg-indigo-50 px-3 py-1 text-sm font-semibold text-indigo-600 hover:bg-indigo-100 dark:bg-indigo-950/50 dark:text-indigo-400 dark:hover:bg-indigo-900/50"
			onclick={() => {
				button.onClick();
				varselToast.dismiss(id);
			}}
		>
			{button.label}
		</button>
	</div>
</div>

API Reference

The public surface is intentionally small: VarselToaster, toast(), helper methods, and the ToastData object. Configure defaults on the toaster, override behavior per toast, and use returned ids with toast.dismiss or toast.dismissAll when you need manual control.

VarselToaster props

PropTypeDefaultDescription
position
ToastPosition
bottom-centerDefault position for all toasts.
visibleToasts
number
3Maximum number of toasts visible in the stack at once.
expand
boolean
trueWhether the toast stack expands when hovered.
duration
number
5000Default duration in milliseconds before a toast automatically closes.
closeButton
boolean
trueWhether to show the close button on toasts by default.
pauseOnHover
boolean
trueWhether to pause the auto-close timer while hovering over the toast.
offset
numberstring
undefinedDistance from the edge of the viewport.
dir
ltrrtlauto
autoText direction for accessibility and layout.
expandedGap
number
12Vertical gap in pixels between toasts when the stack is expanded.

toast() methods

MethodSignatureDescription
toast(data: string | ToastInput) => stringCreates a default toast and returns its id. Pass a stable id on the payload to upsert an existing toast in place.
toast.success(data: string | ToastVariantInput) => stringCreates a success variant toast.
toast.warning(data: string | ToastVariantInput) => stringCreates a warning variant toast.
toast.error(data: string | ToastVariantInput) => stringCreates a destructive variant toast.
toast.info(data: string | ToastVariantInput) => stringCreates an info variant toast.
toast.promise(promise: Promise<T>, options: ToastPromiseOptions) => Promise<T>Updates one toast automatically as a promise loads, resolves, or rejects. Returns the underlying promise so callers can await the original value or rejection.
toast.custom(component: Component, options?: ToastInput) => stringRenders a custom Svelte component as a toast.
toast.update(id: string, data: string | Partial<ToastData>) => voidRefreshes an existing toast in place. Pass a string as shorthand for { description }.
toast.dismiss(id: string) => voidProgrammatically dismisses a toast by id.
toast.dismissAll() => voidDismisses all active toasts.

ToastData fields

FieldTypeDescription
id
string
Optional stable id. When provided and a matching active toast exists, the call updates it in place instead of creating a duplicate.
title
string
The main title of the notification.
description
string
Detailed message or description.
duration
number
Overrides the default duration for this toast; Infinity keeps it open.
showClose
boolean
Controls close button visibility and swipe dismissal for this toast.
variant
defaultsuccesswarningdestructiveinfo
The visual style of the toast.
action
{ label: string, onClick: () => void }
Renders an action button.
onAutoClose
() => void
Runs after the toast finishes its timer-based exit.
onDismiss
() => void
Runs after manual dismissal by button, swipe, toast.dismiss, or toast.dismissAll.
position
ToastPosition
Overrides the global position for this toast.
priority
lowhigh
Announcement priority for assistive tech. "high" promotes the toast to alertdialog with assertive aria-live and adds a visually-hidden role=alert mirror so screen readers read it immediately.
className
string
Additional CSS classes for the toast container.
component
Component
Internal custom component rendered by toast.custom.
componentProps
Record<string, any>
Props passed to the custom component.