Next.js Toast Notifications
Next.js toast notifications for the App Router: a ToastProvider in the root layout and a useToast() hook for client components, built on Bootstrap's toast.
This component is only available in AdminKit PRO. Learn more.
Toasts tell the user that something worked, or did not, without getting in their way. The message appears in a corner of the window and hides itself after a few seconds.
There are two ways to show one. The useToast() hook shows a toast with one function call. The Toast component is a single toast you place yourself. Both render Bootstrap's toast markup from React state, without Bootstrap's JavaScript. Toasts are part of the PRO version of AdminKit for Next.js. Their code is in src/components/bootstrap/toast.tsx.
The setup is already in place. ToastProvider wraps {children} in the root layout, src/app/layout.tsx, so it covers every route. The layout stays a server component: it renders on the server and has no state. ToastProvider is a client component, a file that starts with "use client", because the toasts need state.
The toasts only exist in the browser. ToastProvider renders them in a portal on document.body after hydration, once the browser has taken over the HTML that Next.js built. So the server HTML has no toast container, and the server and the browser never disagree about the markup (no hydration mismatch).
Example
useToast() reads a React context, which a server component cannot do. Call it in a client component and render that from your page:
"use client";
import { useToast } from "@/components/bootstrap/toast";
export function PublishButton() {
const showToast = useToast();
return (
<button
type="button"
className="btn btn-primary"
onClick={() =>
showToast({
message: "The post is live.",
type: "success",
dismissible: true,
})
}
>
Publish
</button>
);
}The page imports it and stays a server component:
import { PublishButton } from "@/components/publish-button";
import { pageMetadata } from "@/lib/site";
export const metadata = pageMetadata("/posts");
export default function PostsPage() {
return (
<>
<h1 className="h3 mb-3">Posts</h1>
<PublishButton />
</>
);
}Each toast fades in, hides itself after 5 seconds and is removed. useToast() throws an error outside ToastProvider. If you write your own root layout, keep <ToastProvider>{children}</ToastProvider> in it.
Options
| Option | Type | Default | Description |
|---|---|---|---|
message | ReactNode | The content of .toast-body | |
type | "default" | "primary" | "secondary" | "success" | "info" | "warning" | "danger" | "default" | The background color (text-bg-*); "default" is primary |
duration | number | 5000 | Milliseconds before it hides; 0 keeps it until it is closed |
dismissible | boolean | false | Adds a close button |
position | { x: "left" | "center" | "right"; y: "top" | "bottom" } | { x: "right", y: "top" } | Where the toast appears in the window |
Toasts at the same position share one fixed .toast-container, newest last. The close button is white on every color except warning and info, which have dark text.
Colors and positions
The color tells the user at a glance how things went. The position keeps the toast clear of what they are working on:
<button
type="button"
className="btn btn-secondary me-2"
onClick={() =>
showToast({ message: "Sync finished.", type: "secondary", position: { x: "left", y: "bottom" } })
}
>
Bottom left
</button>
<button
type="button"
className="btn btn-warning me-2"
onClick={() =>
showToast({ message: "You have used 90% of your storage.", type: "warning", position: { x: "center", y: "bottom" } })
}
>
Bottom center
</button>
<button
type="button"
className="btn btn-danger"
onClick={() =>
showToast({ message: "You are offline.", type: "danger", position: { x: "right", y: "top" } })
}
>
Top right
</button>After a request
A toast is a good way to report how a form submission went. AdminKit for Next.js is a static export (output: "export"): the build turns every page into HTML files, with no Next.js server behind them. That means there are no Server Actions. A form sends its data with fetch from a client component instead, and shows the result in a toast. showToast is the same function on every render, so it is safe in callbacks and effects:
"use client";
import { useToast } from "@/components/bootstrap/toast";
export function InviteForm() {
const showToast = useToast();
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const response = await fetch("/api/invites", {
method: "POST",
body: new FormData(event.currentTarget),
});
if (response.ok) {
showToast({ message: "Invite sent.", type: "success" });
} else {
showToast({ message: "The invite could not be sent.", type: "danger", duration: 0, dismissible: true });
}
}
return (
<form onSubmit={onSubmit} className="d-flex gap-2">
<input name="email" type="email" className="form-control" aria-label="Email" required />
<button type="submit" className="btn btn-primary">Invite</button>
</form>
);
}A single toast
For a toast with a header, or one you position yourself, use Toast: one toast with Bootstrap's fade. It needs state to know whether it is shown, so it goes in a client component too:
"use client";
import { useCallback, useState } from "react";
import { CloseButton } from "@/components/bootstrap/close-button";
import { Toast } from "@/components/bootstrap/toast";
export function StartBackup() {
const [show, setShow] = useState(false);
const close = useCallback(() => setShow(false), []);
return (
<>
<button type="button" className="btn btn-primary" onClick={() => setShow(true)}>
Start backup
</button>
<Toast show={show} onClose={close} className="mt-3">
<div className="toast-header">
<strong className="me-auto">Backups</strong>
<CloseButton onClick={close} />
</div>
<div className="toast-body">The backup is running. It takes a few minutes.</div>
</Toast>
</>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
show | boolean | Whether the toast is shown | |
onClose | () => void | Called when the autohide delay has passed; set show to false here | |
onExited | () => void | Called once the toast has faded out | |
autohide | boolean | true | Hides the toast after delay |
delay | number | 5000 | Milliseconds from the end of the fade-in to onClose |
className | string | Extra classes for .toast |
Every change of onClose restarts the delay, so wrap the function in useCallback. For a toast in a corner, put it in <div className="toast-container position-fixed top-0 end-0 p-3">.
Accessibility
- Toasts have
role="alert",aria-live="assertive"andaria-atomic="true": screen readers announce them when they appear. Keep the message short. - The focus stays where it is. Errors and messages with an action should not hide on their own: use
duration: 0anddismissible: true, or a modal. - The close button has
aria-label="Close".
Related
- React Bootstrap toasts: the same component in AdminKit for React (Vite)
- Bootstrap 5 toasts: the toasts and
showToast()of the HTML version - Alerts: messages that stay in the page
- Notifications demo
Last updated on
Tables
Next.js Bootstrap table markup rendered on the server: rows from an array with map and key, striped and hover tables, row colors, tables in cards.
TooltipsPro
A Next.js Bootstrap tooltip for the App Router: wrap an element in a client component for a tooltip on hover and focus, positioned by Floating UI.