Toast Notification System
Custom toast component, queue management, animations, and accessibility
What You’ll Build
After following this guide, you will have a working implementation of toast notification system in your project. Build a lightweight toast notification system or use the battle-tested sonner library. Toasts stack from the bottom, auto-dismiss, support different types (success, error, info), and are accessible with proper ARIA roles.
Use Cases & Problems Solved
- Implement interactive UI features that users expect from modern apps
- Follow established patterns that scale and remain maintainable
- Reduce boilerplate and avoid common frontend pitfalls
Prerequisites
- React 18+
Step-by-Step Implementation
Using Sonner (recommended)
The following snippet shows how to using sonner (recommended). Copy this into your project and adjust the values for your environment.
npm install sonner
Setup and usage
The following snippet shows how to setup and usage. Copy this into your project and adjust the values for your environment.
// app/layout.tsx or App.tsx
import { Toaster, toast } from 'sonner';
function App() {
return (
<>
<Toaster position="bottom-right" richColors expand />
<button onClick={() => toast.success('Profile saved!')}>Save</button>
<button onClick={() => toast.error('Failed to delete')}>Delete</button>
<button onClick={() => toast.promise(saveData(), {
loading: 'Saving...',
success: 'Data saved!',
error: 'Could not save',
})}>Save with Promise</button>
</>
);
}
// Use from anywhere — even outside React components
import { toast } from 'sonner';
async function handleUpload(file) {
try {
await uploadFile(file);
toast.success('File uploaded successfully');
} catch (err) {
toast.error(\`Upload failed: \${err.message}\`);
}
}
⚠️ Don’t Do This
❌ Using alert() for user feedback
// Blocks the UI thread! User can't interact until dismissed
alert('Profile saved successfully!');
alert('Error: Could not delete user');
✅ Use non-blocking toast notifications
toast.success('Profile saved!');
tost.error('Could not delete user');
// User can continue working while notification shows
Testing
Verify your implementation with these tests:
// __tests__/toast-notification-system.test.ts
import { describe, it, expect } from 'vitest';
describe('Toast Notification System', () => {
it('should initialize without errors', () => {
// Test that the setup completes successfully
expect(() => setup()).not.toThrow();
});
it('should handle the primary use case', async () => {
const result = await execute();
expect(result).toBeDefined();
expect(result.success).toBe(true);
});
it('should handle edge cases', async () => {
// Test with empty/null input
const result = await execute(null);
expect(result.error).toBeDefined();
});
});
Verification
# Click each button type (success, error, promise)
# Verify toast appears in bottom-right corner
# Verify auto-dismiss after ~4 seconds
# Verify multiple toasts stack properly
# Test keyboard accessibility: toast should be aria-live Related Specs
Next.js Server Actions Guide
Form mutations, revalidation, optimistic updates, and error handling