Skip to content

Conversation

@gadenbuie
Copy link
Member

@gadenbuie gadenbuie commented Oct 27, 2025

Introduces a comprehensive toast notification system for Shiny applications, including toast() (with toast_header() helper), show_toast() and hide_toast().

Toasts are small notification messages that are shown temporarily on screen. By default they autohide after 5 seconds, but this delay can be customized. bslib toasts have a few interesting features above and beyond the Bootstrap standard toasts:

  1. A progress border give the user a sense of how long the toast will remain on screen
  2. Hovering over an auto-hiding toast will pause the auto-hide timer, giving users a way to keep a toast on screen as long as they need (or if they're interacting with it)
  3. Toasts that are not auto-hiding can be hidden with server logic: show_toast() returns the ID of the toast that was shown (either auto-generated or from the toast object being shown). This ID can then be used with hide_toast() to hide the toast.
  4. Toasts can be positioned in any combination of top, middle, bottom and left, center, right. We automatically group toasts together if another toast in the same position already exists.

bslib toasts come with an additional demo app to try out all of the features:

shiny::runExample("toast", package = "bslib")

Examples

Basic toast

show_toast(toast("This is a toast notification!"))

# or more simply
show_toast("This is a toast notification!")
image

Toast with header

show_toast(
  toast(
    "This is a toast notification!",
    header = toast_header(
      title = "Notification Title",
      icon = bsicons::bs_icon("star"),
      status = "16s ago"
    )
  )
)
image

Toast with header and type = "success"

show_toast(
  toast(
    "Your profile has been updated successfully",
    header = toast_header(
      title = "Profile updated",
      icon = bsicons::bs_icon("check", class = "text-success"),
      status = "just now"
    )
  )
)
image

Toast with Shiny controls

show_toast(
  toast(
    id = "unsaved_changes_toast",
    p("Would you like to save your changes?"),
    actionButton("save_yes", "Save", class = "btn-sm btn-primary me-2"),         
    actionButton(
      "save_no",
      "Don't Save",
      class = "btn-sm btn-secondary"
    ),
    header = "Unsaved Changes",
    type = "warning",
    duration_s = 0
  )
)
image

gadenbuie and others added 30 commits October 24, 2025 14:43
Implement core R functions for toast notifications:
- toast(): Create toast objects with customizable options
- show_toast(): Display toasts in Shiny apps
- hide_toast(): Manually dismiss toasts
- toast_header(): Create structured headers with icons/status

Follows bslib component patterns with object-oriented design
and server-side creation model.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implement client-side toast functionality:
- ToastContainerManager for auto-creating position-specific containers
- showToast handler to display toasts with Bootstrap Toast API
- hideToast handler to manually dismiss toasts
- Auto-cleanup of toast elements and empty containers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add minimal custom styles for toast component:
- CSS variable for runtime theming support
- Ensure close button visibility on colored backgrounds

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Import toast component to ensure message handlers are registered
when the components bundle is loaded.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Test coverage includes:
- Toast object creation and validation
- Position and type argument validation
- Accessibility attribute generation
- Close button rendering logic
- HTML structure generation
- toast_header() functionality
- All type and position options

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Create comprehensive demo app showcasing toast features:
- Basic toasts with different types (success, error, warning, info)
- Position options (all 9 positions)
- Advanced features (persistent, long duration, no close button)
- Interactive toasts with action buttons
- Multiple toasts and stacking behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Change from direct import to window.bootstrap access pattern:
- Import Toast type only, not implementation
- Access Toast from window.bootstrap with class fallback
- Add console warning if Bootstrap is not available
- Follows pattern used by tooltip and popover components

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Replace basic examples with comprehensive toast builder:
- Interactive form with all toast options
- input_switch() for binary settings (header, icon, status, autohide, etc)
- Conditional panels to show/hide relevant options
- Select menus for type, position, and icon choices
- Slider for duration control
- Custom ID option for manual toast management
- Two buttons: show toast and hide last toast
- Keep advanced features and interactive toast examples

Users can now experiment with all toast parameters interactively.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Change the status parameter in toast_header() from a colored
indicator to status text (e.g., "11 mins ago", "just now") that
appears as small muted text in the header, matching Bootstrap's
toast examples.

- Remove validation for status parameter
- Render status as <small class="text-muted"> instead of badge
- Update documentation and examples
- Update tests to match new behavior
Replace separate autohide (logical) and duration (milliseconds)
parameters with single autohide_s parameter in seconds:
- autohide_s = 0, NA, or NULL disables auto-hiding
- autohide_s > 0 enables auto-hiding after N seconds
- Internally converts to milliseconds for Bootstrap Toast
- Simplifies API: one parameter instead of two

Updates:
- toast() function signature and implementation
- All examples in demo app
- All tests to use new parameter

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add two interactive features for autohiding toasts:

1. Progress Bar Animation:
   - Animated bar at top of toast shows remaining time
   - Uses CSS transform animation for smooth performance
   - Gradient styling with primary color theme integration
   - Automatically added only to autohiding toasts

2. Hover Pause Behavior:
   - Mouse hover pauses the auto-hide timer
   - Progress bar animation pauses when hovered
   - Both resume when mouse leaves
   - Override Bootstrap's hide() to check hover state

Implementation:
- addProgressBar(): Creates and styles progress element
- setupHoverPause(): Configures hover event handlers
- CSS keyframe animation for progress bar
- Toast positioned relative with overflow hidden

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Change progress bar animation from emptying (1 → 0) to filling
(0 → 1) to better indicate time passing. Bar now grows from left
to right as the toast approaches auto-hide time.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Extract random ID generation into `toast_random_id()` helper function
- Replace duplicate ID generation logic with centralized helper
- Simplify `show_toast()` by using `processDeps()` instead of manual HTML rendering and dependency resolution
- Remove redundant comments and streamline code flow
…simplify implementation

- Consolidate documentation for `show_toast()` and `hide_toast()` into a single help page using `@describeIn`
- Remove `session$onFlush()` callbacks in favor of immediate `sendCustomMessage()` calls
- Make `hide_toast()` accept toast objects with IDs and return the ID consistently
- Add error handling for hiding toasts without IDs
- Update documentation examples and parameter descriptions
- Add tests for `show_toast()` return value and `hide_toast()` functionality
Comment on lines +272 to +276
observeEvent(input$hide_toast, {
req(last_toast_id())
hide_toast(last_toast_id())
last_toast_id(NULL)
})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I click 'show' repeatedly, then 'hide' repeatedly, only the 1st hide works (since we're only tracking the last ID).

Related to this problem, would it make sense for hide_toast() to remove the "oldest" toast? Or provide access to a input[["toast_id"]] == T/F ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's why I labelled the button "Hide Last Toast". We certainly could keep track of a stack of toasts, but I don't think it's worth it for this example app.

I'm also fairly strongly against creating inputs for toasts to track their state. Toasts are primarily ephemeral by design; I don't think it's a good idea to add to the input namespace for something that is intended to be short-lived in the app.

I could be convinced to add a toast_state(id) helper that would return a reactive value indicating whether a toast with the given ID is currently visible or not. But I don't think it's a necessary feature so I'd rather hold off on that for now.

@gadenbuie gadenbuie requested a review from cpsievert November 3, 2025 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants