Introduction
What Cloudlet is, how a notification lives in it, and what your application keeps deciding.
On this page
Cloudlet is a notification library for React and the web. It keeps every notification in one stack, newest first, collapsed to a single pill at rest. Activating the pill opens the Island: one surface with a slider over every notification, where you read, retry, cancel or close. Notifications that need attention open the Island on their own after a short delay, without moving keyboard focus.
Everything that moves runs on springs, and every frame can be interrupted: a new notification arriving mid-animation, a swipe released early, a theme switched while the Island is open. The package is ESM only, has no runtime dependencies and ships its CSS as one explicit import.
One stack, one Island
Most toast libraries show each notification as its own card and let them pile up. Cloudlet shows the stack: the top notification as a pill, the ones behind it as tails, and a badge with how many are waiting. The stack takes one small spot at the top or bottom of the viewport, so five notifications cost the same space as one.
Opening the stack turns the pill into the Island. Inside it, one notification is on screen at a time; the slider moves between them with a swipe, the arrow keys or the trackpad. Closing a notification slides the next one in; closing the last one collapses the Island; pressing Escape collapses it with every notification kept.
Time is spent only by what is on screen: the top pill in the stack, or the current slide in the Island. Hovering, focusing, a hidden tab, a window that lost focus, a gesture in progress and an Island opened by the user all pause the budget, so a notification never disappears while someone is looking at it.
Controller, Provider, Toaster
Cloudlet splits into a core that knows nothing about the DOM and a React shell that renders it.
createCloudlet()returns a controller: the owner of notifications, their time, operations and announcements. Creating it allocates no timers and touches no DOM. Your application creates one per browser session, or one per request on the server, and callsdispose()only when that session is over.CloudletProviderhands the controller to a React tree. It does not render anything itself.useCloudlet()returns the controller to any component under the Provider, so a save button can callshowwithout prop drilling.Toasterrenders the stack and the Island for the controller it finds in context. It renders nothing on the server and portals into the document after mount. Unmounting it pauses visible time and keeps every record; mounting it again resumes the same queue.
import { createCloudlet } from '@cloudlet/toast';
// One controller per browser session, created by the application bootstrap.
const cloudlet = createCloudlet();
const admission = cloudlet.show({
title: 'Synced to the cloud',
kind: 'success',
description: { kind: 'text', text: '12 photos, 4 seconds.' },
});
// Admission is explicit: a record was created, merged into an identical one, or rejected.
if (admission.status === 'created') {
cloudlet.dismiss(admission.handle);
}Every call returns a value you can act on rather than a promise that is silently dropped: show tells you whether a record was created, deduplicated or rejected and why; dismiss and update report a stale handle instead of doing nothing.
Operations, not just messages
A notification often stands for work in progress: an upload, a sync, a request that may fail. Cloudlet tracks that work as an operation. You pass a promise or a factory to track, and the controller shows the pending state, switches to the result, and offers Retry when a factory is available. Cancellation is cooperative: Cloudlet gives the operation an AbortSignal and asks; your code decides what cancelling means. Closing or clearing a notification detaches the UI and never cancels the business operation behind it.
Actions work the same way. A command runs through Cloudlet, stays blocked while it is pending and closes the notification only after it succeeded; a failed command reports its error inside the notification and offers to try again. A link is a real link.
Attention
Not every notification deserves the Island. Each one declares how much attention it wants:
stackstays in the stack and waits for the user.autoopens the Island after a short delay unless the user is already in the middle of something.openopens the Island right away.
Opening the Island for attention never steals keyboard focus. Screen readers get one polite or assertive announcement per event; notifications deep in the stack are announced as a count, and returning to a hidden tab does not replay what was missed.
What stays with your application
Cloudlet owns the stack, its time and its accessibility. A few things stay yours by design:
- The controller's lifetime. Create it once per session and dispose of it when that session ends; never share a mutable server controller between users.
- Important data. A notification is a notice, not a record: anything the user must not lose also lives in your main interface.
- Words. Every label, announcement and fallback message is a string or a formatter you can replace, so the Toaster speaks your product's language.
- Styling. Cloudlet ships CSS tokens for colour, radius and spacing and adds your class names per part; it does not require Tailwind or shadcn/ui.
Next steps
- Installation: npm or the shadcn registry, peers, the CSS import.
- Quick start: a Provider, a Toaster and the first
show. - Playground: every kind of notification on a button, with the exact call to copy.