Toast Notifications
A cookie-based toast system that lets a Server Action send a one-shot message to the
client, surviving a redirect(). The server writes a short-lived cookie; the next page
render reads it, fires the toast, and clears the cookie so it shows exactly once.
It is library-agnostic and dependency-free (no js-cookie — it reads
document.cookie directly). You plug in whatever toast UI you already use.
Reference files (read as needed):
- architecture.md — how it works, cookie config, security, debugging
- patterns.md — when/how to use it + complete examples (redirect vs. client-handled, validation, bulk, upload) and the do/don't list
Copy-into-project source lives under
assets/lib/toast/.
A redirect response can't carry data back to the component, but a cookie survives the navigation and auto-expires. See architecture.md for the full rationale, cookie config, and security notes.
Installation (run when the toast system isn't present yet)
Check whether the project already has it. Search for
setToastCookieor alib/toast/(or similar) folder. If it exists, skip to Usage — don't duplicate it.Copy the four bundled files from this skill's
assets/lib/toast/into the project (alib/toast/folder is the conventional home, but anywhere works):<project>/lib/toast/ ├── constants.ts # cookie name + max-age (seconds) ├── types.ts # ToastType, ToastMessage ├── server/toast.cookie.ts # setToastCookie() — server-only └── components/toast-handler.tsx # ToastHandler — client, reads cookie on route changeThe files import each other with relative paths, so they work no matter what path alias (
@/,~/, …) the project uses.Wire your toast library into
toast-handler.tsx(two marked steps inside the file): replaceREPLACE_WITH_YOUR_TOAST_LIBRARYwith your import and adjust theswitchif your library lacks.warning/.info. Recommended:@szum-tech/design-system(exposes.success/.error/.warning/.info); alternatives includesonnerorreact-hot-toast.Mount
<ToastHandler />once near the app root, alongside your toast library's container, so it runs on every route change:// app/layout.tsx (or your providers component) import { ToastHandler } from "@/lib/toast/components/toast-handler"; // import { Toaster } from "sonner"; // your library's container export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html> <body> {children} {/* <Toaster /> */} <ToastHandler /> </body> </html> ); }Adjust the import alias (
@/) to match the project.
Usage
Use setToastCookie only when the action redirects — the response is discarded, so the
cookie carries the message to the destination page. When an action instead returns a result
to the component, skip the cookie and toast on the client from that result (simpler, no
round-trip). See patterns.md for the full decision rule.
"use server";
import { redirect } from "next/navigation";
import { setToastCookie } from "@/lib/toast/server/toast.cookie"; // adjust alias
export async function createOrder(formData: FormData) {
const [error, order] = await createOrderInDb(formData);
if (error) {
// Returned to the component → let the client toast it. No cookie here.
return { success: false, error: "Couldn't create the order. Please try again." };
}
await setToastCookie("Order created!", "success");
redirect(`/orders/${order.id}`); // response discarded → cookie shows it on the destination
}
Toast types & duration
await setToastCookie("Saved!", "success");
await setToastCookie("Something went wrong", "error");
await setToastCookie("Please review your input", "warning");
await setToastCookie("New features available", "info");
// Optional duration in milliseconds (passed to your toast library):
await setToastCookie("This stays longer", "info", 10_000);
API
function setToastCookie(message: string, type?: ToastType, duration?: number): Promise<void>;
// type defaults to "success"; duration is milliseconds, forwarded to the toast library.
type ToastType = "success" | "error" | "info" | "warning";
type ToastMessage = { type: ToastType; message: string; duration?: number };
Key rules
- Cookie only when you redirect. If the action returns a result to the component, toast on
the client from that result instead — don't set the cookie (see
patterns.md). - Set the cookie before
redirect()— code afterredirect()never runs (it throws). - One
<ToastHandler />only — multiple mounts can fire a toast twice. - No sensitive data in messages — the cookie is readable by client JS (
httpOnly: false) and messages are user-facing. Keep them generic. - Toasts are for page-level feedback, not inline field validation — return field errors to the form for those.
See patterns.md for the full do/don't list and complete server-action examples.