← Components/Notifications

ToastStack

toaststack-1779378817369.tsx
'use client'
import { useState, useCallback } from 'react'

type ToastType = 'success' | 'error' | 'warning' | 'info'

interface Toast {
  id: number
  type: ToastType
  title: string
  message?: string
}

const CONFIGS = {
  success: { icon: '✓', color: '#22c55e', bg: 'rgba(34,197,94,0.1)', border: 'rgba(34,197,94,0.2)' },
  error: { icon: '✕', color: '#ef4444', bg: 'rgba(239,68,68,0.1)', border: 'rgba(239,68,68,0.2)' },
  warning: { icon: '⚠', color: '#f59e0b', bg: 'rgba(245,158,11,0.1)', border: 'rgba(245,158,11,0.2)' },
  info: { icon: 'ℹ', color: '#6366f1', bg: 'rgba(99,102,241,0.1)', border: 'rgba(99,102,241,0.2)' },
}

const EXAMPLES: { type: ToastType; title: string; message?: string }[] = [
  { type: 'success', title: 'Component copied!', message: 'Code is ready to paste into your project.' },
  { type: 'error', title: 'Build failed', message: 'TypeScript error in Line 42. Check the console.' },
  { type: 'warning', title: 'Rate limit approaching', message: '85% of your monthly API quota used.' },
  { type: 'info', title: 'New components added', message: '47 new components published this week.' },
]

let nextId = 1

export default function ToastStack() {
  const [toasts, setToasts] = useState<Toast[]>([])

  const add = useCallback((type: ToastType, title: string, message?: string) => {
    const id = nextId++
    setToasts(t => [...t, { id, type, title, message }])
    setTimeout(() => remove(id), 4000)
  }, [])

  function remove(id: number) {
    setToasts(t => t.filter(x => x.id !== id))
  }

  return (
    <div style={{ background: '#0A0A0A', padding: 40, minHeight: 360, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
      <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', justifyContent: 'center', marginBottom: 40 }}>
        {(Object.keys(CONFIGS) as ToastType[]).map(type => (
          <button key={type} onClick={() => { const ex = EXAMPLES.find(e => e.type === type)!; add(type, ex.title, ex.message) }}
            style={{ background: CONFIGS[type].bg, border: `1px solid ${CONFIGS[type].border}`, color: CONFIGS[type].color, borderRadius: 8, padding: '8px 16px', cursor: 'pointer', fontSize: 13, fontWeight: 600, textTransform: 'capitalize' }}>
            {type}
          </button>
        ))}
      </div>

      <div style={{ position: 'fixed', bottom: 24, right: 24, display: 'flex', flexDirection: 'column', gap: 10, zIndex: 9999, pointerEvents: 'none', width: 340 }}>
        {toasts.map((toast) => {
          const c = CONFIGS[toast.type]
          return (
            <div key={toast.id} style={{
              background: '#1a1a1a', border: `1px solid ${c.border}`, borderLeft: `3px solid ${c.color}`,
              borderRadius: 10, padding: '14px 16px', display: 'flex', gap: 12, alignItems: 'flex-start',
              pointerEvents: 'all', animation: 'slideIn 0.3s ease', cursor: 'pointer',
              boxShadow: '0 8px 32px rgba(0,0,0,0.5)',
            }} onClick={() => remove(toast.id)}>
              <div style={{ width: 20, height: 20, borderRadius: '50%', background: c.bg, border: `1px solid ${c.border}`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: c.color, fontSize: 11, fontWeight: 700, flexShrink: 0 }}>
                {c.icon}
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ color: '#F5F5F0', fontSize: 13, fontWeight: 600 }}>{toast.title}</div>
                {toast.message && <div style={{ color: 'rgba(255,255,255,0.45)', fontSize: 12, marginTop: 2, lineHeight: 1.5 }}>{toast.message}</div>}
              </div>
              <span style={{ color: 'rgba(255,255,255,0.3)', fontSize: 16, lineHeight: 1 }}>×</span>
            </div>
          )
        })}
      </div>

      <div style={{ color: 'rgba(255,255,255,0.3)', fontSize: 13 }}>Click buttons above to show toasts</div>
      <style>{`@keyframes slideIn { from { opacity: 0; transform: translateX(20px) } to { opacity: 1; transform: translateX(0) } }`}</style>
    </div>
  )
}

Component info

CategoryNotifications
Frameworkreact
TierFREE
Views0
Copies0

About

Animated toast notification stack with different types, auto-dismiss, and drag-to-dismiss