javascript erb 112 lines · 3 tabs

Toast notifications with Stimulus and Tailwind

Jordan Lee Jan 2026
3 tabs
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["container"]

  connect() {
    // Make toast controller globally available
    window.toast = this
  }

  show(message, type = 'info', duration = 5000) {
    const toast = this.createToast(message, type)
    this.containerTarget.appendChild(toast)

    // Trigger animation
    requestAnimationFrame(() => {
      toast.classList.remove('translate-x-full', 'opacity-0')
    })

    // Auto dismiss
    if (duration > 0) {
      setTimeout(() => {
        this.dismiss(toast)
      }, duration)
    }
  }

  createToast(message, type) {
    const toast = document.createElement('div')
    toast.className = `toast toast-${type} flex items-center gap-3 p-4 rounded-lg shadow-lg mb-4 transform translate-x-full opacity-0 transition-all duration-300`

    const icons = {
      success: '<i class="fas fa-check-circle text-green-500"></i>',
      error: '<i class="fas fa-exclamation-circle text-red-500"></i>',
      warning: '<i class="fas fa-exclamation-triangle text-yellow-500"></i>',
      info: '<i class="fas fa-info-circle text-blue-500"></i>'
    }

    const colors = {
      success: 'bg-green-50 border-green-200',
      error: 'bg-red-50 border-red-200',
      warning: 'bg-yellow-50 border-yellow-200',
      info: 'bg-blue-50 border-blue-200'
    }

    toast.classList.add(colors[type] || colors.info)

    toast.innerHTML = `
      ${icons[type] || icons.info}
      <p class="flex-1">${message}</p>
      <button type="button" class="text-gray-400 hover:text-gray-600" data-action="toast#dismissTarget">
        <i class="fas fa-times"></i>
      </button>
    `

    return toast
  }

  dismissTarget(event) {
    const toast = event.target.closest('.toast')
    this.dismiss(toast)
  }

  dismiss(toast) {
    toast.classList.add('translate-x-full', 'opacity-0')
    setTimeout(() => {
      toast.remove()
    }, 300)
  }

  success(message) {
    this.show(message, 'success')
  }

  error(message) {
    this.show(message, 'error')
  }

  warning(message) {
    this.show(message, 'warning')
  }

  info(message) {
    this.show(message, 'info')
  }
}
3 files · javascript, erb Explain with highlit

Toast notifications provide non-intrusive feedback for user actions. I build a notification system with Stimulus that manages a stack of toasts, auto-dismisses them after a timeout, and supports manual dismissal. Notifications are queued when multiple fire simultaneously, with smooth enter/exit animations. Each toast has a type (success, error, info) that determines its color and icon. The controller exposes a global method that other controllers can call to show notifications. I also integrate with Turbo Stream responses so server actions can trigger toasts. The notification container is positioned fixed and stacks toasts vertically. Accessibility is maintained with ARIA live regions that announce messages to screen readers.


Related snips

erb
<form data-controller="query-sync" data-action="change->query-sync#apply">
  <select name="status" class="rounded border p-2">
    <option value="">Any</option>
    <option value="open">Open</option>
    <option value="closed">Closed</option>
  </select>

Filter UI that syncs query params via Stimulus (no front-end router)

rails hotwire stimulus
by Henry Kim 2 tabs
javascript
import { Controller } from "@hotwired/stimulus"
import Mousetrap from "mousetrap"

export default class extends Controller {
  connect() {
    // Global shortcuts

Keyboard shortcuts with Stimulus and Mousetrap

stimulus javascript ux
by Jordan Lee 2 tabs
javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["form"]
  static values = { delay: { type: Number, default: 250 } }

Debounced live search with Stimulus + Turbo Streams

rails hotwire stimulus
by codesnips 4 tabs
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Form Validation Example</title>
  <style>

HTML forms with validation and accessibility

html forms validation
by Alex Chang 1 tab
javascript
import { Application } from "@hotwired/stimulus"
import FormSubmitController from "./controllers/form_submit_controller"

const application = Application.start()
application.debug = false

Disable submit button while Turbo form is submitting

rails hotwire stimulus
by codesnips 3 tabs
javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["modal", "message", "confirm", "cancel"]
  static values = {
    message: String

Turbo confirmation dialogs with custom modal

hotwire turbo stimulus
by Jordan Lee 3 tabs

Share this code

Here's the card — post it anywhere.

Toast notifications with Stimulus and Tailwind — share card
Link copied