Skip to content

Feedback: alerts, toasts & loaders

Tell people what happened and what is happening. Messages use polite live regions (errors use assertive ones), loaders announce their label once, and every animation respects prefers-reduced-motion.

16 components on this page. Every demo is live — change the theme in the header or flip a demo to right-to-left with its RTL button. 10 of them are React-only for now; Vue, Svelte and Angular versions are coming.

Alert Link to Alert

An inline message with four severities and an optional close button.

Live demo
Heads up
Your trial ends in 3 days.
Published
Your site is live.

Props

PropTypeDescription
variant 'info' | 'success' | 'warning' | 'danger' Severity.
title ReactNode Bold heading.
onClose () => void Adds a close button.

Accessibility

Danger and warning use role="alert"; info and success use role="status" so they do not interrupt.

Code

Alert · React
<Alert variant="warning" title="Storage almost full">You have used 92% of your space.</Alert>

Message Link to Message

A compact, optionally closable inline message — handy next to form fields.

Live demo
Info: A new version is available.
Success: Profile saved.

Props

PropTypeDescription
severity 'info' | 'success' | 'warn' | 'error' Severity.
text / children ReactNode Content.
icon ReactNode | false Custom or no icon.
closable / onClose boolean / () => void Close button.

Accessibility

Code

ReactComing to Vue, Svelte and Angular.

Message · React
<Message severity="error" text="Card number is incomplete." />
<Message severity="success" closable>Saved.</Message>

swal (alert dialogs) Link to swal (alert dialogs)

SweetAlert-style promise-based dialogs: confirm, prompt with validation, timers and toast mode.

Live demo

Result: —

Props

PropTypeDescription
<SwalHost /> component Render once near the app root.
swal.fire(options) Promise<SwalResult> { isConfirmed, isDenied, isDismissed, value, dismiss }.
swal.success / error / warning / info / question (title, text?, options?) Shortcuts (pass { illustration: true } for a scene).
illustration / layout / locale … See “Illustrated swal & presets” below.
icon / title / text / html … Content.
showCancelButton / showDenyButton boolean Extra buttons.
input / inputValidator / preConfirm … Prompts with validation and async confirm.
timer / timerProgressBar / toast / position … Auto-close and toast mode.

Accessibility

swal (alert dialogs) keyboard support
KeyAction
Tab / Shift + Tab Cycle focus inside the dialog
Enter Confirm (when the confirm button is focused)
Escape Dismiss (unless allowEscapeKey is false)

Modal dialogs trap focus and restore it to the opener; the timer pauses on hover and focus and has a pause label.

Code

ReactComing to Vue, Svelte and Angular.

swal (alert dialogs) · React
<SwalHost />

const r = await swal.fire({
  icon: 'warning', title: 'Delete file?', text: 'You can’t undo this.',
  showCancelButton: true, confirmButtonText: 'Delete',
});
if (r.isConfirmed) swal.success('Deleted!');

const { value: email } = await swal.fire({
  title: 'Your email', input: 'email', inputLabel: 'Email address',
  inputValidator: (v) => (!v ? 'Email is required' : undefined),
});

Illustrated swal & presets Link to Illustrated swal & presets

Dialogs with themeable scenes from @pelagic-ui/illustrations: an illustration layout with a status badge, a full-bleed hero band, tone-tinted backdrops, and ready presets with English and Arabic copy.

Live demo

Presets

Result: —

Props

PropTypeDescription
illustration IllustrationName | ReactElement | true A scene name (e.g. "offline"), any node, or true to pick one from the icon type.
layout 'icon' | 'illustration' | 'hero' Icon = animated glyph; illustration = scene + status badge; hero = large scene on a tinted band.
locale / labels 'en' | 'ar' / Partial<SwalLabels> Built-in copy language (sets lang and dir) and per-string overrides.
confirmButtonVariant 'primary' | 'danger' Danger for destructive confirms.
sheet boolean Bottom sheet under 640px (default true).
sound UiSoundName | false Sound requested on open (needs SoundProvider); false keeps the dialog silent.
swal.confirmDelete(item?, options?) Promise<SwalResult> Cancel focused; isConfirmed = delete.
swal.unsavedChanges(options?) Promise<SwalResult> isConfirmed = save, isDenied = discard, dismissed = stay.
swal.sessionExpired / offline / permissionDenied / serverError (options?) => Promise<SwalResult> Hero presets with consistent copy.
swal.configure(defaults) void App-wide defaults, e.g. { locale: 'ar', illustration: true }.

Accessibility

Roles, focus trap, Escape and focus return are unchanged. Scenes are decorative (aria-hidden): the title and text carry the meaning, and the status keeps a visible badge plus a screen-reader prefix. The entrance animation is removed under prefers-reduced-motion; forced colors get plain outlines.

Code

ReactComing to Vue, Svelte and Angular.

Illustrated swal & presets · React
<SwalHost />

await swal.success('All done!', 'Your project is live.', { illustration: true });
await swal.fire({ icon: 'info', title: 'Welcome', illustration: 'welcome', layout: 'hero' });

const r = await swal.confirmDelete('Q3 report.pdf');
if (r.isConfirmed) remove();

const choice = await swal.unsavedChanges({ locale: 'ar' });
if (choice.isConfirmed) save(); else if (choice.isDenied) discard();

swal.configure({ locale: 'ar', illustration: true }); // app-wide defaults

Sound (SoundProvider, useSound) Link to Sound (SoundProvider, useSound)

Optional UI sounds synthesized with the Web Audio API — no audio files. Off by default, never before a user gesture, silent in hidden tabs, with four themes, master volume and a reduced mode. Swal, toasts and loaders request sounds through emitUiSound.

Live demo
With components:

Sound: Web Audio is not available in this browser

Props

PropTypeDescription
<SoundProvider> enabled theme volume allowed reduced enabled/theme/volume are defaults for users without a saved choice; allowed=false silences the whole app.
useSound() UseSound { enabled, theme, volume, reduced, unlocked, supported, play, setEnabled, toggle, setTheme, setVolume, setReduced }. User changes persist to localStorage ("pl-sound").
UiSoundName 18 sounds click · toggle-on · toggle-off · select · success · error · warning · info · notification · delete · send · pop · swipe · open · close · drag-start · drop · typing.
themes 'soft' | 'crisp' | 'playful' | 'minimal' Timbre presets.
emitUiSound(name) @pelagic-ui/core/sound Request a sound from any code without depending on the engine.
createSoundEngine(options) @pelagic-ui/core/sound Framework-agnostic engine (play, setEnabled, setVolume, setTheme, isEnabled, subscribe, destroy).
<html data-sound="off | reduced"> attribute Page-level mute or reduced mode (e.g. from an accessibility panel).

Accessibility

Sound is never the only signal: every component keeps its visual state and live-region announcement. Users can turn sound off, lower it, or keep only status sounds; the choice is remembered.

Code

ReactComing to Vue, Svelte and Angular.

Sound (SoundProvider, useSound) · React
<SoundProvider theme="soft">
  <App />
</SoundProvider>

function SoundToggle() {
  const sound = useSound();
  return <Switch label="UI sounds" checked={sound.enabled} onChange={sound.setEnabled} />;
}

// Anywhere, framework-agnostic:
import { emitUiSound } from '@pelagic-ui/core/sound';
emitUiSound('send');

Toaster & toast() Link to Toaster & toast()

Stacked, swipeable notifications with types, actions, promise states and updates.

Live demo

    Press Alt + T to jump to the notifications. Hover or focus fans the deck out and pauses every countdown (so does hiding the tab); swipe a toast sideways to dismiss it. Errors are announced assertively, everything else politely, each once. With reduced motion toasts only fade.

    Props

    PropTypeDescription
    <Toaster /> component Render once; position, richColors, closeButton, expand, visibleToasts, duration.
    toast(message, options?) ToastId Default toast.
    toast.success / error / warning / info / loading ToastId Typed toasts.
    toast.promise(p, { loading, success, error }) ToastId Follows a promise.
    toast.update(id, opts) / toast.dismiss(id?) void Change or close.
    options { description, action, cancel, duration, icon } Per toast.

    Accessibility

    Toaster & toast() keyboard support
    KeyAction
    Alt + T Move focus to the notifications (configurable hotkey)
    Escape Dismiss the focused toast
    Tab Reach toast actions

    A labelled live region; timers pause on hover, focus and when the page is hidden.

    Code

    ReactComing to Vue, Svelte and Angular.

    Toaster & toast() · React
    <Toaster position="bottom-end" richColors closeButton />
    
    toast.success('Saved', { description: 'Your changes are live.' });
    toast('Message archived', { action: { label: 'Undo', onClick: undo } });
    toast.promise(save(), { loading: 'Saving…', success: 'Saved', error: 'Could not save' });

    Toast (controlled) Link to Toast (controlled)

    A simple controlled toast stack — the cross-framework core component.

    Live demo

    Props

    PropTypeDescription
    items { id, title?, message, variant? }[] Toasts to show.
    onDismiss (id) => void Remove one.

    Accessibility

    Rendered in a polite live region.

    Code

    Toast (controlled) · React
    const [toasts, setToasts] = useState<ToastItem[]>([]);
    <Toast items={toasts} onDismiss={(id) => setToasts((t) => t.filter((x) => x.id !== id))} />

    Loader Link to Loader

    15 animated loader types — ring, dots, bars, wave, orbit, infinity, progress ring and more.

    Live demo
    ring loaderring
    dual-ring loaderdual-ring
    dots loaderdots
    bounce loaderbounce
    pulse loaderpulse
    bars loaderbars
    wave loaderwave
    orbit loaderorbit
    grid loadergrid
    infinity loaderinfinity
    square-spin loadersquare-spin
    ripple loaderripple
    typing loadertyping
    hourglass loaderhourglass
    progress-ring loaderprogress-ring
    SmallLarge successUploading

    Props

    PropTypeDescription
    type LoaderType 'ring' | 'dual-ring' | 'dots' | 'bounce' | 'pulse' | 'bars' | 'wave' | 'orbit' | 'grid' | 'infinity' | 'square-spin' | 'ripple' | 'typing' | 'hourglass' | 'progress-ring'.
    size 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number Size.
    tone 'primary' | 'current' | 'muted' | 'success' | … Colour.
    label / showLabel string / boolean Accessible (and optionally visible) label.
    value number Determinate progress for progress-ring.

    Accessibility

    role="status" with the label; under reduced motion the animation slows to a gentle fade instead of spinning.

    Code

    ReactComing to Vue, Svelte and Angular.

    Loader · React
    <Loader type="orbit" label="Loading projects" />
    <Loader type="progress-ring" value={64} showLabel label="Uploading" />

    LoaderOverlay Link to LoaderOverlay

    Covers a region with a loader while it refreshes.

    Live demo
    Monthly active users
    48,210

    +12% vs last month

    Props

    PropTypeDescription
    active boolean Show the overlay.
    blur boolean Blur the content underneath.
    type / label LoaderType / string Loader settings.

    Accessibility

    The covered content is aria-busy and inert while active.

    Code

    ReactComing to Vue, Svelte and Angular.

    LoaderOverlay · React
    <LoaderOverlay active={refreshing} label="Refreshing stats">
      <StatsCard />
    </LoaderOverlay>

    PageLoader Link to PageLoader

    A full-page splash loader with your brand that fades out when ready.

    Live demo

    Props

    PropTypeDescription
    active boolean Visible while true.
    brand ReactNode Logo or name.
    exitDuration number Fade-out length (ms).
    type / label / showLabel … Loader settings.

    Accessibility

    Code

    ReactComing to Vue, Svelte and Angular.

    PageLoader · React
    <PageLoader active={!ready} brand={<Logo />} label="Loading dashboard" />

    TopProgressBar Link to TopProgressBar

    A slim progress bar at the top of the window for route changes and long requests.

    Live demo
    Watch the very top of the window.

    Props

    PropTypeDescription
    <TopProgressBar /> component Render once.
    topProgress.start() / done() void Trickles until done.
    topProgress.set(n) / inc(n?) void Manual control (0–1).

    Accessibility

    role="progressbar" with a label; hidden from view when idle.

    Code

    ReactComing to Vue, Svelte and Angular.

    TopProgressBar · React
    <TopProgressBar />
    
    router.on('start', () => topProgress.start());
    router.on('end', () => topProgress.done());

    useAsyncAction Link to useAsyncAction

    A hook that tracks loading and error state for any async action — pair it with Button loading.

    Live demo

    Props

    PropTypeDescription
    useAsyncAction(fn) { run, loading, error, reset } Wraps fn; ignores stale results and unmounted components.

    Accessibility

    Code

    ReactComing to Vue, Svelte and Angular.

    useAsyncAction · React
    const save = useAsyncAction(api.save);
    <Button loading={save.loading} onClick={() => save.run(form)}>Save</Button>
    {save.error && <Message severity="error" text="Could not save" />}

    EmptyState Link to EmptyState

    Explains why a view is empty and offers the next step.

    Live demo

    No projects yet

    Create your first project to see it here.

    Props

    PropTypeDescription
    icon ReactNode Decorative icon.
    title / description ReactNode Text.
    children ReactNode Actions.

    Accessibility

    Code

    EmptyState · React
    <EmptyState icon="📁" title="No projects yet" description="Create your first project.">
      <Button>New project</Button>
    </EmptyState>

    Spinner Link to Spinner

    A minimal indeterminate spinner.

    Live demo

    Props

    PropTypeDescription
    size 'md' | 'lg' Size.
    label string Accessible label. Default “Loading”.

    Accessibility

    Code

    Spinner · React
    <Spinner label="Loading results" />

    ProgressBar Link to ProgressBar

    Determinate or indeterminate progress.

    Live demo

    Props

    PropTypeDescription
    value number (0–100) Omit for indeterminate.
    label string Accessible name.

    Accessibility

    role="progressbar" with aria-valuenow when determinate.

    Code

    ProgressBar · React
    <ProgressBar value={64} label="Upload progress" />
    <ProgressBar label="Processing" />

    Skeleton Link to Skeleton

    Placeholder shapes for text, cards and tables while content loads.

    Live demo
    Loading profile
    Loading article

    Props

    PropTypeDescription
    Skeleton width / height / circle number | string / boolean A single shape.
    SkeletonText lines number Paragraph placeholder.
    SkeletonCard lines number Card placeholder.
    label string Announced once (“Loading…”).

    Accessibility

    Announced once as a status; the shapes themselves are hidden from screen readers and the shimmer stops under reduced motion.

    Code

    Skeleton · React
    {loading ? <SkeletonCard label="Loading profile" /> : <Profile />}
    <SkeletonText lines={4} />
    <DataTable loading rows={[]} columns={columns} />

    Search Pelagic UI