FSAI Design System
Patterns

Toasts

Platform rules for transient feedback, async status, undo actions, and custom notification cases.

Overview

Toasts are a core platform feedback pattern.

In this codebase, there are two layers:

LayerWhat it isWho should use it
Toast / DefaultToastLow-level composable UIShared-ui maintainers and special custom cases
showToast / showPromiseToastStandard toast APIAlmost all product code

The default rule is simple:

Use showToast for ordinary transient feedback. Use showPromiseToast for async work that should move through a pending state and then resolve to success or error. Only build a custom toast when the standard toast layout is not enough.

Because toasts are used heavily across the brand dashboard, this pattern deserves its own page rather than being buried inside a generic feedback section.

Default Pattern

Use showToast for most success, error, info, or neutral notifications.

import { showToast } from '@fsai/shared-ui';

showToast({
  type: 'success',
  content: 'Workflow exported',
});

This is the normal pattern used throughout the brand dashboard for:

  • save success
  • import/export feedback
  • validation or mutation errors
  • small completion messages

When To Use showToast

Use showToast when the feedback is:

  • transient
  • not blocking
  • short enough to scan quickly
  • not the only place critical information appears
  • related to a user action or an important background event

Common good fits:

  • "Saved"
  • "Invite sent"
  • "Workflow imported"
  • "Failed to save changes"
  • "Copied to clipboard"

Async Workflows

Use showPromiseToast when a single async action should show pending progress and then transition to success or error.

await showPromiseToast(saveChanges(), {
  pending: {
    type: 'neutral',
    content: 'Saving changes...',
  },
  success: {
    type: 'success',
    content: 'Changes saved',
  },
  error: {
    type: 'error',
    content: 'Failed to save changes',
  },
});

This is the right fit for:

  • report generation
  • CSV import flows
  • permission or bulk-update saves
  • other user-triggered async work where a pending state is meaningful

Important behavior:

  • the pending toast is delayed slightly so very fast operations do not flash
  • the pending toast is kept visible for a minimum time so it does not flicker
  • success or error updates reuse the same toast id rather than stacking new toasts

Undo And Action Toasts

Use showToast actions when the user may immediately reverse or follow up on the notification.

const toastId = showToast({
  type: 'neutral',
  title: 'Lead Deleted',
  content: 'The lead has been deleted',
  actions: [
    {
      text: 'Undo',
      onClick: () => {
        onUndo();
        toast.dismiss(toastId);
      },
    },
  ],
});

This is the pattern used in Leads.tsx.

Use action toasts for:

  • undo
  • retry
  • open/view follow-up actions

Do not overload the toast with too many actions or turn it into a mini workflow.

Global Error Handling

The brand dashboard wires showToast into the app-level React Query caches in App.tsx.

That means many unhandled query and mutation errors already become platform toasts by default.

Important consequences:

  • do not add a second local toast for the same error unless you intentionally want duplicate feedback
  • use meta.skipDefaultErrorHandler when a query or mutation should not trigger the global default behavior
  • repeated global errors can share a toastId to dedupe notifications

This global behavior is one of the main reasons AI agents should prefer the standard toast helpers rather than rolling custom notification logic.

When Custom Toasts Fit

Use a custom toast only when the default showToast layout is not enough.

Good custom-toast cases:

  • upload progress with expanding file lists
  • custom live-updating notification UIs
  • richer layouts that still fit inside toast-sized feedback

Representative example:

  • fsai/apps/brand-dashboard/src/context/UploadContext/UploadContext.tsx updates a custom UploadProgressToast with toast.update(...)

This is the exception, not the rule.

When Not To Use A Toast

Do not use a toast when the feedback needs to be:

  • blocking
  • persistent until resolved
  • deeply instructional
  • tied to a field-level validation issue
  • visible as part of page content rather than transient feedback

Use another pattern instead:

NeedBetter pattern
Blocking confirmation or required decisionModal
Field-level validationinline FieldError
Persistent warning or prerequisite explanationinline AlertBox
Rich promotional or workflow paneldedicated component, not a toast

DemoBookingToast.tsx in brand dashboard is a good reminder that not every bottom-right floating surface should be implemented as a platform toast.

App Wiring

Shared toasts depend on app-level setup:

  • a #toast-root element in the app HTML
  • <ToastPortal /> mounted near the app root
  • react-toastify/dist/ReactToastify.css imported once

In the brand dashboard this is already wired in index.html and App.tsx.

If you are working in another app, confirm this setup exists before expecting showToast to work.

Brand Dashboard Usage Patterns

Representative usages:

PatternPathNotes
Global default error toastsfsai/apps/brand-dashboard/src/App.tsxReact Query query and mutation caches show standard error toasts.
Simple success/error feedbackfsai/apps/brand-dashboard/src/pages/Workflows/components/toolbar/WorkflowToolbar.tsxExport, import, and validation feedback uses showToast.
Undo action toastfsai/apps/brand-dashboard/src/pages/Leads/Leads.tsxNeutral toast with an Undo action and manual dismiss.
Promise lifecycle toastfsai/apps/brand-dashboard/src/pages/Analytics/components/AnalyticsReportsDialog/AnalyticsReportsDialog.tsxUses showPromiseToast for report generation/opening.
Import flow statusfsai/apps/brand-dashboard/src/modules/csv-ingestion/lead-import/LeadImportFlow/LeadImportFlowPage.tsxPromise-driven async flow feedback.
Custom upload progress toastfsai/apps/brand-dashboard/src/context/UploadContext/UploadContext.tsxUses raw react-toastify for a special progress UI.

Important Gotchas

  • Prefer the showToast function, but be aware that some feature APIs also use a boolean prop named showToast. That boolean is not the shared helper.
  • The brand-dashboard modules/toast folder is about Toast POS integration, not UI notification toasts.
  • Raw toast.error(...) from react-toastify exists in some legacy areas such as upload flows, but it bypasses the shared DefaultToast styling.
  • showToast returns a toast id, which can be used with toast.dismiss(id) for action flows like Undo.

Guidelines

  • Do use showToast as the default notification API
  • Do use showPromiseToast for async work with meaningful pending state
  • Do use toast actions sparingly for high-value follow-up actions like Undo
  • Do rely on the global error handler when it already covers the failure path
  • Don't create custom toast layouts unless the standard default toast is genuinely insufficient
  • Don't use a toast for field validation, blocking decisions, or long-form instructional content
  • Don't confuse UI toast notifications with Toast POS integration code

On this page