+12% vs last month
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.
Code
<Alert variant="warning" title="Storage almost full">You have used 92% of your space.</Alert> <Alert variant="warning" title="Storage almost full">You have used 92%.</Alert> <Alert variant="warning" title="Storage almost full">You have used 92%.</Alert> <Alert variant="warning" title="Storage almost full">You have used 92%.</Alert> <Alert variant="warning" title="Storage almost full">You have used 92%.</Alert> Message Link to Message
A compact, optionally closable inline message — handy next to form fields.
Code
ReactComing to Vue, Svelte and Angular.
<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.
Result: —
Code
ReactComing to Vue, Svelte and Angular.
<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.
Presets
Result: —
Code
ReactComing to Vue, Svelte and Angular.
<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.
Sound: Web Audio is not available in this browser
Code
ReactComing to Vue, Svelte and Angular.
<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.
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.
Code
ReactComing to Vue, Svelte and Angular.
<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.
Code
const [toasts, setToasts] = useState<ToastItem[]>([]);
<Toast items={toasts} onDismiss={(id) => setToasts((t) => t.filter((x) => x.id !== id))} /> <Toast :items="toasts" @dismiss="(id) => toasts = toasts.filter((t) => t.id !== id)" /> <Toast items={toasts} ondismiss={(id) => (toasts = toasts.filter((t) => t.id !== id))} /> <Toaster />
<script>
window.pelagicToast({ title: 'Saved', message: 'Changes are live', variant: 'success' });
</script> <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.
ringdual-ringdotsbouncepulsebarswaveorbitgridinfinitysquare-spinrippletypinghourglassprogress-ringCode
ReactComing to Vue, Svelte and Angular.
<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.
Code
ReactComing to Vue, Svelte and Angular.
<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.
Code
ReactComing to Vue, Svelte and Angular.
<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.
Code
ReactComing to Vue, Svelte and Angular.
<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.
Code
ReactComing to Vue, Svelte and Angular.
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.
No projects yet
Create your first project to see it here.
Code
<EmptyState icon="📁" title="No projects yet" description="Create your first project.">
<Button>New project</Button>
</EmptyState> <EmptyState icon="📁" title="No projects yet"><Button>New project</Button></EmptyState> <EmptyState icon="📁" title="No projects yet"><Button>New project</Button></EmptyState> <EmptyState title="No projects yet"><span slot="icon">📁</span><Button slot="action">New</Button></EmptyState> <EmptyState icon="📁" title="No projects yet"><Button title="New project" /></EmptyState> Spinner Link to Spinner
A minimal indeterminate spinner.
Code
<Spinner label="Loading results" /> <Spinner /> <Spinner /> <Spinner /> <Spinner /> ProgressBar Link to ProgressBar
Determinate or indeterminate progress.
Code
<ProgressBar value={64} label="Upload progress" />
<ProgressBar label="Processing" /> <ProgressBar :value="64" label="Upload progress" /> <ProgressBar value={64} label="Upload progress" /> <ProgressBar value={64} label="Upload progress" /> <ProgressBar value={64} label="Upload progress" /> Skeleton Link to Skeleton
Placeholder shapes for text, cards and tables while content loads.
Code
{loading ? <SkeletonCard label="Loading profile" /> : <Profile />}
<SkeletonText lines={4} />
<DataTable loading rows={[]} columns={columns} /> <SkeletonCard v-if="loading" label="Loading profile" />
<SkeletonText :lines="4" /> {#if loading}<SkeletonCard label="Loading profile" />{/if}
<SkeletonText lines={4} /> <SkeletonCard label="Loading profile" />
<SkeletonText lines={4} /> {loading ? <SkeletonCard label="Loading profile" /> : <Profile />}
<SkeletonText lines={4} />