import React, { createContext, useContext, useState, ReactNode } from 'react'
type ToastType = 'success' | 'error' | 'info' | 'warning'
interface Toast {
id: string
message: string
type: ToastType
}
interface ToastContextType {
showToast: (message: string, type?: ToastType) => void
}
const ToastContext = createContext<ToastContextType | undefined>(undefined)
export function ToastProvider({ children }: { children: ReactNode }) {
const [toasts, setToasts] = useState<Toast[]>([])
const showToast = (message: string, type: ToastType = 'info') => {
const id = Math.random().toString(36).substring(7)
const toast = { id, message, type }
setToasts((prev) => [...prev, toast])
setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== id))
}, 5000)
}
const removeToast = (id: string) => {
setToasts((prev) => prev.filter((t) => t.id !== id))
}
const getToastStyles = (type: ToastType) => {
const styles = {
success: 'bg-green-50 border-green-200 text-green-800',
error: 'bg-red-50 border-red-200 text-red-800',
warning: 'bg-yellow-50 border-yellow-200 text-yellow-800',
info: 'bg-blue-50 border-blue-200 text-blue-800',
}
return styles[type]
}
const getIcon = (type: ToastType) => {
const icons = {
success: 'fa-check-circle text-green-500',
error: 'fa-exclamation-circle text-red-500',
warning: 'fa-exclamation-triangle text-yellow-500',
info: 'fa-info-circle text-blue-500',
}
return icons[type]
}
return (
<ToastContext.Provider value={{ showToast }}>
{children}
<div className="fixed top-4 right-4 z-50 space-y-2">
{toasts.map((toast) => (
<div
key={toast.id}
className={`flex items-center gap-3 px-4 py-3 rounded-lg shadow-lg border transition-all duration-300 ${getToastStyles(toast.type)}`}
>
<i className={`fas ${getIcon(toast.type)}`} />
<p className="flex-1">{toast.message}</p>
<button
onClick={() => removeToast(toast.id)}
className="text-gray-400 hover:text-gray-600"
>
<i className="fas fa-times" />
</button>
</div>
))}
</div>
</ToastContext.Provider>
)
}
export function useToast() {
const context = useContext(ToastContext)
if (!context) {
throw new Error('useToast must be used within ToastProvider')
}
return context
}
Toast notifications provide non-intrusive feedback for user actions. I create a toast context that manages a queue of notifications, auto-dismisses them after a timeout, and supports different types (success, error, info). The context exposes methods to show toasts from anywhere in the app. Each toast has a unique ID for tracking and removal. Animations using Tailwind's transition classes provide smooth enter/exit effects. The toast container is positioned fixed and stacks toasts vertically. I also implement dismiss gestures for touch devices and keyboard navigation. This centralized notification system ensures consistent feedback throughout the app without prop drilling or Redux.