Documentation
Notch-style notifications for React.
A small toast API with an attached notch or floating dynamic island.
01) Get started
Install, mount, and trigger
pnpm i notch-toastMotion dependency. Notch Toast uses Motion for its transitions. Install it once alongside the package.
pnpm i motion// app/layout.tsx
import { NotchToaster } from "notch-toast"
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en">
<body>
{children}
<NotchToaster />
</body>
</html>
)
}"use client"
import { toast } from "notch-toast"
export function SaveButton() {
return (
<button onClick={() => toast.success("Changes saved")}>Save</button>
)
}02) shadcn/ui
Install a local wrapper
components/ui and installs the package and Motion dependency.npx shadcn@latest add https://notch.devsethi.me/r/notch-toaster.json// app/layout.tsx
import { NotchToaster } from "@/components/ui/notch-toaster"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<NotchToaster />
</body>
</html>
)
}
// In a client component
import { notchToast } from "@/components/ui/notch-toaster"
notchToast.success("Saved!")The generated component is intentionally a small local wrapper. Keep one NotchToaster mounted in your layout and call notchToast from client components.
03) AI agents & skills
Give your agent the right context
Install it from the skills registry:
npx skills add Devsethi3/notch-toastOr install the bundled skill directly from the package:
npx notch-toast add-skill # -> .claude/skills/notch-toast/SKILL.md
npx notch-toast add-skill --agents # also adds an AGENTS.md pointer
npx notch-toast add-skill --dir .cursor/skills/notch-toast04) API reference
Toast methods
toast() function for neutral messages, or use a status helper when the message has a semantic state. Each call returns its toast ID.import { toast } from "notch-toast"
toast("A new notification arrived")
toast.success("Changes saved")
toast.error("Unable to save changes")
toast.warning("Your session is about to expire")
toast.info("A new version is available")
toast.loading("Syncing changes")| Method | Default behavior |
|---|---|
| toast() | Neutral text-only toast; dismisses after 3.6 seconds. |
| success() | Success state; dismisses after 3.6 seconds. |
| info() | Informational state; dismisses after 3.6 seconds. |
| warning() / error() | Persistent and dismissible until action is taken. |
| loading() | Persistent progress state, intended to morph or update. |
05) Promise
Morph progress into a result
toast.promise() for async work. It shows a loading island immediately, then updates that same island to success or error when the promise settles.async function saveChanges() {
const response = await fetch("/api/settings", { method: "POST" })
if (!response.ok) throw new Error("Save failed")
return response.json() as Promise<{ name: string }>
}
void toast
.promise(saveChanges(), {
loading: "Saving changes",
success: (settings) => `Saved ${settings.name}`,
error: "Unable to save changes",
duration: 3600,
})
.catch(() => undefined)Note: the returned promise preserves the original rejection. Handle or await it as you normally would.
06) Update and dismiss
Control the active island
update for explicit state changes and dismiss to remove it.const id = toast.loading("Publishing changes", {
id: "publish",
})
try {
await publishChanges()
toast.update(id, {
title: "Published successfully",
type: "success",
duration: 3600,
dismissible: false,
})
} catch {
toast.update(id, {
title: "Publishing failed",
type: "error",
duration: 0,
dismissible: true,
})
}
toast.dismiss(id) // dismiss this toast
toast.dismiss() // dismiss the active toastThe toaster is intentionally latest-toast-wins: a new trigger replaces the active island. Updating an active toast with the same ID morphs its content in place.
07) Options and duration
Adjust a toast when needed
toast.error("Unable to save changes", {
id: "save-changes",
duration: 0,
dismissible: true,
action: {
label: "Retry",
onClick: () => retrySave(),
},
})Close button: dismissible
dismissible is a boolean. Set it to true to show the close × button and allow the Escape key to dismiss the toast. Set it to false to hide the close button and disable Escape dismissal for that toast.
// Show the close “×” button and allow Escape to dismiss.
toast.success("Profile saved", {
dismissible: true,
})
// Keep the toast visible without a close button.
toast.success("Saving in the background", {
dismissible: false,
})Errors, warnings, and loading toasts default to dismissible: true. Success, info, and default toasts default to dismissible: false.
idA stable identity for a toast you plan to update or dismiss.
durationMilliseconds until dismissal. Set 0 to keep it visible.
dismissibleBoolean. true shows the close button; false hides it. Errors, warnings, and loading states default to true.
actionAn optional label and callback, useful for retry or undo flows.
Accessibility. Toasts announce updates without stealing focus. Dismissible toasts support their close button and the Escape key; reduced motion keeps a short opacity fade while removing movement.
08) Placement and motion
Configure the presentation
<NotchToaster
position="bottom-center"
mode="floating"
animation="smooth"
/>Position
top-center or bottom-center.
Mode
attached joins the edge; floating adds an inset.
Animation
fluid, gooey, smooth, subtle, or snappy.
fluid is the default balanced spring. Use gooey for expressive bounce, smooth for eased transitions, subtle for restrained motion, and snappy for faster feedback. Reduced-motion preferences are respected automatically.