In-product notification
Notifications provide timely feedback about user actions or system status. Used well, they build trust and clarity. Used carelessly, they interrupt flow.
There are two primary patterns in our system:
- Sonner — time-based, non-modal notifications for lightweight feedback. Floating, transient, always tied to a user action, never blocking.
- Alert — inline notifications within page content for contextual, persistent, or blocking feedback. Structural, anchored to content, can be persistent or blocking.
Both components support status variants and theme-based contrast styles. Use them intentionally.
When to use notifications
Task-generated notifications
Triggered directly by a user action during a task.
Purpose:
- Provide immediate feedback
- Confirm results
- Surface validation or errors
Pattern:
- Sonner for success or lightweight confirmation
- Inline (Alert) for validation, warnings, or blocking errors
The message must clearly relate to the user’s action.
System-generated notifications
Triggered by the system, independent of a user action.
Purpose:
- Communicate background updates
- Inform about system status
- Surface out-of-context events
Pattern:
- Inline (Alert), anchored near relevant UI
- Persistent when time-sensitive or important
Avoid using Sonner for system-level updates that require awareness over time.
When there is no obvious relevant UI element (for example, a background sync failure or session timeout), surface the alert as a persistent full-width banner directly below the main header. Do not use a floating Sonner notification for these cases.
Sonner variant
Sonner notifications are short, floating, non-modal messages used for quick feedback after a user action. They are always transient, never blocking, and always tied to something the user just did.
When to use Sonner
Use Sonner when:
- Confirming a successful or completed action — example: “Project published”, “Saved successfully”
- The feedback is short and non-blocking
- No user action is required
- The message is directly tied to a user-initiated action
- You want to surface passive updates without interrupting workflow
Do not use Sonner for
- Errors that block progress
- Situations requiring user input
- Form validation
- Persistent guidance or contextual messaging
- System-generated events with no direct user trigger
Use Inline (Alert) instead.
Placement and behavior
Sonner notifications appear at the top-center of the screen.
Rationale: The top-right corner is reserved for primary CTAs across our solutions. The bottom-right corner is reserved for the chat component. Top-center keeps notifications prominent and visible without competing with either.
| Property | Value |
|---|---|
| Position | Top-center of the screen |
| Auto-dismiss | 5 seconds |
| Manual dismiss | Close icon |
| Persistent | Never |
Never use Sonner for errors that require resolution.
Example
Click each button to trigger a toast at the top-center of the screen. Toasts auto-dismiss after 5 seconds.
Inline variant (Alert)
Inline notifications use the Alert component and appear within the layout, near relevant UI elements. They provide contextual feedback tied to the current task or workflow. Unlike Sonner, they are structural — they occupy space in the layout rather than floating above it.
When to use Inline
Use Inline (Alert) when:
- Feedback must remain visible in context
- Displaying validation errors or warnings
- The message relates to a specific form, section, or workflow state
- The system requires user action
- You need persistent contextual guidance
- There is no direct user action to tie the message to
Inline notifications may be persistent or dismissible depending on priority.
Placement and behavior
- Appear inline with content, near the relevant UI
- Persist until resolved or dismissed (if dismissible)
- Should be visually connected to the related task or component
- When no specific UI element is the anchor (e.g. session expiry, system outage), appear as a persistent full-width banner below the main header
Choosing a visual style
Inline alerts support two visual styles. Choose based on the urgency and importance of the message — not personal preference. Do not mix styles within the same context.
| Style | Variant | When to use | Dismissible |
|---|---|---|---|
| Default | Tinted | Routine info, background updates, blocking errors, unresolved issues | No |
| Accent | Outline | Contextual warnings, guidance, acknowledged states, unresolved requirements | Context-dependent |
When unsure, start with default. Use Accent only when the message needs a distinct visual call-out.
Default (tinted)
Low-opacity status background with neutral title text. Use for persistent, non-dismissible messages, from routine updates and background info to blocking errors.
Accent (outline)
Status color on the border and icon, with default text colors. Use for contextual warnings, dismissible guidance, or messages the user has already acknowledged.
Complex alert patterns (edge cases)
The patterns above cover the majority of notification needs. The following compositions are reserved for complex workflows where a standard alert is not sufficient. Do not use these as defaults — reach for them only when the workflow genuinely requires inline items or embedded actions.
Collapsible content
When an alert references a list of items (e.g., missing documents, failed validations), show items inline as chips. If the list overflows a single row, clip to one row and show a “Show all” toggle that expands to reveal the full list.
Overflow behavior: Items display inline by default. The expand/collapse toggle only appears when items overflow the available space — keeping simple cases compact and reserving the toggle for edge cases with many items.
Upload these documents before completing quality check.
Action buttons
When an alert can trigger a direct action (e.g., filtering a table, retrying an operation), include an action button inside the alert description area. Limit to one primary action per alert.
Some documents could not be automatically classified.
Dynamic state updates
Alerts should reflect real-time state changes client-side. When a user resolves an item referenced by an alert (e.g., uploads a missing document), the alert should update immediately without requiring a page refresh:
- Update the count in the title (“3 required documents missing” becomes “2 required documents missing”)
- Remove resolved items from the collapsible list
- Auto-dismiss the alert when all items are resolved
Implement this by deriving alert visibility and content from reactive state (e.g., React state or a server state library like TanStack Query). Do not hard-code alert content.
Layout boundaries
The following areas are reserved and must not be obscured by floating notifications:
| Area | Reserved for |
|---|---|
| Top-right | Primary CTAs |
| Bottom-right | Chat component |
No Sonner notification or floating overlay should appear in these areas. Top-center is the designated position for all Sonner notifications.
Dismissible vs persistent
| Variant | Auto-dismiss | User-dismissible | Persistent |
|---|---|---|---|
| Sonner | Yes (5s) | Yes | No |
| Inline (Alert) | No | Optional | Yes |
Make Inline dismissible if
- The message is low priority — example: “Settings updated”, “You’ve been signed out from another device”
- The user has already acknowledged the information
- Dismissing improves focus and reduces clutter
Keep Inline persistent if
- The message indicates an error that must be resolved
- The system requires user input
- The message provides important ongoing context
Never rely on dismissibility for critical blockers.
Note: Visual style does not determine dismissibility. An accent (outline) alert can be either dismissible or persistent depending on whether the condition it represents requires resolution. Missing required documents, for example, should use accent styling but remain persistent until all documents are provided.
Status and semantic meaning
Each notification has a status that communicates tone and urgency through color and icon. Choose status based on both urgency and user context.
Status definitions
Notification statuses align with the Badge status system for consistent semantic meaning across the design system.
| Status | Usage | Token | Color | Sonner | Alert |
|---|---|---|---|---|---|
info | General updates, FYIs | --info | oklch(0.60 0.125 210) | toast.info() | Use status="default" |
success | Confirmation of completed tasks | --success | oklch(0.57 0.105 152) | toast.success() | Not available — Sonner only |
warning | Potential issues or unintended consequences | --warning | oklch(0.80 0.1401 80.82) | toast.warning() | status="warning" |
error | Failures, blockers, critical issues | --destructive | oklch(0.62 0.150 18) | toast.error() | status="error" |
The Alert component supports
status="default",status="warning", andstatus="error". Use Sonner for success confirmations. For persistent system guidance or background alerts, usedefaultorwarningin Alert.
Content guidelines
Notifications have limited space. Write for clarity and scanability.
Users should understand:
- What happened
- Why it matters
- What to do next
Title
- Short and clear
- Communicates the most important information
- No period at the end
- For errors, state what failed or what cannot be completed
- Examples: “Changes saved”, “Connection failed”
Accessibility note: Screen readers read the entire notification as one string. Do not rely on text styling alone to convey meaning.
Body text
- Maximum two lines
- Do not repeat the title
- Support the title with context or next steps
- Include troubleshooting guidance when relevant
- User actions are mandatory for error messages
- Never truncate critical or actionable content
If more explanation is required:
- Provide a short summary
- Add a single “View more” link to a modal or page
No bolding within body text. If emphasis is necessary, use quotation marks.
Lists and structured content
Notifications should not contain bulleted or numbered lists. Lists add visual complexity and break the scanability of a compact notification.
If the notification references multiple items:
- Summarize with a count in the title — example: “3 required documents missing”
- Show items as inline chips or a comma-separated string
- Limit visible items to one row; use a “Show all” link for overflow
- Never display more than one level of hierarchy
For details that require a full list, link out to a dedicated view with “View details”.
Call to action links
Use links sparingly.
| Guideline | Rule |
|---|---|
| Maximum links | One per notification |
| Position | End of notification only |
| Inline links | Do not embed links within body text |
| Label length | One or two words |
| Tone | Concise and action-oriented |
Examples: “View details”, “Retry”, “Fix”
Avoid driving users away from their workflow unless necessary.
Quick decision guide
Use Sonner when
- Confirming success
- The message is short
- No action is required
- The feedback is tied to a user action
Use Inline (Alert) when
- Displaying validation errors
- The issue blocks progress
- Context must persist
- The message relates to a specific UI element
- The system requires action
- There is no user action to tie the message to
If the message blocks progress, it should never use Sonner.
Choosing an Alert style
| Question | Answer | Style |
|---|---|---|
| Is the message routine, informational, or does it require immediate attention? | Yes | Default (tinted) |
| Is the message contextual, a warning, or an unresolved requirement? | Yes | Accent (outline) |
When unsure, use default. Use Accent only when the message needs a distinct visual call-out.