Sonner
A toast notification component for displaying temporary messages
Configuration
Adjust toast settings for all demos below.
Toast Types
Different toast types for various notification scenarios.
1
2<script lang="ts">
3 import { Button, Sonner, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6</script>
7
8<Sonner />
9
10<div class="flex flex-wrap gap-3">
11 <Button
12 variant="outline"
13 onclick={() => toast("This is a default notification")}
14 >
15 Default
16 </Button>
17 <Button
18 color="success"
19 onclick={() => toast.success("Operation completed successfully!")}
20 >
21 Success
22 </Button>
23 <Button
24 color="danger"
25 onclick={() => toast.error("Something went wrong!")}
26 >
27 Error
28 </Button>
29 <Button
30 color="warning"
31 onclick={() => toast.warning("Please review your input")}
32 >
33 Warning
34 </Button>
35 <Button
36 color="info"
37 onclick={() => toast.info("Here's some useful information")}
38 >
39 Info
40 </Button>
41 <Button
42 variant="outline"
43 onclick={() => {
44 const id = toast.loading("Processing...");
45 setTimeout(() => {
46 toast.dismiss(id);
47 toast.success("Done!");
48 }, 2000);
49 }}
50 >
51 Loading
52 </Button>
53</div>Visual Variants
Four distinct styles to match your UI
Default — tinted backgrounds with type-colored borders
Bordered — clean background with colored left accent border
Filled — solid colored backgrounds for high contrast
Minimal — transparent background with subtle bottom border
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 let activeVariant: SonnerPrimitives.ToasterStyle = $state("default");
6
7 const { toast } = SonnerPrimitives;
8
9</script>
10
11<Sonner variant={activeVariant} />
12
13 <!-- Default -->
14<div class="space-y-8">
15 <div>
16 <h3 class="text-sm font-medium text-muted-foreground mb-3">Default — tinted backgrounds with type-colored borders</h3>
17 <div class="flex flex-wrap gap-2">
18 <Button color="success" variant="flat" onclick={() => { activeVariant = "default"; toast.success("Default variant success"); }}>
19 Success
20 </Button>
21 <Button color="danger" variant="flat" onclick={() => { activeVariant = "default"; toast.error("Default variant error"); }}>
22 Error
23 </Button>
24 <Button color="warning" variant="flat" onclick={() => { activeVariant = "default"; toast.warning("Default variant warning"); }}>
25 Warning
26 </Button>
27 <Button color="info" variant="flat" onclick={() => { activeVariant = "default"; toast.info("Default variant info"); }}>
28 Info
29 </Button>
30 </div>
31 </div>
32
33 <!-- Bordered -->
34 <div>
35 <h3 class="text-sm font-medium text-muted-foreground mb-3">Bordered — clean background with colored left accent border</h3>
36 <div class="flex flex-wrap gap-2">
37 <Button color="success" variant="bordered" onclick={() => { activeVariant = "bordered"; toast.success("Bordered variant success"); }}>
38 Success
39 </Button>
40 <Button color="danger" variant="bordered" onclick={() => { activeVariant = "bordered"; toast.error("Bordered variant error"); }}>
41 Error
42 </Button>
43 <Button color="warning" variant="bordered" onclick={() => { activeVariant = "bordered"; toast.warning("Bordered variant warning"); }}>
44 Warning
45 </Button>
46 <Button color="info" variant="bordered" onclick={() => { activeVariant = "bordered"; toast.info("Bordered variant info"); }}>
47 Info
48 </Button>
49 </div>
50 </div>
51
52 <!-- Filled -->
53 <div>
54 <h3 class="text-sm font-medium text-muted-foreground mb-3">Filled — solid colored backgrounds for high contrast</h3>
55 <div class="flex flex-wrap gap-2">
56 <Button color="success" variant="default" onclick={() => { activeVariant = "filled"; toast.success("Filled variant success"); }}>
57 Success
58 </Button>
59 <Button color="danger" variant="default" onclick={() => { activeVariant = "filled"; toast.error("Filled variant error"); }}>
60 Error
61 </Button>
62 <Button color="warning" variant="default" onclick={() => { activeVariant = "filled"; toast.warning("Filled variant warning"); }}>
63 Warning
64 </Button>
65 <Button color="info" variant="default" onclick={() => { activeVariant = "filled"; toast.info("Filled variant info"); }}>
66 Info
67 </Button>
68 </div>
69 </div>
70
71 <!-- Minimal -->
72 <div>
73 <h3 class="text-sm font-medium text-muted-foreground mb-3">Minimal — transparent background with subtle bottom border</h3>
74 <div class="flex flex-wrap gap-2">
75 <Button color="success" variant="outline" onclick={() => { activeVariant = "minimal"; toast.success("Minimal variant success"); }}>
76 Success
77 </Button>
78 <Button color="danger" variant="outline" onclick={() => { activeVariant = "minimal"; toast.error("Minimal variant error"); }}>
79 Error
80 </Button>
81 <Button color="warning" variant="outline" onclick={() => { activeVariant = "minimal"; toast.warning("Minimal variant warning"); }}>
82 Warning
83 </Button>
84 <Button color="info" variant="outline" onclick={() => { activeVariant = "minimal"; toast.info("Minimal variant info"); }}>
85 Info
86 </Button>
87 </div>
88 </div>
89</div>
90
91
92With Descriptions
Add descriptive text for more context.
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6</script>
7
8<Sonner />
9
10<div class="flex flex-wrap gap-3">
11 <Button
12 color="success"
13 onclick={() => toast.success("File uploaded", {
14 description: "Your document has been saved to the cloud successfully."
15 })}
16 >
17 Success with Description
18 </Button>
19 <Button
20 color="danger"
21 onclick={() => toast.error("Upload failed", {
22 description: "The file exceeds the maximum size limit of 10MB."
23 })}
24 >
25 Error with Description
26 </Button>
27 <Button
28 color="warning"
29 onclick={() => toast.warning("Low storage", {
30 description: "You are using 95% of your available storage."
31 })}
32 >
33 Warning with Description
34 </Button>
35</div>With Action Buttons
Add action and cancel buttons for interactive toasts.
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6</script>
7
8<Sonner />
9
10<div class="flex flex-wrap gap-3">
11 <Button
12 color="success"
13 variant="flat"
14 onclick={() => toast.success("Event created", {
15 description: "Your event has been scheduled.",
16 action: {
17 label: "View Event",
18 onClick: () => toast.info("Opening event details...")
19 }
20 })}
21 >
22 With Action
23 </Button>
24 <Button
25 color="danger"
26 variant="flat"
27 onclick={() => toast.error("Delete this item?", {
28 description: "This action cannot be undone.",
29 action: {
30 label: "Delete",
31 onClick: () => toast.success("Item deleted successfully")
32 },
33 cancel: {
34 label: "Cancel",
35 onClick: () => toast.info("Deletion cancelled")
36 }
37 })}
38 >
39 With Action & Cancel
40 </Button>
41</div>Promise Toasts
Automatically handle loading, success, and error states.
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6
7 function simulateAsyncOperation(shouldSucceed: boolean = true) {
8 return new Promise((resolve, reject) => {
9 setTimeout(() => {
10 if (shouldSucceed) {
11 resolve({ items: 42, name: "Report.pdf" });
12 } else {
13 reject(new Error("Network connection failed"));
14 }
15 }, 2000);
16 });
17 }
18</script>
19
20<Sonner />
21
22<div class="flex flex-wrap gap-3">
23 <Button
24 onclick={() => {
25 toast.promise(simulateAsyncOperation(true), {
26 loading: "Uploading file...",
27 success: (data) => `${data.name} uploaded successfully!`,
28 error: "Failed to upload file"
29 });
30 }}
31 >
32 Promise (Success)
33 </Button>
34 <Button
35 variant="outline"
36 onclick={() => {
37 toast.promise(simulateAsyncOperation(false), {
38 loading: "Connecting to server...",
39 success: "Connected!",
40 error: (err) => `Error: ${err.message}`
41 });
42 }}
43 >
44 Promise (Error)
45 </Button>
46</div>Duration & Persistence
Control how long toasts stay visible.
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6</script>
7
8<Sonner />
9
10<div class="flex flex-wrap gap-3">
11 <Button
12 variant="outline"
13 onclick={() => toast.info("Quick toast", { duration: 1000 })}
14 >
15 1 Second
16 </Button>
17 <Button
18 variant="outline"
19 onclick={() => toast.info("Normal toast", { duration: 4000 })}
20 >
21 4 Seconds
22 </Button>
23 <Button
24 variant="outline"
25 onclick={() => toast.info("Long toast", { duration: 10000 })}
26 >
27 10 Seconds
28 </Button>
29 <Button
30 color="warning"
31 onclick={() => toast.warning("Persistent notification", {
32 description: "This toast will not auto-dismiss.",
33 duration: Infinity
34 })}
35 >
36 Persistent (Infinity)
37 </Button>
38</div>Multiple Toasts
Stack multiple toasts with expand on hover.
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6</script>
7
8<Sonner />
9
10<div class="flex flex-wrap gap-3">
11 <Button
12 onclick={() => {
13 toast.success("First notification");
14 setTimeout(() => toast.info("Second notification"), 200);
15 setTimeout(() => toast.warning("Third notification"), 400);
16 setTimeout(() => toast.error("Fourth notification"), 600);
17 }}
18 >
19 Show 4 Toasts
20 </Button>
21 <Button
22 variant="destructive"
23 onclick={() => toast.dismiss()}
24 >
25 Dismiss All
26 </Button>
27</div>Custom Styling
Apply custom classes and styles to toasts.
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6</script>
7
8<Sonner />
9
10<div class="flex flex-wrap gap-3">
11 <Button
12 variant="outline"
13 onclick={() => toast.success("Custom styled toast", {
14 description: "This toast has custom border styling.",
15 class: "border-2 border-green-500"
16 })}
17 >
18 Custom Border
19 </Button>
20 <Button
21 variant="outline"
22 onclick={() => toast("Gradient background", {
23 description: "Beautiful gradient styling.",
24 style: "background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;"
25 })}
26 >
27 Gradient Style
28 </Button>
29</div>Important Toasts
Important toasts stay on top of the stack.
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6</script>
7<Sonner />
8<div class="flex flex-wrap gap-3">
9 <Button
10 color="danger"
11 onclick={() => {
12 toast.info("Regular notification 1");
13 toast.info("Regular notification 2");
14 toast.error("CRITICAL: System error detected!", {
15 important: true,
16 duration: 10000,
17 action: {
18 label: "Fix Now",
19 onClick: () => toast.success("Issue resolved!")
20 }
21 });
22 }}
23 >
24 Show Important Toast
25 </Button>
26</div>Sequential Updates
Update the same toast through multiple stages.
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6</script>
7<Sonner />
8<div class="flex flex-wrap gap-3">
9 <Button
10 onclick={async () => {
11 const id = toast.loading("Step 1: Validating data...");
12
13 await new Promise(r => setTimeout(r, 1500));
14 toast.loading("Step 2: Processing...", { id });
15
16 await new Promise(r => setTimeout(r, 1500));
17 toast.loading("Step 3: Saving to database...", { id });
18
19 await new Promise(r => setTimeout(r, 1500));
20 toast.success("All steps completed successfully!", { id });
21 }}
22 >
23 Multi-Step Process
24 </Button>
25</div>Real-world Examples
Common use cases for toast notifications.
Form Submission
Copy to Clipboard
Delete Confirmation
Network Status
Update Available
Settings Saved
1
2<script lang="ts">
3 import { Button, SonnerPrimitives } from "@kareyes/aether";
4
5 const { toast } = SonnerPrimitives;
6</script>
7<Sonner />
8<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
9 <div class="p-4 border rounded-lg space-y-3">
10 <h3 class="font-medium">Form Submission</h3>
11 <Button
12 class="w-full"
13 onclick={() => {
14 const id = toast.loading("Submitting form...");
15 setTimeout(() => {
16 toast.success("Form submitted successfully!", {
17 id,
18 description: "We'll get back to you within 24 hours."
19 });
20 }, 1500);
21 }}
22 >
23 Submit Form
24 </Button>
25 </div>
26
27 <div class="p-4 border rounded-lg space-y-3">
28 <h3 class="font-medium">Copy to Clipboard</h3>
29 <Button
30 class="w-full"
31 variant="outline"
32 onclick={() => {
33 navigator.clipboard?.writeText("Copied text!");
34 toast.success("Copied to clipboard!");
35 }}
36 >
37 Copy Text
38 </Button>
39 </div>
40
41 <div class="p-4 border rounded-lg space-y-3">
42 <h3 class="font-medium">Delete Confirmation</h3>
43 <Button
44 class="w-full"
45 color="danger"
46 onclick={() => {
47 toast.error("Delete this item?", {
48 description: "This cannot be undone.",
49 action: {
50 label: "Delete",
51 onClick: () => toast.success("Deleted!")
52 },
53 cancel: {
54 label: "Keep",
55 onClick: () => {}
56 },
57 duration: 10000
58 });
59 }}
60 >
61 Delete Item
62 </Button>
63 </div>
64 <div class="p-4 border rounded-lg space-y-3">
65 <h3 class="font-medium">Network Status</h3>
66 <Button
67 class="w-full"
68 variant="secondary"
69 onclick={() => {
70 toast.error("Connection lost", {
71 description: "Attempting to reconnect...",
72 duration: 3000,
73 });
74 setTimeout(() => {
75 toast.success("Connection restored!");
76 }, 3000);
77 }}
78 >
79 Simulate Offline
80 </Button>
81 </div>
82
83 <div class="p-4 border rounded-lg space-y-3">
84 <h3 class="font-medium">Update Available</h3>
85 <Button
86 class="w-full"
87 color="info"
88 variant="flat"
89 onclick={() => {
90 toast.info("Update available", {
91 description:
92 "A new version is ready to install.",
93 action: {
94 label: "Install Now",
95 onClick: () =>
96 toast.loading(
97 "Installing update...",
98 ),
99 },
100 duration: 10000,
101 });
102 }}
103 >
104 Check Updates
105 </Button>
106 </div>
107
108 <div class="p-4 border rounded-lg space-y-3">
109 <h3 class="font-medium">Settings Saved</h3>
110
111 <Button
112 class="w-full"
113 color="success"
114 variant="flat"
115 onclick={() => {
116 toast.success("Settings saved", {
117 description:
118 "Your preferences have been updated.",
119 action: {
120 label: "Undo",
121 onClick: () =>
122 toast.info("Changes reverted"),
123 },
124 });
125 }}
126 >
127 Save Settings
128 </Button>
129 </div>
130</div>Features
- 6 Toast Types: default, success, error, warning, info, loading
- Promise Toasts: Automatic loading → success/error states
- Rich Colors: Beautiful color-coded toasts for each type
- Custom Icons: Replace default icons with custom Svelte components
- Action Buttons: Add action and cancel buttons to toasts
- Customizable Position: 6 different positions on screen
- Dark Mode: Full dark mode support
- Accessible: Keyboard navigation and screen reader friendly
Import
<script lang="ts">
import { Toaster, toast } from "@kareyes/aether";
</script>
Setup
Add the Toaster component to your root layout (e.g., +layout.svelte):
<script lang="ts">
import { Toaster } from "@kareyes/aether";
</script>
<Toaster />
<slot />
Basic Usage
Simple Toasts
<script lang="ts">
import { toast } from "@kareyes/aether";
</script>
<button onclick={() => toast("Hello, world!")}>
Show Toast
</button>
Toast Variants
<script lang="ts">
import { toast } from "@kareyes/aether";
</script>
<!-- Success -->
<button onclick={() => toast.success("Changes saved successfully!")}>
Success
</button>
<!-- Error -->
<button onclick={() => toast.error("Something went wrong!")}>
Error
</button>
<!-- Warning -->
<button onclick={() => toast.warning("Please review your input")}>
Warning
</button>
<!-- Info -->
<button onclick={() => toast.info("New features available!")}>
Info
</button>
<!-- Loading -->
<button onclick={() => toast.loading("Processing...")}>
Loading
</button>
With Description
<script lang="ts">
import { toast } from "@kareyes/aether";
</script>
<button onclick={() => toast.success("File uploaded", {
description: "Your file has been uploaded successfully."
})}>
Upload File
</button>
With Actions
<button onclick={() => toast.warning("Delete item?", {
description: "This action cannot be undone.",
action: {
label: "Delete",
onClick: () => console.log("Deleted!")
},
cancel: {
label: "Cancel",
onClick: () => console.log("Cancelled")
}
})}>
Delete with Confirmation
</button>
Promise Toast
<script lang="ts">
import { toast } from "@kareyes/aether";
async function saveData() {
const promise = fetch("/api/save", { method: "POST" });
toast.promise(promise, {
loading: "Saving...",
success: "Data saved successfully!",
error: "Failed to save data"
});
}
</script>
Persistent Toast
<button onclick={() => toast.info("Important message", {
duration: Infinity,
dismissible: true
})}>
Persistent Toast
</button>
Dismiss Toast
<script lang="ts">
import { toast } from "@kareyes/aether";
let toastId: string | number;
function showToast() {
toastId = toast.loading("Processing...");
}
function dismissToast() {
toast.dismiss(toastId);
}
function dismissAll() {
toast.dismiss(); // No ID = dismiss all
}
</script>
Toaster Props
| Prop | Type | Default | Description |
|---|---|---|---|
position |
"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" |
"bottom-right" |
Position of toasts on screen |
expand |
boolean |
false |
Expand toasts on hover |
richColors |
boolean |
true |
Enable variant-specific colors |
closeButton |
boolean |
false |
Show close button on toasts |
duration |
number |
4000 |
Auto-dismiss duration in ms |
gap |
number |
14 |
Gap between toasts in pixels |
visibleToasts |
number |
3 |
Maximum visible toasts |
loadingIcon |
Snippet |
undefined |
Custom loading icon |
successIcon |
Snippet |
undefined |
Custom success icon |
errorIcon |
Snippet |
undefined |
Custom error icon |
infoIcon |
Snippet |
undefined |
Custom info icon |
warningIcon |
Snippet |
undefined |
Custom warning icon |
class |
string |
undefined |
Additional CSS classes |
Toast Options
| Option | Type | Description |
|---|---|---|
description |
string |
Toast description text |
duration |
number |
Override default duration (0 = no auto-dismiss) |
dismissible |
boolean |
Allow dismissing by click |
icon |
Component |
Custom icon component |
action |
{ label: string, onClick: () => void } |
Action button |
cancel |
{ label: string, onClick: () => void } |
Cancel button |
onDismiss |
(toast) => void |
Callback when dismissed |
onAutoClose |
(toast) => void |
Callback when auto-closed |
class |
string |
Custom class for toast |
descriptionClass |
string |
Custom class for description |
style |
string |
Inline styles |
position |
string |
Position override |
important |
boolean |
Important toast (stays on top) |
id |
string | number |
Custom toast ID |
Toast Types
Default
Basic toast with neutral styling.
toast("Default message");
Success
Green-themed toast for successful operations.
toast.success("Operation completed!");
Error
Red-themed toast for errors.
toast.error("Something went wrong!");
Warning
Yellow-themed toast for warnings.
toast.warning("Please check your input");
Info
Blue-themed toast for informational messages.
toast.info("New update available!");
Loading
Toast with a spinner, useful for async operations.
const id = toast.loading("Processing...");
// Later...
toast.dismiss(id);
toast.success("Done!");
Promise
Automatically handles loading, success, and error states.
toast.promise(asyncFunction(), {
loading: "Loading...",
success: (data) => `Loaded ${data.count} items`,
error: (err) => `Error: ${err.message}`
});
Custom Icons
Via Toaster Props
<Toaster>
{#snippet successIcon()}
<MyCustomSuccessIcon class="size-4" />
{/snippet}
{#snippet errorIcon()}
<MyCustomErrorIcon class="size-4" />
{/snippet}
</Toaster>
Via Toast Options
import MyIcon from "./MyIcon.svelte";
toast.success("Custom icon!", {
icon: MyIcon
});
Positions
<!-- Top positions -->
<Toaster position="top-left" />
<Toaster position="top-center" />
<Toaster position="top-right" />
<!-- Bottom positions -->
<Toaster position="bottom-left" />
<Toaster position="bottom-center" />
<Toaster position="bottom-right" />
Advanced Examples
Custom Styled Toast
toast.success("Custom styles!", {
class: "border-2 border-green-500",
descriptionClass: "text-green-600",
style: "background: linear-gradient(to right, #f0fff4, #dcfce7);"
});
Important Toast
toast.error("Critical error!", {
important: true,
duration: Infinity,
action: {
label: "Fix Now",
onClick: () => fixError()
}
});
Sequential Updates
const id = toast.loading("Step 1: Validating...");
await validate();
toast.loading("Step 2: Processing...", { id });
await process();
toast.loading("Step 3: Saving...", { id });
await save();
toast.success("All done!", { id });
Accessibility
- Toast notifications are announced to screen readers
- Keyboard navigation support
- Focus management for action buttons
- Sufficient color contrast for all variants
Styling
The toaster uses Tailwind CSS classes that integrate with your theme:
--normal-bg: Background color for default toasts--normal-text: Text color for default toasts--normal-border: Border color for default toasts
Rich color variants use semantic colors (green for success, red for error, etc.) with automatic dark mode adjustments.