themtfy
v2.1.0
Published
Lightweight, framework-free toast notifications for the web
Readme
Themtfy
A framework-free, lightweight toast notification library for JavaScript/TypeScript. Works with vanilla JS, React, Vue, Svelte, Angular, Solid, Preact, Astro, Web Components, and any other frontend framework.
- Installation
- Quick Start
- Toast Types
- API Reference
- Configuration
- Programmatic Control
- Promise Toasts
- Loading State
- Actions
- Queue Management
- Deduplication
- Custom Containers
- Theming
- Accessibility
- SSR Safety
- Legacy API
- Migration Guide
Installation
npm install themtfyyarn add themtfypnpm add themtfyTypeScript + CSS imports: If your
tsconfig.jsondoesn't include"vite/client"types and you see an error onimport "themtfy/style.css", add an ambient declaration to your project (e.g.env.d.ts):declare module "*.css";
Quick Start
import { toast } from "themtfy";
import "themtfy/style.css";
toast.success("Saved", "Your changes have been saved.");
toast.error("Error", "Something went wrong.");
toast.warning("Warning", "Please review your input.");
toast.info("Info", "A new version is available.");Minimal
toast.success("Saved!");With Body
toast.success("Saved", "Your changes have been saved.");Object Form
toast.show({
title: "Uploading",
body: "Please wait...",
type: "info",
autoClose: false,
});Toast Types
toast.success(title, body?)
toast.error(title, body?)
toast.warning(title, body?)
toast.info(title, body?)
toast.loading(title, body?)Each type has an associated icon and color:
| Type | Icon | Color |
| --------- | --------- | ------ |
| success | Checkmark | Green |
| error | X | Red |
| warning | Triangle | Yellow |
| info | Circle-i | Blue |
| loading | Spinner | Blue |
| default | Bell | Gray |
API Reference
toast.show(options)
Show a toast notification.
const id = toast.show({
title: "Hello",
body: "World",
type: "info",
position: "top-right",
autoClose: 5000,
canClose: true,
showProgress: false,
pauseOnHover: true,
pauseOnFocus: true,
pauseOnVisibilityChange: true,
dedupeKey: "my-key",
icon: "info", // or false, or HTMLElement
action: {
label: "Undo",
onClick: () => restore(),
},
});Returns: string (toast ID)
toast.update(id, options)
Update an existing toast.
toast.update(id, {
title: "Complete",
body: "Upload finished.",
type: "success",
progress: 100,
});toast.dismiss(id)
Dismiss a specific toast.
toast.dismiss(id);toast.dismissAll()
Dismiss all active toasts.
toast.dismissAll();toast.get(id)
Get the state of a specific toast.
const state = toast.get(id);
// Returns: ToastState | undefinedtoast.getAll()
Get all active toasts.
const states = toast.getAll();
// Returns: ToastState[]toast.configure(options)
Configure global defaults.
toast.configure({
defaults: {
position: "bottom-right",
autoClose: 4000,
showProgress: true,
},
maxToasts: 5,
maxQueue: 20,
theme: "system",
});Configuration
Global Defaults
toast.configure({
defaults: {
position: "top-right",
autoClose: 5000,
canClose: true,
showProgress: false,
pauseOnHover: true,
pauseOnFocus: true,
pauseOnVisibilityChange: true,
},
});Manager Instance
For isolated configurations, use createThemtfy():
import { createThemtfy } from "themtfy";
const manager = createThemtfy({
maxToasts: 3,
maxQueue: 10,
defaults: {
position: "bottom-right",
autoClose: 3000,
},
theme: "dark",
container: document.getElementById("my-container"),
});
manager.show({ title: "Hello" });
manager.success("Saved");Programmatic Control
Dismiss
const id = toast.show({ title: "Hello" });
toast.dismiss(id);Update
const id = toast.show({ title: "Uploading", type: "info" });
// Update after 50%
toast.update(id, {
body: "50% complete",
progress: 50,
});
// Update when done
toast.update(id, {
title: "Complete",
type: "success",
body: "Upload finished.",
progress: 100,
});Pause/Resume
Pause auto-close on hover, focus, or tab visibility:
toast.show({
title: "Installing",
body: "Please wait...",
pauseOnHover: true,
pauseOnFocus: true,
pauseOnVisibilityChange: true,
});Promise Toasts
Show loading, success, or error states based on a promise.
const data = await toast.promise(fetch("/api/data"), {
loading: { title: "Loading...", body: "Fetching data..." },
success: (data) => ({
title: "Loaded",
body: `Found ${data.count} items`,
}),
error: (error) => ({
title: "Failed",
body: error.message || "An error occurred",
}),
});
// data is the resolved value
console.log(data);Static Handlers
const data = await toast.promise(fetch("/api/data"), {
loading: { title: "Loading..." },
success: { title: "Loaded" },
error: { title: "Failed" },
});Loading State
Loading toasts don't auto-close by default.
const id = toast.loading("Uploading", "0%");
// Simulate progress
let progress = 0;
const interval = setInterval(() => {
progress += 25;
toast.update(id, {
body: `${progress}%`,
progress,
});
if (progress >= 100) {
clearInterval(interval);
toast.update(id, {
type: "success",
title: "Complete",
body: "Upload finished.",
autoClose: 3000,
});
}
}, 1000);Actions
Add action buttons to toasts.
toast.show({
title: "Item deleted",
body: "The item was moved to trash.",
action: {
label: "Undo",
onClick: () => restoreItem(),
},
});Link Action
toast.show({
title: "Post published",
body: "Your post is now live.",
action: {
label: "View post",
href: "/posts/123",
},
});Disable Auto-Dismiss on Action
toast.show({
title: "Confirm action",
body: "Are you sure?",
action: {
label: "Yes",
onClick: () => confirmAction(),
closeOnAction: false, // Toast won't dismiss after clicking
},
});Queue Management
Limit concurrent toasts and queue overflow.
const manager = createThemtfy({
maxToasts: 3, // Max 3 visible toasts
maxQueue: 20, // Max 20 queued toasts
});
manager.show({ title: "Toast 1" });
manager.show({ title: "Toast 2" });
manager.show({ title: "Toast 3" });
manager.show({ title: "Toast 4" }); // Queued
manager.show({ title: "Toast 5" }); // Queued
// Dismiss the first toast - Toast 4 will be promoted
manager.dismiss(firstToastId);
// Clear the queue
manager.clearQueue();Deduplication
Prevent duplicate notifications using dedupeKey.
// First call - creates toast
const id1 = toast.show({
title: "Network error",
dedupeKey: "network-error",
});
// Second call - updates existing toast
toast.show({
title: "Network restored",
dedupeKey: "network-error",
});
// id1 === id2 (same toast updated)Custom Containers
Render toasts in a specific container.
const manager = createThemtfy({
container: document.getElementById("my-notifications"),
});Or with a function:
const manager = createThemtfy({
container: () => document.getElementById("my-container"),
});Theming
CSS Custom Properties
:root {
--themtfy-bg: #ffffff;
--themtfy-color: #1a1a1a;
--themtfy-radius: 8px;
--themtfy-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
--themtfy-success: #36cb8c;
--themtfy-error: #ff4e64;
--themtfy-warning: #ffbc3c;
--themtfy-info: #4d7eff;
}Dark Theme
toast.configure({ theme: "dark" });System Theme
toast.configure({ theme: "system" }); // Follows prefers-color-schemeAccessibility
Themtfy includes accessibility features by default:
- Roles:
role="status"for normal toasts,role="alert"for errors - ARIA Live:
aria-live="polite"for normal,aria-live="assertive"for errors - Keyboard: Close button is focusable with Tab, dismisses on Enter/Space
- Reduced Motion: Respects
prefers-reduced-motion - Screen Readers: Decorative icons are
aria-hidden="true"
Focus Management
By default, toasts do not steal focus from other elements.
SSR Safety
Themtfy is safe to import in SSR environments (Next.js, Nuxt, Astro, etc.):
// Safe - no DOM access at import time
import { toast } from "themtfy";
// DOM is only touched when show() is called
function MyComponent() {
const handleClick = () => {
toast.show({ title: "Hello" }); // DOM touched here
};
return <button onClick={handleClick}>Show Toast</button>;
}Legacy API
The v1 API still works for backward compatibility:
import Themtfy from "themtfy";
new Themtfy({
title: "Hello",
body: "World",
position: "top-right",
autoClose: 5000,
});Migration Guide
v1 to v2
Before:
import Themtfy from "themtfy";
new Themtfy({ title: "Hello", body: "World" });After:
import { toast } from "themtfy";
toast.show({ title: "Hello", body: "World" });
// Or even simpler:
toast.success("Hello", "World");Option Changes:
| v1 | v2 |
| ------------------------- | -------------------------------- |
| variation | type |
| distanceX / distanceY | offset: { x, y } |
| onClose callback | toast.on("dismiss", ...) event |
Browser Support
Modern browsers with ES2020 support:
- Chrome 80+
- Firefox 75+
- Safari 13.1+
- Edge 80+
Bundle Size
| Format | Size | Gzipped | | ------ | ------- | ------- | | ESM | 38.5 KB | 10.4 KB | | CJS | 32.2 KB | 9.7 KB | | CSS | 8.0 KB | 1.7 KB |
License
MIT
