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

Add the package, mount one global toaster, then trigger it from any client component.
pnpm i notch-toast

Motion dependency. Notch Toast uses Motion for its transitions. Install it once alongside the package.

pnpm i motion
app/layout.tsx
// 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>
  )
}
SaveButton.tsx
"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

Add Notch Toast through the shadcn registry with one command. It creates a thin wrapper in components/ui and installs the package and Motion dependency.
Terminal
npx shadcn@latest add https://notch.devsethi.me/r/notch-toaster.json
app/layout.tsx
// 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

The bundled skill teaches coding agents the API, the required toaster mount, and the dismissible-toast behavior before they add notifications to your app.

Install it from the skills registry:

Terminal
npx skills add Devsethi3/notch-toast

Or install the bundled skill directly from the package:

Terminal
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-toast

04) API reference

Toast methods

Call the default toast() function for neutral messages, or use a status helper when the message has a semantic state. Each call returns its toast ID.
tsx
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")
MethodDefault 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

Use toast.promise() for async work. It shows a loading island immediately, then updates that same island to success or error when the promise settles.
tsx
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

Give a toast a stable ID to morph it in place. Use update for explicit state changes and dismiss to remove it.
tsx
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 toast

The 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

All toast helpers accept the same options object. Keep defaults for short confirmations; make important feedback persistent.
tsx
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.

Dismissible 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.

id

A stable identity for a toast you plan to update or dismiss.

duration

Milliseconds until dismissal. Set 0 to keep it visible.

dismissible

Boolean. true shows the close button; false hides it. Errors, warnings, and loading states default to true.

action

An 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

Configure the mounted toaster once. Top-center attached is the default; bottom-center automatically uses the matching attached bottom notch.
tsx
<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.