Skip to Content

Toast

Toast provides event-driven notifications for status updates, processing states, and user feedback. It renders dismissable banners in a fixed overlay and manages multiple toasts with stacking, live updates, and optional auto-dismiss.

Loading...

Overview

Resources

Loading...

Loading...

Loading...

Loading...

Install

yarn add @camp/toast

Upgrading to Next Gen

From AI Toast (@camp/ai-banner)

The API is identical — only the import path and component name have changed.

Before

import { AiToaster, toast, ToastDurationPreset } from '@camp/ai-banner'; function App() { return ( <> {/* ...your app... */} <AiToaster headerOffset="60px" /> </> ); }

After

import { Toaster, toast, ToastDurationPreset } from '@camp/toast'; function App() { return ( <> {/* ...your app... */} <Toaster headerOffset="60px" /> </> ); }

🔄 Changed:

AI Toast (@camp/ai-banner)Next Gen Toast (@camp/toast)
AiToasterToaster
import { ... } from '@camp/ai-banner'import { ... } from '@camp/toast'

All other exports — toast, showToast, updateToast, dismissToast, clearToasts, ToastDurationPreset — are unchanged.

From Camp 1 (@activecampaign/camp-components-toast)

The Next Gen API is a significant improvement: toast() returns a controller object so you can update or dismiss a toast without tracking IDs manually. Several prop names have also changed.

Setting up the Toaster

The toastDismissButtonLabel prop is no longer required on <Toaster>. Pass dismissLabel per individual toast instead if you need a custom label.

// Before (Camp 1) import { Toaster } from '@activecampaign/camp-components-toast'; <Toaster toastDismissButtonLabel="Dismiss" headerOffset="60px" />; // After (Next Gen) import { Toaster } from '@camp/toast'; <Toaster headerOffset="60px" />;

Creating a toast

createToast() is replaced by toast(), which returns a controller. duration is now optional (omit or pass 0 for a persistent toast).

// Before (Camp 1) import { createToast, ToastDuration } from '@activecampaign/camp-components-toast'; const id = createToast({ title: 'Changes saved', appearance: 'success', duration: ToastDuration.Short, }); // After (Next Gen) import { toast, ToastDurationPreset } from '@camp/toast'; const controller = toast({ title: 'Changes saved', appearance: 'success', duration: ToastDurationPreset.Short, });

Dismissing a toast

// Before (Camp 1) import { destroyToast } from '@activecampaign/camp-components-toast'; destroyToast(id); // After (Next Gen) — via controller controller.dismiss(); // After (Next Gen) — via store function (when you only have the id) import { dismissToast } from '@camp/toast'; dismissToast(id);

Updating a toast (new in Next Gen)

Camp 1 had no way to update an existing toast. Next Gen lets you update title, description, appearance, and duration in place:

import { toast, ToastDurationPreset } from '@camp/toast'; const controller = toast({ title: 'Saving…', appearance: 'info', duration: 0, }); controller.update({ title: 'Saved', appearance: 'success', duration: ToastDurationPreset.Short, });

Adding actions

The actions prop is renamed to renderActions.

// Before (Camp 1) createToast({ title: 'Error saving', appearance: 'destructive', actions: () => <Button size="small">Try again</Button>, }); // After (Next Gen) toast({ title: 'Error saving', appearance: 'destructive', renderActions: () => <Button size="small">Try again</Button>, });

Custom dismiss handler

onDismiss now receives the toast id in both APIs, but in Next Gen you call dismissToast(id) (instead of destroyToast(id)) inside your handler.

// Before (Camp 1) import { createToast, destroyToast } from '@activecampaign/camp-components-toast'; createToast({ title: 'My notification', onDismiss: (id) => { // custom logic… destroyToast(id); }, dismissLabel: 'Dismiss', }); // After (Next Gen) import { toast, dismissToast } from '@camp/toast'; toast({ title: 'My notification', onDismiss: (id) => { // custom logic… dismissToast(id); }, dismissLabel: 'Dismiss', });

Prop changes summary

Camp 1 prop / exportNext Gen equivalentNotes
createToast(props)toast(props)Returns a controller instead of a plain id
destroyToast(id)dismissToast(id) / controller.dismiss()Renamed
actionsrenderActionsSame signature: () => ReactNode
ToastDurationToastDurationPresetSame string values: short, standard, long
<Toaster toastDismissButtonLabel="..." />toast({ dismissLabel: '...' })Moved from Toaster to per-toast
toast().update(partial)New: live-update an existing toast
clearToasts()New: dismiss all toasts at once

Variations

Basic Toast

Use the toast() controller to create, update, and dismiss toasts from component or hook code. It returns a controller with id, update, and dismiss methods.

import { Toaster, toast, ToastDurationPreset } from '@camp/toast'; const controller = toast({ title: 'Processing', description: 'Your request is being handled.', appearance: 'info', duration: ToastDurationPreset.Standard, }); // Update the same toast in place controller.update({ title: 'Complete', description: 'Your request was processed successfully.', appearance: 'success', duration: ToastDurationPreset.Standard, }); // Dismiss when done controller.dismiss();

Stacked Toasts

Multiple toasts stack automatically in the top-right corner. Each toast is independently dismissable and auto-dismisses based on its configured duration.

import { Toaster, toast, ToastDurationPreset } from '@camp/toast'; toast({ title: 'Info notice', description: 'Heads up', appearance: 'info', duration: ToastDurationPreset.Long, }); toast({ title: 'Warning', description: 'Check settings', appearance: 'warning', duration: ToastDurationPreset.Standard, }); toast({ title: 'Success', description: 'Saved', appearance: 'success', duration: 3000 });

Updating Toast Content

Update a toast in place while an async operation is in progress — useful for uploads, API calls, or multi-step workflows.

import { Toaster, toast, ToastDurationPreset } from '@camp/toast'; const controller = toast({ title: 'Processing…', description: 'Please wait.', appearance: 'info', duration: 0, // persistent until updated }); setTimeout(() => { controller.update({ title: 'Complete', description: 'All done.', appearance: 'success', duration: ToastDurationPreset.Standard, }); }, 3000);

Multiple Toasts with Targeted Updates

Use separate controllers to independently update multiple concurrent toasts.

import { Toaster, toast, ToastDurationPreset } from '@camp/toast'; const syncA = toast({ title: 'Syncing A…', appearance: 'info', duration: ToastDurationPreset.Long, }); const syncB = toast({ title: 'Syncing B…', appearance: 'info', duration: ToastDurationPreset.Long, }); syncA.update({ title: 'A Complete', appearance: 'success', duration: 4000 }); syncB.update({ title: 'B Error', description: 'Something went wrong', appearance: 'destructive', duration: ToastDurationPreset.Standard, });

Toast with Progress Bar

The title and description props accept React nodes, allowing you to embed rich content like progress bars or formatted text.

import { Toaster, toast } from '@camp/toast'; import { ProgressBar } from '@camp/progress-bar'; import { useEffect, useRef } from 'react'; function UploadComponent() { const controllerRef = useRef<ReturnType<typeof toast> | null>(null); useEffect(() => { controllerRef.current = toast({ title: 'Uploading…', description: ( <div style={{ width: 320, marginTop: 8 }}> <ProgressBar value={1} ariaLabel="Upload progress" showValue /> </div> ), appearance: 'info', duration: 0, }); const timer = setTimeout(() => { controllerRef.current?.update({ title: 'Upload complete', description: ( <div style={{ width: 320, marginTop: 8 }}> <ProgressBar value={100} appearance="success" ariaLabel="Upload progress" showValue /> </div> ), appearance: 'success', duration: 3000, }); }, 2000); return () => clearTimeout(timer); }, []); return <Toaster />; }

Store API

For cross-boundary scenarios — outside React components, background jobs, or global cleanup — use the low-level store functions directly.

import { showToast, updateToast, dismissToast, clearToasts, ToastDurationPreset, } from '@camp/toast'; // Create a toast and capture the id const id = showToast({ title: 'Processing started', description: 'Analyzing data…', appearance: 'info', duration: 0, }); // Update it later using the id updateToast(id, { title: 'Processing complete', appearance: 'success', duration: ToastDurationPreset.Standard, }); // Dismiss a specific toast, or clear all dismissToast(id); clearToasts();

Usage

Setup

Render <Toaster /> once near your app root. It requires a ThemeProvider and uses a portal to render toasts at the top of document.body. Use headerOffset to push the container below a fixed header.

import { Toaster } from '@camp/toast'; import { ThemeProvider } from 'styled-components'; import { campTheme } from '@camp/theme'; function App() { return ( <ThemeProvider theme={campTheme}> {/* ...your app... */} <Toaster headerOffset="60px" /> </ThemeProvider> ); }

See the Styled Components & ThemeProvider guide for full setup instructions.

Controller API vs Store API

Use the Controller API (toast()) for typical component and hook code — it keeps the toast lifecycle localized without tracking IDs across modules:

import { toast, ToastDurationPreset } from '@camp/toast'; const controller = toast({ title: 'Settings updated', appearance: 'success', duration: ToastDurationPreset.Standard, }); controller.update({ title: 'Refreshing…', appearance: 'info', duration: 0 }); controller.dismiss();

Use the Store API (showToast, updateToast, dismissToast, clearToasts) when you need to trigger or update toasts from outside React — for example in service files, background jobs, or global error handlers.

Duration Presets

Use ToastDurationPreset for consistent timing across the application. Pass duration: 0 (or omit it) for a persistent toast that the user must dismiss manually.

PresetDuration
ToastDurationPreset.Short4 seconds
ToastDurationPreset.Standard8 seconds
ToastDurationPreset.Long10 seconds

Best Practices

  • Use appearance: 'info' while an operation is in progress; update to 'success', 'warning', or 'destructive' once the outcome is known.
  • Use persistent toasts (duration: 0) only when user action is required before dismissal.
  • Prefer updating an existing toast over creating multiple toasts for the same operation.
  • Clean up persistent toasts on navigation if they are no longer relevant.
  • Keep toast content concise — one or two sentences at most.

Content Guidelines

✅ DO

Update an in-progress toast to reflect the outcome when an async operation completes.

🚫 DON’T

Create a new toast for every step of a multi-step operation. Update the existing one instead.

✅ DO

Use duration presets for consistent timing across the application.

🚫 DON’T

Use an arbitrary number when a preset covers the use case.

Accessibility

  • Each toast renders with role="alert", so screen readers announce it immediately on creation.
  • Dismiss buttons have an accessible label via the dismissLabel prop (falls back to a default close label).
  • Action buttons rendered via renderActions are reachable through normal tab order.
  • Persistent toasts (duration: 0) remain visible until dismissed, giving screen reader users sufficient time to interact with the content.

Similar Components

Banner

Banner

Displays an important, succinct message, and provides actions for users to address.

Popover

Popover

A transient view that shows on top of other content onscreen when requested.

Modal

Modal

A modal window creates a mode that disables the main window but keeps it visible with the modal window as a child window in front of it.

Last updated on