Snackbars (also known as toasts) are used for brief notifications of processes that have been or will be performed.
The Snackbar component appears temporarily and floats above the UI to provide users with (non-critical) updates on an appās processes. The demo below, inspired by Google Keep, shows a basic Snackbar with a text element and two actions:
{{ādemoā: āSimpleSnackbar.jsā}}
Snackbars differ from Alerts in that Snackbars have a fixed position and a high z-index, so theyāre intended to break out of the document flow; Alerts, on the other hand, are usually part of the flowāexcept when theyāre used as children of a Snackbar.
Snackbars also from differ from Dialogs in that Snackbars are not intended to convey critical information or block the user from interacting with the rest of the app; Dialogs, by contrast, require input from the user in order to be dismissed.
import Snackbar from '@mui/material/Snackbar';
Use the anchorOrigin
prop to control the Snackbarās position on the screen.
{{ādemoā: āPositionedSnackbar.jsā}}
import SnackbarContent from '@mui/material/SnackbarContent';
Use the Snackbar Content component to add text and actions to the Snackbar.
{{ādemoā: āLongTextSnackbar.jsā}}
Use the autoHideDuration
prop to automatically trigger the Snackbarās onClose
function after a set period of time (in milliseconds).
Make sure to provide sufficient time for the user to process the information displayed on it.
{{ādemoā: āAutohideSnackbar.jsā}}
You can use the TransitionComponent
prop to change the transition of the Snackbar from Grow (the default) to others such as Slide.
{{ādemoā: āTransitionsSnackbar.jsā}}
Use an Alert inside a Snackbar for messages that communicate a certain severity.
{{ādemoā: āCustomizedSnackbars.jsā}}
If youāre using a Floating Action Button on mobile, Material Design recommends positioning snackbars directly above it, as shown in the demo below:
import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import CssBaseline from '@mui/material/CssBaseline';
import GlobalStyles from '@mui/material/GlobalStyles';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
import MenuIcon from '@mui/icons-material/Menu';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import Fab from '@mui/material/Fab';
import AddIcon from '@mui/icons-material/Add';
import Snackbar from '@mui/material/Snackbar';
export default function FabIntegrationSnackbar() {
return (
<React.Fragment>
<CssBaseline />
<GlobalStyles
styles={(theme) => ({
body: { backgroundColor: theme.palette.background.paper },
})}
/>
<div>
<AppBar position="static" color="primary">
<Toolbar>
<IconButton
edge="start"
sx={{ mr: 2 }}
color="inherit"
aria-label="menu"
>
<MenuIcon />
</IconButton>
<Typography variant="h6" color="inherit" component="div">
App bar
</Typography>
</Toolbar>
</AppBar>
<Fab
color="secondary"
sx={(theme) => ({
position: 'absolute',
bottom: theme.spacing(2),
right: theme.spacing(2),
})}
>
<AddIcon />
</Fab>
<Snackbar
open
autoHideDuration={6000}
message="Archived"
action={
<Button color="inherit" size="small">
Undo
</Button>
}
sx={{ bottom: { xs: 90, sm: 0 } }}
/>
</div>
</React.Fragment>
);
}
This demo shows how to display multiple Snackbars without stacking them by using a consecutive animation.
{{ādemoā: āConsecutiveSnackbars.jsā}}
With an imperative API, notistack lets you vertically stack multiple Snackbars without having to handle their open and close states. Even though this is discouraged in the Material Design guidelines, it is still a common pattern.
import * as React from 'react';
import Button from '@mui/material/Button';
import { SnackbarProvider, VariantType, useSnackbar } from 'notistack';
function MyApp() {
const { enqueueSnackbar } = useSnackbar();
const handleClick = () => {
enqueueSnackbar('I love snacks.');
};
const handleClickVariant = (variant: VariantType) => () => {
// variant could be success, error, warning, info, or default
enqueueSnackbar('This is a success message!', { variant });
};
return (
<React.Fragment>
<Button onClick={handleClick}>Show snackbar</Button>
<Button onClick={handleClickVariant('success')}>Show success snackbar</Button>
</React.Fragment>
);
}
export default function IntegrationNotistack() {
return (
<SnackbarProvider maxSnack={3}>
<MyApp />
</SnackbarProvider>
);
}
Note that notistack prevents Snackbars from being closed by pressing Escape.
The user should be able to dismiss Snackbars by pressing Escape. If there are multiple instances appearing at the same time and you want Escape to dismiss only the oldest one thatās currently open, call event.preventDefault
in the onClose
prop.
export default function MyComponent() {
const [open, setOpen] = React.useState(true);
return (
<React.Fragment>
<Snackbar
open={open}
onClose={(event, reason) => {
// `reason === 'escapeKeyDown'` if `Escape` was pressed
setOpen(false);
// call `event.preventDefault` to only close one Snackbar at a time.
}}
/>
<Snackbar open={open} onClose={() => setOpen(false)} />
</React.Fragment>
);
}
The Snackbar component is composed of a root <div>
that houses interior elements like the Snackbar Content and other optional components (such as buttons or decorators).
<div role="presentation" class="MuiSnackbar-root">
<div class="MuiPaper-root MuiSnackbarContent-root" role="alert">
<div class="MuiSnackbarContent-message">
<!-- Snackbar content goes here -->
</div>
</div>
</div>
You can create and manipulate notifications imperatively with the useNotifications()
API in @toolpad/core
. This API provides state management for opening and closing snackbars. It also allows for queueing multiple notifications at once.
import * as React from 'react';
import {
useNotifications,
NotificationsProvider,
} from '@toolpad/core/useNotifications';
import Button from '@mui/material/Button';
function NotifyButton() {
const notifications = useNotifications();
const [online, setOnline] = React.useState(true);
const prevOnline = React.useRef(online);
React.useEffect(() => {
if (prevOnline.current === online) {
return () => {};
}
prevOnline.current = online;
// preview-start
const key = online
? notifications.show('You are now online', {
severity: 'success',
autoHideDuration: 3000,
})
: notifications.show('You are now offline', {
severity: 'error',
});
return () => {
notifications.close(key);
};
// preview-end
}, [notifications, online]);
return <Button onClick={() => setOnline((prev) => !prev)}>Notify</Button>;
}
export default function ToolpadNotificationsNoSnap() {
return (
<NotificationsProvider>
<NotifyButton />
</NotificationsProvider>
);
}