How to Use the Astryx Toast Notification System with the useToast Hook
The Astryx useToast hook returns an imperative showToast function that automatically manages viewport attachment, stacking, auto-dismiss timers, and deduplication, falling back to a singleton detached DOM tree if no ToastViewport provider exists.
The Astryx design system (facebook/astryx) provides a robust notification layer built around a hook-driven API. The Toast notification system abstracts away positioning, lifecycle management, and accessibility concerns through a single useToast hook. This article breaks down the implementation details found in the core source code and demonstrates how to trigger, customize, and manage toast notifications in React applications.
Core Hook API and Fallback Viewport
The useToast hook (located in packages/core/src/Toast/useToast.tsx) exports an imperative interface that bypasses the need for manual context wiring. When invoked, the hook obtains a ToastContext from the nearest ToastViewport provider. If no provider is present in the component tree, the hook lazily creates a singleton fallback viewport (a detached DOM tree) via getFallbackContext()—this singleton is created only once per application instance (lines 61-79).
The hook returns a showToast function of type ShowToastFn. Calling this function generates a unique toast ID using generateToastId, constructs a toast entry object, and dispatches it to the context via ctx.addToast(entry). The function also returns a dismissal callback that invokes ctx.removeToast(id, 'manual') to programmatically dismiss the notification.
Viewport Context and Stacking Logic
The ToastViewport component (packages/core/src/Toast/ToastViewport.tsx) supplies the ToastContext that stores the list of active toasts and provides utility methods such as findByUniqueID. This component handles the actual rendering of each toast inside a <Toast> component, applying enter/exit animations and managing stacking order for multiple simultaneous notifications.
The viewport also manages focus handling and ARIA live region announcements through the useAnnounce hook. When the viewport operates in fallback mode (detached from the main React tree), it mirrors the page's theme attributes—data-theme and data-astryx-theme—via syncRootThemeAttrs to ensure CSS @scope rules apply correctly to the toast notifications.
Deduplication and Collision Behavior
Each toast entry supports a uniqueID option that enables intelligent deduplication. When a toast with a matching uniqueID is already active, the collisionBehavior option (defined in packages/core/src/Toast/types.ts) determines whether the existing toast is overwritten or the new request is ignored. This prevents notification spam when users trigger the same action repeatedly.
The entry object also contains lifecycle options including isAutoHide (boolean), duration (milliseconds), and callbacks such as onHide. When isAutoHide is true, the viewport automatically initiates removal after the specified duration, invoking the exit animation before calling ctx.removeToast.
Implementation Examples
Simple success notification:
import { Button } from '@astryxdesign/core';
import { useToast } from '@astryxdesign/core/Toast';
function SaveButton() {
const toast = useToast();
const handleSave = async () => {
try {
await saveData();
toast({ body: 'Saved successfully' });
} catch {
toast({ body: 'Failed to save', type: 'error' });
}
};
return <Button label="Save" onClick={handleSave} />;
}
Toast with action and deduplication:
import { useToast } from '@astryxdesign/core/Toast';
import { Button } from '@astryxdesign/core';
function DeleteItem({ itemId }) {
const toast = useToast();
const handleDelete = () => {
deleteItem(itemId);
toast({
body: 'Item deleted',
endContent: <Button label="Undo" onClick={() => restoreItem(itemId)} />,
uniqueID: `delete-${itemId}`, // Prevents duplicate notifications
});
};
return <Button label="Delete" onClick={handleDelete} />;
}
Explicit viewport placement (optional):
import { ToastViewport } from '@astryxdesign/core/Toast';
function App() {
return (
<>
{/* Application content */}
<ToastViewport position="bottomEnd" maxVisible={5} />
</>
);
}
Summary
- The
useToasthook inpackages/core/src/Toast/useToast.tsxprovides an imperative API that automatically handles viewport attachment and fallback creation. - Fallback viewport logic ensures toast functionality works even without an explicit
ToastViewportprovider, creating a singleton detached DOM tree on first use. - Deduplication is controlled via
uniqueIDandcollisionBehaviorproperties in the toast entry object. - The
ToastViewportcomponent manages stacking, animations, auto-hide timers, focus handling, and screen reader announcements via ARIA live regions. - Theme attributes are automatically synchronized to the fallback viewport using
syncRootThemeAttrsto maintain styling consistency.
Frequently Asked Questions
How does the useToast hook handle cases where no ToastViewport is rendered?
The hook implements a lazy fallback mechanism via getFallbackContext(). When no provider is found in the component tree, it creates a singleton detached DOM tree that acts as a viewport. This fallback is created only once per application lifecycle and automatically mirrors theme attributes from the document root.
What is the purpose of the uniqueID option when calling the toast function?
The uniqueID string enables deduplication logic. When a toast with an identical uniqueID is already active, the system consults the collisionBehavior option to either overwrite the existing toast or ignore the duplicate request. This prevents notification spam from rapid user interactions.
Can I programmatically dismiss a toast before its auto-hide timer expires?
Yes. The showToast function returns a dismiss callback that invokes ctx.removeToast(id, 'manual'). Store this callback from the toast invocation and call it to immediately trigger the exit animation and remove the notification from the stack.
Where are the type definitions for toast configuration options located?
The TypeScript interfaces for toast options, entries, and dismiss reasons are defined in packages/core/src/Toast/types.ts. This includes the ShowToastFn type signature and the collisionBehavior enum for handling duplicate notifications.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →