Toast Notifications with MUI Snackbar
This guide shows how to use MUI's Snackbar component for toast notifications in the application.
Why MUI Snackbar?
- ✅ Already in dependencies (no extra packages)
- ✅ Matches application design system
- ✅ Full TypeScript support
- ✅ Accessible by default
- ✅ Flexible and customizable
Basic Usage
Simple Toast
import { useState } from 'react';
import { Snackbar, Alert, Button } from '@mui/material';
function MyComponent() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Show Toast</Button>
<Snackbar
open={open}
autoHideDuration={6000}
onClose={() => setOpen(false)}
>
<Alert severity="success" onClose={() => setOpen(false)}>
Operation successful!
</Alert>
</Snackbar>
</>
);
}
With State Management Hook
Using useBoolean from usehooks-ts for cleaner state management:
import { useBoolean } from 'usehooks-ts';
import { Snackbar, Alert, Button } from '@mui/material';
function MyComponent() {
const { value: open, setTrue: show, setFalse: hide } = useBoolean();
return (
<>
<Button onClick={show}>Show Toast</Button>
<Snackbar open={open} autoHideDuration={6000} onClose={hide}>
<Alert severity="success" onClose={hide}>
Success!
</Alert>
</Snackbar>
</>
);
}
Severity Variants
Alert supports four severity levels:
<Alert severity="success">Success message</Alert>
<Alert severity="error">Error message</Alert>
<Alert severity="warning">Warning message</Alert>
<Alert severity="info">Info message</Alert>
Common Patterns
After Form Submission
import { useBoolean } from 'usehooks-ts';
import { Snackbar, Alert } from '@mui/material';
function SaveButton() {
const { value: success, setTrue: showSuccess } = useBoolean();
const { value: error, setTrue: showError, setFalse: hideError } = useBoolean();
const [errorMessage, setErrorMessage] = useState('');
const handleSave = async () => {
try {
await saveData();
showSuccess();
} catch (err) {
setErrorMessage(err instanceof Error ? err.message : 'Save failed');
showError();
}
};
return (
<>
<Button onClick={handleSave}>Save</Button>
<Snackbar open={success} autoHideDuration={3000}>
<Alert severity="success">Saved successfully!</Alert>
</Snackbar>
<Snackbar open={error} onClose={hideError}>
<Alert severity="error" onClose={hideError}>
{errorMessage}
</Alert>
</Snackbar>
</>
);
}
With Offline Detection
import { useOnlineStatus } from '@/hooks/core/network';
import { Snackbar, Alert } from '@mui/material';
function AppLayout({ children }) {
const { isOnline, wasOffline } = useOnlineStatus();
return (
<>
{children}
{/* Offline warning */}
<Snackbar open={!isOnline}>
<Alert severity="warning">
You're offline. Changes will sync when reconnected.
</Alert>
</Snackbar>
{/* Reconnection notification */}
<Snackbar
open={wasOffline && isOnline}
autoHideDuration={4000}
>
<Alert severity="success">
Connection restored!
</Alert>
</Snackbar>
</>
);
}
Async Operations
function AsyncAction() {
const [loading, setLoading] = useState(false);
const { value: success, setTrue: showSuccess } = useBoolean();
const { value: error, setTrue: showError } = useBoolean();
const handleAction = async () => {
setLoading(true);
try {
await performAction();
showSuccess();
} catch (err) {
showError();
} finally {
setLoading(false);
}
};
return (
<>
<Button onClick={handleAction} disabled={loading}>
{loading ? 'Processing...' : 'Perform Action'}
</Button>
<Snackbar open={success} autoHideDuration={3000}>
<Alert severity="success">Action completed!</Alert>
</Snackbar>
<Snackbar open={error}>
<Alert severity="error">Action failed</Alert>
</Snackbar>
</>
);
}
Positioning
Control toast position with anchorOrigin:
<Snackbar
open={open}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
>
<Alert>Top right toast</Alert>
</Snackbar>
Options:
vertical: 'top' | 'bottom'horizontal: 'left' | 'center' | 'right'
Auto-Dismiss
// Auto-dismiss after 6 seconds (default)
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
// No auto-dismiss (user must close manually)
<Snackbar open={open}>
// Quick dismiss (3 seconds)
<Snackbar open={open} autoHideDuration={3000} onClose={handleClose}>
Custom Actions
<Snackbar open={open} onClose={handleClose}>
<Alert
severity="info"
action={
<Button color="inherit" size="small" onClick={handleUndo}>
UNDO
</Button>
}
>
Item deleted
</Alert>
</Snackbar>
Multiple Toasts
For managing multiple toasts, use an array in state:
interface ToastMessage {
id: string;
message: string;
severity: 'success' | 'error' | 'info' | 'warning';
}
function MultiToast() {
const [toasts, setToasts] = useState<ToastMessage[]>([]);
const addToast = (message: string, severity: ToastMessage['severity']) => {
const id = Date.now().toString();
setToasts(prev => [...prev, { id, message, severity }]);
};
const removeToast = (id: string) => {
setToasts(prev => prev.filter(toast => toast.id !== id));
};
return (
<>
<Button onClick={() => addToast('Success!', 'success')}>
Add Toast
</Button>
{toasts.map((toast) => (
<Snackbar
key={toast.id}
open={true}
autoHideDuration={6000}
onClose={() => removeToast(toast.id)}
>
<Alert severity={toast.severity} onClose={() => removeToast(toast.id)}>
{toast.message}
</Alert>
</Snackbar>
))}
</>
);
}
Best Practices
-
Use appropriate severity
success: Completed actionserror: Failed operationswarning: Important noticesinfo: General information
-
Set reasonable auto-dismiss times
- Success: 3-4 seconds
- Error: Manual dismiss (or longer duration)
- Info: 4-6 seconds
- Warning: Manual dismiss
-
Keep messages concise
- Short, actionable messages
- Avoid technical jargon
- Be specific about what happened
-
Provide actions when needed
- Undo for destructive actions
- Retry for failed operations
- Links for more details