A lightweight, customizable toast notification system for React applications with TypeScript support.
npm install react-universal-toast
Choose where toasts appear on the screen:
# Install the package
npm install react-universal-toast
// Setup in your App.js
import React from 'react';
import { ToastProvider } from 'react-universal-toast';
import 'react-universal-toast/dist/styles.css';
function App() {
return (
<ToastProvider placement="top-right">
{/* Your app components */}
</ToastProvider>
);
}
import { useToast } from 'react-universal-toast';
function MyComponent() {
const { show, remove, clear } = useToast();
const handleSuccess = () => {
const id = show('Success! Operation completed.', { type: 'success' });
// Auto-remove after 3 seconds
setTimeout(() => remove(id), 3000);
};
return (
<div>
<button onClick={handleSuccess}>Show Success</button>
<button onClick={() => show('Error!', { type: 'error' })}>Show Error</button>
<button onClick={clear}>Clear All</button>
</div>
);
}
import { toast } from 'react-universal-toast';
function MyComponent() {
const handleSubmit = async () => {
try {
await api.submit();
toast.success('Data saved successfully!');
} catch (error) {
toast.error('Failed to save data');
}
};
return <button onClick={handleSubmit}>Submit</button>;
}
import { toast } from 'react-universal-toast';
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
if (!email || !password) {
toast.error('Please fill in all fields');
return;
}
if (!email.includes('@')) {
toast.error('Please enter a valid email');
return;
}
try {
toast.info('Logging in...');
await login(email, password);
toast.success('Welcome back!');
} catch (error) {
toast.error('Invalid credentials');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
);
}
import { ToastType, Placement, useToast } from 'react-universal-toast';
interface ApiResponse {
success: boolean;
message: string;
}
function TypedComponent() {
const { show } = useToast();
const handleApiCall = async () => {
try {
const response: ApiResponse = await fetch('/api/data').then(r => r.json());
const toastType: ToastType = response.success ? 'success' : 'error';
show(response.message, { type: toastType });
} catch (error) {
show('Network error occurred', { type: 'error' });
}
};
return <button onClick={handleApiCall}>Fetch Data</button>;
}
No, you should only have one ToastProvider at the root of your application. The toast system uses a singleton pattern to manage state globally.
You can override the CSS classes. Here's an example:
.toast {
background: #your-color;
border-radius: 12px;
font-weight: bold;
}
.toast-success {
background: linear-gradient(45deg, #10B981, #059669);
}
Yes! The show()
method accepts ReactNode:
const customToast = (
<div>
<strong>Custom Toast</strong>
<p>With JSX content!</p>
</div>
);
show(customToast, { type: 'info' });
Toasts don't auto-dismiss by default. You can implement this manually:
const { show, remove } = useToast();
const showAutoToast = () => {
const id = show('Auto-dismissing toast', { type: 'info' });
setTimeout(() => remove(id), 3000); // Remove after 3 seconds
};
The toasts are keyboard accessible and clickable. For screen reader support, consider adding ARIA labels to your toast messages for better accessibility.
Yes! Here's how to set it up with the App Router:
// app/layout.js
import { ToastProvider } from 'react-universal-toast';
import 'react-universal-toast/dist/styles.css';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<ToastProvider placement="top-right">
{children}
</ToastProvider>
</body>
</html>
);
}
The library is fully compatible with Server-Side Rendering. It handles hydration properly and works with Next.js, Gatsby, and other SSR frameworks.
The library is extremely lightweight with zero runtime dependencies. The entire package is under 5KB gzipped, making it perfect for performance-conscious applications.
Absolutely! Contributions are welcome. Check out the GitHub repository for contribution guidelines and open issues.