@get-set/gs-toast
v1.0.2
Published
Get-Set Toast
Maintainers
Readme
@get-set/gs-toast
A dependency-free, fully accessible (WAI-ARIA) toast / notification / snackbar plugin available in two flavours from one codebase:
- Native / vanilla JS — a callable
window.GSToast(opts)global with attachedsuccess / error / warning / info / loading / update / dismiss / dismissAll / isActivestatics. It also accepts a CSS selector or DOM element as its first argument, and is exposed as a jQuery plugin ($(sel).GSToast(params)) and anHTMLElement.prototypemethod (el.GSToast(params)). - React — a
gsToastimperative helper (identical surface to the native global), an optional declarative<GSToast />forwardRefcomponent, and a<GSToastContainer />mount point.
Both surfaces drive one shared engine (actions/, constants/, helpers/,
types/) and fire into the same auto-created, positioned containers, so a
toast raised from React and one raised from the native global stack together.
Every option works in both targets (except the few tagged React-only below).
Features
- Imperative API —
gsToast(opts)returns a stable id; per-type helpersgsToast.success / error / warning / info / loading, plusupdate(id, opts),dismiss(id),dismissAll(), andisActive(id). - 8 position anchors —
top-left,top-center,top-right,bottom-left,bottom-center,bottom-right, plus the vertically-centeredtop-center-left/top-center-rightedge anchors. A positioned container is auto-created per position on first use and removed when empty. - 7-entry animation catalog —
slide(edge-aware),fade,zoom,flip,pop,bounce,none, with a separateexitAnimationand a smoothly settling stack (toasts collapse their height as they leave so the others glide into place). - 6 semantic types —
default,success,error,warning,info,loading, each with an auto SVG icon + accent color (theloadingtype ships an animated spinner and is sticky by default). duration/autoClosetimer with a depleting progress bar,pauseOnHover, andpauseOnFocusLoss(the whole stack pauses when the tab is hidden).falsemakes a toast sticky.maxVisiblestack + queue — cap how many show at once; the rest queue and drain automatically as toasts dismiss.newestOnTopand a configurablegap.- Swipe / drag to dismiss (
swipeToDismiss,swipeThreshold), a×closeButton, optionaldismissOnClick, and action buttons withprimary/ghostvariants. update(id, opts)rewrites a live toast in place (e.g. aloadingtoast becomes asuccessone) with a subtle flash.- Light / dark / auto theming, an
accentoverride, RTL, customicon/html,prefers-reduced-motionhonored, per-partcustomClassoverrides, and a React-only scoped-stylesgsxprop. - Callbacks —
onShow(id),onClose(id, reason),onClick(id).
Compatibility
| Target | Requirement |
|---|---|
| React (gsToast, <GSToast>, <GSToastContainer>) | React 16.8+ (Hooks + forwardRef), and 17 / 18 / 19. React is an optional peer dependency — you only need it for the component/helper. |
| Native / vanilla (window.GSToast) | No framework. Any modern evergreen browser. Optional jQuery ($(...).GSToast(...)) and HTMLElement.prototype.GSToast(...) adapters are registered automatically by the bundle. |
| TypeScript | First-class — type declarations (.d.ts) ship in the package. |
| SSR / Next.js | Safe to import server-side (DOM work is client-gated). Toasts are browser-only, so fire them from a Client Component ('use client') / an effect or event handler. |
The peer range is
^16.8.0 || ^17.0 || ^18.0 || ^19.0, withreact/react-dommarked optional so the native build has zero dependencies.
Project layout
GSToast.ts # native entry (webpack -> dist-js/bundle.js, window global)
components/GSToast.tsx # React: gsToast helper, <GSToast>, <GSToastContainer> (tsc -> dist/)
actions/ # shared engine (api/engine/containers/registry/animate/…)
constants/ # animations, icons, default params
helpers/ # layout, content, ui helpers
types/ # Params / Ref / GSToastApi / Window augmentation
components/styles/ # SCSS + compiled CSS + CSS-as-TS (runtime injection for React)
styles/ # SCSS + compiled CSS (for <link> use by the native build)Install
npm install @get-set/gs-toastBuild & test
npm install
npm run build # builds both targets
npm run build:js # native bundle -> dist-js/bundle.js
npm run build:react # React + types -> dist/
npm test # vitestSee example.html for a runnable native showcase exercising the type, position,
and animation catalogs, the queue, progress bar, action buttons, and theming.
Usage — Native JS
The primary form is the options object:
<link rel="stylesheet" href="node_modules/@get-set/gs-toast/styles/GSToast.css" />
<script src="node_modules/@get-set/gs-toast/dist-js/bundle.js"></script>
<script>
// Fire a toast (returns its id):
const id = GSToast({
type: 'success',
title: 'Saved',
message: 'Your changes are live.',
position: 'top-right',
duration: 5000,
onClose: (id, reason) => console.log('closed', id, reason)
});
// Per-type shorthands:
GSToast.success('Profile updated');
GSToast.error('Something went wrong', { duration: false });
GSToast.warning('Low disk space');
GSToast.info('New version available');
// Loading -> resolve in place (sticky until updated):
const t = GSToast.loading('Uploading…');
setTimeout(() => GSToast.update(t, { type: 'success', message: 'Done!', duration: 3000 }), 1500);
// Dismiss:
GSToast.dismiss(id);
GSToast.dismissAll();
GSToast.isActive(id); // boolean
</script>Selector / element form (jQuery + prototype contract)
The factory also accepts a CSS selector string or a DOM element/collection
as its first argument — the resolved element's innerHTML becomes the toast
message (merged under any options passed second). This is what powers the jQuery
and prototype adapters, which call window.GSToast(thisElement, params):
// selector string:
GSToast('#flash', { type: 'info' });
// raw element:
GSToast(document.querySelector('#flash'), { type: 'info' });
// jQuery (registered by the bundle banner):
$('#flash').GSToast({ type: 'info' });
// HTMLElement.prototype (registered by the bundle banner):
document.querySelector('#flash').GSToast({ type: 'info' });Instance registry — window.GSToastConfigue
Every live toast is registered in the window.GSToastConfigue registry (note the
canonical misspelling). Look up the live Ref by its id:
const id = GSToast({ reference: 'main', message: 'Hi' });
const ref = window.GSToastConfigue.instance('main'); // -> the live Ref
ref.update({ message: 'Updated' });
ref.dismiss();
window.GSToastConfigue.references; // [{ key, ref }, …] (one per active toast)Usage — jQuery
// content of the element becomes the message; params customize the toast
$('#flash').GSToast({ type: 'success', title: 'Saved', duration: 4000 });Usage — HTMLElement.prototype
document.querySelector('#flash').GSToast({ type: 'error', message: 'Failed' });Usage — React
The ergonomic form is the imperative gsToast helper — identical surface to the
native global. Styles are injected at runtime; no CSS import required.
import { gsToast } from '@get-set/gs-toast';
function SaveButton() {
const onSave = async () => {
const id = gsToast.loading('Saving…');
try {
await save();
gsToast.update(id, { type: 'success', message: 'Saved!', duration: 3000 });
} catch {
gsToast.update(id, { type: 'error', message: 'Save failed', duration: 5000 });
}
};
return <button onClick={onSave}>Save</button>;
}Declarative <GSToast> component
<GSToast open … /> fires a toast through the same engine; clearing open (or
calling dismiss() on the ref) dismisses it. Props mirror the options object.
import React, { useRef, useState } from 'react';
import GSToast, { GSToastHandle } from '@get-set/gs-toast';
function Example() {
const ref = useRef<GSToastHandle>(null);
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => ref.current?.show()}>Show</button>
<button onClick={() => setOpen(true)}>Open (controlled)</button>
<GSToast
ref={ref}
open={open}
type="info"
title="Heads up"
message="This is a toast."
position="bottom-center"
animation="pop"
onClose={() => setOpen(false)}
/>
</>
);
}Controlled vs. uncontrolled
- Uncontrolled: omit
open. Drive it withdefaultOpenor the imperativeref(show()/dismiss()). - Controlled: pass
open(and react toonClose); the toast mirrors your state.
<GSToastContainer> (optional)
Mounting it guarantees the stylesheet is injected and lets you scope gsx to all
toast containers. Toasts are still fired imperatively via gsToast. The
positioned containers themselves are auto-created.
import { GSToastContainer } from '@get-set/gs-toast';
<GSToastContainer gsx={{ '.gs-toast': { borderRadius: '16px' } }} />Next.js (App Router)
Toasts are browser-only — fire them from a Client Component:
'use client';
import { gsToast } from '@get-set/gs-toast';
export default function Notify() {
return <button onClick={() => gsToast.success('Hello from the client')}>Notify</button>;
}gsx — scoped styles (React-only)
The gsx prop on <GSToast> injects a <style> scoped to the instance via the
toast's data-key, and removes it on unmount:
<GSToast open message="Custom" gsx={{ color: 'rebeccapurple' }} />Options / props
Legend: N = native (GSToast(opts) / jQuery / prototype),
R = React (gsToast + <GSToast>). All durations are in milliseconds.
Identity
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| reference | string | auto (GUID) | ✓ | ✓ | Stable id / registry key / data-key. Reuse it with update/dismiss, or call the factory again with the same reference to update in place. |
| open | boolean | — | | ✓ | Controlled visibility (<GSToast>). |
| defaultOpen | boolean | false | | ✓ | Uncontrolled initial visibility (<GSToast>). |
| gsx | NestedCSS | — | | ✓ | React-only scoped style object. |
Placement & stacking
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| position | GSToastPosition | 'top-right' | ✓ | ✓ | Container anchor (see catalog). Each position is its own auto-created container. |
| gap | number | 12 | ✓ | ✓ | Gap (px) between stacked toasts (--gst-gap). |
| maxVisible | number | Infinity | ✓ | ✓ | Max toasts shown at once per position; the rest queue and drain as toasts close. |
| newestOnTop | boolean | true | ✓ | ✓ | Newest toast sits closest to the anchored edge. |
Content
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| type | GSToastType | 'default' | ✓ | ✓ | Semantic type — drives the auto icon + accent (see catalog). |
| title | string | — | ✓ | ✓ | Bold heading line (escaped). |
| message | string | — | ✓ | ✓ | Body text (escaped). |
| html | string | — | ✓ | ✓ | Rich HTML body (raw, not escaped). Wins over message. |
| icon | string \| false | auto by type | ✓ | ✓ | Icon override (raw HTML), or false to hide the icon, or omit for the auto type icon. |
Lifecycle
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| duration | number \| false | 5000 | ✓ | ✓ | Auto-dismiss delay. false = sticky (no auto-close). loading toasts default to sticky. |
| autoClose | number \| false | alias of duration | ✓ | ✓ | react-toastify naming alias; overrides duration when supplied. |
| pauseOnHover | boolean | true | ✓ | ✓ | Pause the countdown while the pointer is over the toast. |
| pauseOnFocusLoss | boolean | true | ✓ | ✓ | Pause the countdown while the tab/window is hidden or blurred. |
Affordances
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| closeButton | boolean | true | ✓ | ✓ | Show the × dismiss button. |
| dismissOnClick | boolean | false | ✓ | ✓ | Dismiss when the toast body is clicked. |
| swipeToDismiss | boolean | true | ✓ | ✓ | Drag/swipe horizontally to dismiss. |
| swipeThreshold | number | 80 | ✓ | ✓ | Distance (px) past which a swipe dismisses. |
Progress bar
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| progressBar | boolean | true (when timed) | ✓ | ✓ | Show the depleting countdown bar. Only shown for timed (non-sticky) toasts. |
| progressDirection | 'ltr' \| 'rtl' | 'rtl' | ✓ | ✓ | Direction the bar runs. |
Animation
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| animation | GSToastAnimation \| false | 'slide' | ✓ | ✓ | Entrance animation (see catalog). false disables motion. slide is edge-aware. |
| exitAnimation | GSToastAnimation \| false | mirror of animation | ✓ | ✓ | Separate exit animation. |
| animationDuration | number \| { enter?: number; exit?: number } | 320 | ✓ | ✓ | Duration(s) in ms (--gst-anim-duration). |
| animationEasing | string | cubic-bezier(.21,1.02,.73,1) | ✓ | ✓ | CSS easing (--gst-anim-easing). |
| reducedMotion | 'auto' \| boolean | 'auto' | ✓ | ✓ | 'auto' honors prefers-reduced-motion: reduce and falls back to no motion. |
Actions
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| actions | GSToastAction[] | — | ✓ | ✓ | Action buttons under the message (see shape below). |
GSToastAction shape:
interface GSToastAction {
label: string; // button text
onClick?: (id: string) => void | boolean; // return false to KEEP the toast
className?: string; // extra per-button class
variant?: 'default' | 'primary' | 'ghost'; // primary uses the accent color
}Theming
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| theme | 'light' \| 'dark' \| 'auto' | 'auto' | ✓ | ✓ | Visual theme; auto follows prefers-color-scheme. |
| accent | string | per-type | ✓ | ✓ | Accent color override (--gst-accent). |
| rtl | boolean | false | ✓ | ✓ | Right-to-left layout. |
| className | string | — | ✓ | ✓ | Extra class on the toast root. |
| customClass | GSToastCustomClass | {} | ✓ | ✓ | Per-part class map (below). |
customClass keys: container, toast, icon, title, message,
closeButton, progressBar, actions.
Accessibility
| Name | Type | Default | N | R | Description |
| --- | --- | --- | :-: | :-: | --- |
| role | 'status' \| 'alert' \| 'log' | 'status' ('alert' for error/warning) | ✓ | ✓ | ARIA role. alert toasts announce assertively (aria-live="assertive"); others politely. |
The container is a labelled role="region" (aria-label="Notifications"), each
toast carries a resolved role + aria-live, the close/action buttons are real
<button>s with aria-label, and prefers-reduced-motion is honored.
Callbacks
| Name | Type | N | R | Description |
| --- | --- | :-: | :-: | --- |
| onShow | (id: string) => void | ✓ | ✓ | Fired after the toast has mounted/entered. |
| onClose | (id: string, reason?: GSToastCloseReason) => void | ✓ | ✓ | Fired after the toast has closed (with the close reason). |
| onClick | (id: string) => void | ✓ | ✓ | Fired when the toast body is clicked. |
GSToastCloseReason is one of: 'timeout', 'click', 'closeButton',
'swipe', 'action', 'api', 'dismissAll', 'limit'.
Variant catalogs
Type catalog
type drives the auto SVG icon and the accent color. The loading type ships an
animated spinner and is sticky by default.
| type | Default role | Default accent | Notes |
| --- | --- | --- | --- |
| default | status | brand (#2563eb) | No icon. |
| success | status | #16a34a | Check icon. |
| error | alert | #dc2626 | Cross icon. |
| warning | alert | #d97706 | Triangle icon. |
| info | status | #2563eb | Info icon. |
| loading | status | #6366f1 | Animated spinner; sticky; no close button by default. |
Position catalog
| Value | Anchor |
| --- | --- |
| top-left | Top-left corner. |
| top-center | Top edge, centered. |
| top-right | Top-right corner (default). |
| bottom-left | Bottom-left corner. |
| bottom-center | Bottom edge, centered. |
| bottom-right | Bottom-right corner. |
| top-center-left | Left edge, vertically centered. |
| top-center-right | Right edge, vertically centered. |
Animation catalog
animation and exitAnimation accept any of these names. Each maps to a
@keyframes gst-<name>-in / gst-<name>-out pair defined in all CSS sources.
slide is edge-aware — it resolves to slide-down (top anchors),
slide-up (bottom anchors), slide-left / slide-right (side anchors).
| Name | Motion |
| --- | --- |
| slide | Slides in from the anchored edge (the default). |
| fade | Opacity in/out. |
| zoom | Scale 0.8 → 1. |
| flip | 3D flip about the X axis. |
| pop | Springy scale overshoot. |
| bounce | Drops in with a bounce. |
| none | No motion (the reduced-motion / disabled fallback). |
GSToast({
message: 'Settled into place',
animation: 'pop',
exitAnimation: 'fade',
animationDuration: { enter: 360, exit: 180 }
});Theme catalog
| theme | Behaviour |
| --- | --- |
| light | Forces the light palette. |
| dark | Forces the dark palette. |
| auto | Follows prefers-color-scheme (default). |
Imperative API
gsToast helper / window.GSToast global
Identical surface in both targets:
| Member | Returns | Description |
| --- | --- | --- |
| gsToast(opts) | string | Show a toast; returns its id. (Native also accepts a selector/element.) |
| gsToast.success(message?, opts?) | string | Show a success toast. |
| gsToast.error(message?, opts?) | string | Show an error toast. |
| gsToast.warning(message?, opts?) | string | Show a warning toast. |
| gsToast.info(message?, opts?) | string | Show an info toast. |
| gsToast.loading(message?, opts?) | string | Show a sticky loading toast (no close button). |
| gsToast.update(id, opts) | void | Update a live toast in place (merges options). |
| gsToast.dismiss(id, reason?) | void | Dismiss a toast by id. |
| gsToast.dismissAll(reason?) | void | Dismiss every toast across all containers. |
| gsToast.isActive(id) | boolean | Whether a toast with the id is currently active. |
React GSToastHandle (via ref)
import GSToast, { GSToastHandle } from '@get-set/gs-toast';
import { useRef } from 'react';
const ref = useRef<GSToastHandle>(null);
// <GSToast ref={ref} message="…" />
ref.current?.show();| Method | Returns | Description |
| --- | --- | --- |
| show() | string | Fire this toast through the shared engine; returns its id. |
| dismiss(reason?) | void | Dismiss this toast. |
| update(opts) | void | Update this toast's options in place. |
| isActive() | boolean | Whether this toast is currently active. |
| getId() | string | This toast's id. |
Native per-toast Ref
Looked up via window.GSToastConfigue.instance(key):
| Member | Type | Description |
| --- | --- | --- |
| reference | string | The toast id. |
| params | Params | The resolved params. |
| el | HTMLElement \| null | The toast DOM element (null while queued). |
| dismiss(reason?) | () => void | Dismiss this toast. |
| update(opts) | (opts) => void | Update this toast in place. |
Registry — window.GSToastConfigue
window.GSToastConfigue.references; // [{ key, ref }, …]
window.GSToastConfigue.instance('my-key'); // -> the live Ref, or undefinedLicense
ISC.
