
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@opentui-ui/toast
Advanced tools
bun add @opentui-ui/toast
import { toast, ToasterRenderable } from "@opentui-ui/toast";
// 1. Add the toaster to your app (one line!)
ctx.root.add(new ToasterRenderable(ctx));
// 2. Show toasts from anywhere!
toast("Hello World");
That's it! No providers, no context, no configuration required.
// Toast Types
toast("message"); // default
toast.success("message"); // green checkmark
toast.error("message"); // red X
toast.warning("message"); // yellow warning
toast.info("message"); // blue info
toast.loading("message"); // animated spinner
// Common Patterns
toast("msg", { description: "details" }); // Two-line toast
toast("msg", { duration: Infinity }); // Persistent (manual dismiss)
toast("msg", { action: { label: "Undo", onClick: fn } }); // With button
// Dismiss
const id = toast("Hello");
toast.dismiss(id); // Dismiss one
toast.dismiss(); // Dismiss all
// Update existing toast
const id = toast.loading("Uploading...");
toast.success("Done!", { id }); // Updates in place
toast("Default notification");
toast.success("Operation completed!");
toast.error("Something went wrong");
toast.warning("Please check your input");
toast.info("Did you know?");
toast.loading("Processing...");
toast.success("File uploaded", {
description: "Your file has been saved to the cloud",
});
The toast.promise() API automatically shows loading, success, and error states:
toast.promise(fetchData(), {
loading: "Fetching data...",
success: "Data loaded successfully!",
error: "Failed to load data",
});
// With dynamic messages
toast.promise(saveUser(data), {
loading: "Saving user...",
success: (user) => `${user.name} has been saved`,
error: (err) => `Error: ${err.message}`,
});
Add interactive buttons to your toasts:
toast("File deleted", {
action: {
label: "Undo",
onClick: () => restoreFile(),
},
});
Update an existing toast by passing its ID:
const id = toast.loading("Uploading...");
// Later...
toast.success("Upload complete!", { id });
// Or on error
toast.error("Upload failed", { id });
// Dismiss a specific toast
const id = toast("Hello");
toast.dismiss(id);
// Dismiss all toasts
toast.dismiss();
Use the built-in TOAST_DURATION presets for consistent, readable duration values:
import { toast, TOAST_DURATION } from "@opentui-ui/toast";
// Quick confirmation (2s)
toast.success("Copied!", { duration: TOAST_DURATION.SHORT });
// Standard duration (4s) - this is the default
toast("Hello", { duration: TOAST_DURATION.DEFAULT });
// Important message (6s)
toast.warning("Check your settings", { duration: TOAST_DURATION.LONG });
// Critical information (10s)
toast.error("Connection lost", { duration: TOAST_DURATION.EXTENDED });
// Manual dismiss only
toast.info("Click to continue", { duration: TOAST_DURATION.PERSISTENT });
| Preset | Duration | Use Case |
|---|---|---|
SHORT | 2000ms | Brief confirmations |
DEFAULT | 4000ms | Standard notifications |
LONG | 6000ms | Important messages |
EXTENDED | 10000ms | Critical information |
PERSISTENT | Infinity | Requires manual dismissal |
You can also pass any number in milliseconds:
// Custom duration (in milliseconds)
toast("This disappears in 10 seconds", {
duration: 10000,
});
// Persistent toast (won't auto-dismiss)
toast("I'll stay until dismissed", {
duration: Infinity,
});
Optional theme presets are available via a separate import. These override the built-in defaults with alternative visual styles.
import { ToasterRenderable } from "@opentui-ui/toast";
import { minimal } from "@opentui-ui/toast/themes";
const toaster = new ToasterRenderable(ctx, minimal);
| Theme | Description |
|---|---|
minimal | Clean and unobtrusive, no borders |
monochrome | Grayscale only, no colors |
Spread a theme and override specific options:
import { minimal } from "@opentui-ui/toast/themes";
const toaster = new ToasterRenderable(ctx, {
...minimal,
position: "bottom-right",
stackingMode: "stack",
});
import { themes } from "@opentui-ui/toast/themes";
// Access all themes
themes.minimal;
themes.monochrome;
import type { ToasterTheme } from "@opentui-ui/toast/themes";
Customize the toaster appearance and behavior:
const toaster = new ToasterRenderable(ctx, {
// Position on screen
position: "bottom-right", // 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'
// Gap between toasts (terminal rows)
gap: 1,
// How to handle multiple toasts
stackingMode: "single", // 'single' | 'stack'
// Max visible toasts in stack mode
visibleToasts: 3,
// Show close button on toasts
closeButton: false,
// Maximum width for toasts
maxWidth: 60,
// Offset from screen edges
offset: {
top: 1,
right: 2,
bottom: 1,
left: 2,
},
// Custom icons
icons: {
success: "✓",
error: "✗",
warning: "⚠",
info: "ℹ",
loading: "◌",
close: "×",
},
// Toast styling and duration options
toastOptions: {
style: {
/* base styles */
},
duration: 4000,
success: {
style: {
/* overrides */
},
duration: 3000,
},
// ... other types
},
});
| Option | Type | Default | Description |
|---|---|---|---|
position | Position | "bottom-right" | Position on screen |
gap | number | 1 | Gap between toasts (terminal rows) |
stackingMode | StackingMode | "single" | How to handle multiple toasts: "single" or "stack" |
visibleToasts | number | 3 | Max visible toasts in stack mode |
closeButton | boolean | false | Show close button on toasts |
maxWidth | number | 60 | Maximum width for toasts (terminal columns) |
offset | ToasterOffset | { top: 1, right: 2, bottom: 1, left: 2 } | Offset from screen edges |
icons | Partial<ToastIcons> | false | - | Custom icons for each toast type, or false to disable |
toastOptions | ToastOptions | - | Default toast options (styles, duration, per-type overrides) |
Configure toast styles using the toastOptions prop:
const toaster = new ToasterRenderable(ctx, {
toastOptions: {
// Base styles applied to all toasts
style: {
backgroundColor: "#1a1a1a",
foregroundColor: "#ffffff",
borderColor: "#333333",
borderStyle: "rounded", // 'single' | 'double' | 'rounded' | 'heavy'
paddingX: 1,
paddingY: 0,
},
// Default duration for all toasts
duration: 4000,
// Per-type overrides
success: {
style: { borderColor: "#22c55e" },
duration: 3000,
},
error: {
style: { borderColor: "#ef4444" },
duration: 6000,
},
warning: {
style: { borderColor: "#f59e0b" },
},
info: {
style: { borderColor: "#3b82f6" },
},
loading: {
style: { borderColor: "#6b7280" },
},
},
});
| Property | Type | Default | Description |
|---|---|---|---|
border | boolean | BorderSides[] | true | Border configuration. true = all sides, false = none, or array like ["left", "right"] |
borderColor | string | "#333333" | Border color (hex, rgb, or named) |
borderStyle | BorderStyle | "single" | Border style: "single" | "double" | "rounded" | "heavy" |
customBorderChars | BorderCharacters | - | Custom border characters (overrides borderStyle) |
minHeight | number | 3 | Minimum height in terminal rows |
maxWidth | number | - | Maximum width in terminal columns |
minWidth | number | - | Minimum width in terminal columns |
padding | number | - | Uniform padding (all sides) |
paddingX | number | 1 | Horizontal padding (left + right) |
paddingY | number | 0 | Vertical padding (top + bottom) |
paddingTop | number | - | Top padding |
paddingBottom | number | - | Bottom padding |
paddingLeft | number | - | Left padding |
paddingRight | number | - | Right padding |
backgroundColor | string | "#1a1a1a" | Background color |
foregroundColor | string | "#ffffff" | Text/foreground color |
mutedColor | string | "#6b7280" | Muted text color (for descriptions) |
iconColor | string | - | Icon color (defaults to borderColor) |
For full control over border rendering, use customBorderChars to define each border character:
const toaster = new ToasterRenderable(ctx, {
toastOptions: {
style: {
border: ["left", "right"],
customBorderChars: {
topLeft: "",
topRight: "",
bottomLeft: "",
bottomRight: "",
horizontal: " ",
vertical: "┃",
topT: "",
bottomT: "",
leftT: "",
rightT: "",
cross: "",
},
},
},
});
This is useful for creating unique border styles, like a vertical bar accent:
// Vertical bar on left and right only
border: ["left", "right"],
customBorderChars: {
vertical: "┃",
// Other characters can be empty strings
topLeft: "", topRight: "", bottomLeft: "", bottomRight: "",
horizontal: " ", topT: "", bottomT: "", leftT: "", rightT: "", cross: "",
},
Override styles on individual toasts:
toast.success("Custom styled!", {
style: {
borderColor: "#8b5cf6",
backgroundColor: "#1e1b4b",
},
});
Choose from built-in icon sets based on terminal capabilities:
import {
DEFAULT_ICONS, // Unicode icons (default)
ASCII_ICONS, // ASCII-only for limited terminals
MINIMAL_ICONS, // Single character icons
EMOJI_ICONS, // Emoji icons
} from "@opentui-ui/toast";
// Use ASCII icons for terminals with limited Unicode support
const toaster = new ToasterRenderable(ctx, {
icons: ASCII_ICONS,
});
// Use emoji icons for terminals with good emoji support
const toaster = new ToasterRenderable(ctx, {
icons: EMOJI_ICONS,
});
The loading icon can be either a static string or an animated spinner configuration:
const toaster = new ToasterRenderable(ctx, {
icons: {
// Static loading icon (no animation)
loading: "...",
// Or animated spinner
loading: {
frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
interval: 80,
},
},
});
Some spinner examples:
// Dots spinner
{ frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"], interval: 80 }
// Circle spinner (default)
{ frames: ["◜", "◠", "◝", "◞", "◡", "◟"], interval: 100 }
// Simple ASCII spinner
{ frames: ["-", "\\", "|", "/"], interval: 100 }
// Bouncing bar
{ frames: ["[ ]", "[= ]", "[== ]", "[=== ]", "[ ===]", "[ ==]", "[ =]", "[ ]"], interval: 120 }
To disable icons entirely, set icons: false:
const toaster = new ToasterRenderable(ctx, {
icons: false,
});
Individual toasts can still override this by providing a custom icon:
// Icons are disabled globally, but this toast will show a custom icon
toast.success("Done!", { icon: "✓" });
toast(message, options?)Show a default toast.
toast.success(message, options?)Show a success toast with a checkmark icon.
toast.error(message, options?)Show an error toast with an X icon.
toast.warning(message, options?)Show a warning toast with a warning icon.
toast.info(message, options?)Show an info toast with an info icon.
toast.loading(message, options?)Show a loading toast with an animated spinner.
toast.promise(promise, options)Show a toast that updates based on promise state.
toast.dismiss(id?)Dismiss a specific toast by ID, or all toasts if no ID provided.
toast.getToasts()Get all currently active toasts.
toast.getHistory()Get all toasts ever created (including dismissed).
| Option | Type | Default | Description |
|---|---|---|---|
id | string | number | auto | Unique identifier for the toast |
description | string | (() => string) | - | Secondary text below the title |
duration | number | 4000 | Time in ms before auto-dismiss |
dismissible | boolean | true | Whether the toast can be dismissed |
icon | string | type-based | Custom icon to display |
action | { label, onClick } | - | Action button configuration |
closeButton | boolean | false | Show close button |
style | ToastStyle | - | Per-toast style overrides |
onDismiss | (toast) => void | - | Callback when dismissed |
onAutoClose | (toast) => void | - | Callback when auto-closed |
import { createCliRenderer } from "@opentui/core";
import { toast, ToasterRenderable } from "@opentui-ui/toast";
const renderer = await createCliRenderer();
// Add toaster
const toaster = new ToasterRenderable(renderer, {
position: "bottom-right",
});
renderer.root.add(toaster);
// Show some toasts
toast.success("Application started!");
setTimeout(() => {
toast.info("Press 'q' to quit");
}, 1000);
async function saveData(data: unknown) {
toast.promise(
fetch("/api/save", {
method: "POST",
body: JSON.stringify(data),
}),
{
loading: "Saving...",
success: "Saved!",
error: "Failed to save",
}
);
}
async function uploadFile(file: File) {
const id = toast.loading("Uploading...");
try {
const result = await upload(file);
toast.success(`Uploaded ${result.filename}`, { id });
} catch (error) {
toast.error("Upload failed", { id });
}
}
For React applications, use the Toaster component and useToasts hook:
import { Toaster, useToasts, toast } from "@opentui-ui/toast/react";
function App() {
return (
<>
<Toaster position="bottom-right" />
<MyComponent />
</>
);
}
function MyComponent() {
const { toasts } = useToasts();
useKeyboard((key) => {
if (key.name === "1") {
toast.success("Hello World");
}
});
return (
<box>
<text>Active toasts: {toasts.length}</text>
</box>
);
}
The useToasts hook provides reactive access to the current toast state, re-rendering your component whenever toasts are added, updated, or dismissed.
For Solid applications, use the Toaster component and useToasts hook:
import { Toaster, useToasts, toast } from "@opentui-ui/toast/solid";
function App() {
return (
<>
<Toaster position="bottom-right" />
<MyComponent />
</>
);
}
function MyComponent() {
const toasts = useToasts();
useKeyboard((key) => {
if (key.name === "1") {
toast.success("Hello World");
}
});
return (
<box>
<text>Active toasts: {toasts().length}</text>
</box>
);
}
The useToasts hook returns a reactive accessor that updates whenever toasts change. Call it as a function (toasts()) to access the current array.
Full TypeScript support with exported types:
import type {
Action, // Action button configuration
ExternalToast, // Options for toast() calls
Position, // Toaster position type
PromiseData, // Configuration for toast.promise()
SpinnerConfig, // Animated spinner configuration { frames, interval }
StackingMode, // Stacking mode ('single' | 'stack')
ToasterOffset, // Offset configuration for positioning
ToasterOptions, // Configuration for ToasterRenderable
ToastIcons, // Custom icon set type
ToastOptions, // Default toast options (styles, duration, per-type overrides)
ToastStyle, // Per-toast styling options
ToastType, // Toast type variants
TypeToastOptions, // Per-type options (style + duration)
} from "@opentui-ui/toast";
// Border types (for customBorderChars) come from @opentui/core
import type { BorderCharacters, BorderSides, BorderStyle } from "@opentui/core";
// Type guards
import { isAction, isSpinnerConfig } from "@opentui-ui/toast";
import { TOAST_DURATION } from "@opentui-ui/toast";
// Duration presets
TOAST_DURATION.SHORT; // 2000ms - brief confirmations
TOAST_DURATION.DEFAULT; // 4000ms - standard notifications
TOAST_DURATION.LONG; // 6000ms - important messages
TOAST_DURATION.EXTENDED; // 10000ms - critical information
TOAST_DURATION.PERSISTENT; // Infinity - manual dismiss only
Inspired by Sonner by Emil Kowalski.
MIT
FAQs
A beautiful toast library for terminal UIs built on OpenTUI
We found that @opentui-ui/toast demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.