npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@iryanraushan/notchify

v2.2.3

Published

A classic toast notification library for React and Next.js

Readme

notchify

A classic, toast notification library for React and Next.js.
Dark & light mode ready. Fully themeable. Zero config to get started.

Features

  • 4 variants — default, success, destructive, loading
  • toast.promise() — auto loading → success/error lifecycle
  • toast.custom() — render any React node as a toast
  • toast.update() — mutate an existing toast in place
  • toast.dismiss() — programmatically remove a toast
  • Pause on hover — timer pauses when mouse is over the toast
  • Swipe to dismiss — horizontal swipe on touch devices
  • Action button — inline CTA with async support
  • Custom icon — replace the dot/spinner with any React node
  • maxToasts — cap the number of visible toasts
  • toastClassName / containerClassName — BYO CSS classes
  • className per toast — fine-grained styling
  • Dark & light mode out of the box
  • Responsive — auto centers on mobile (≤768px)
  • Smooth per-position enter + fade-out exit animations
  • Respects prefers-reduced-motion
  • Fully customizable colors, background, border, shadow, and duration
  • Lightweight — no extra dependencies beyond React
  • Works with React 17+ and Next.js (App & Pages Router)
  • TypeScript first — all types exported

Installation

npm i @iryanraushan/notchify

Peer dependencies:

npm install react react-dom

Quick Start

1. Wrap your app with ToastProvider

// App.tsx or layout.tsx (Next.js)
import { ToastProvider } from '@iryanraushan/notchify';

export default function RootLayout({ children }) {
  return (
    <ToastProvider>
      {children}
    </ToastProvider>
  );
}

2. Trigger toasts from anywhere

import { useToast } from '@iryanraushan/notchify';

export default function MyComponent() {
  const { toast } = useToast();

  return (
    <button onClick={() => toast({ message: 'Saved!', variant: 'success' })}>
      Save
    </button>
  );
}

Variants

| Variant | Dot Color | Usage | |---|---|---| | default | Blue #3b82f6 | General info | | success | Green #22c55e | Operation succeeded | | destructive | Red #ef4444 | Error / failure | | loading | Purple #a855f7 | In-progress, persists until updated |

const { toast } = useToast();

toast({ message: 'Profile updated.' });
toast({ message: 'Payment successful!', variant: 'success' });
toast({ message: 'Failed to delete file.', variant: 'destructive' });
toast.loading('Uploading…');

Shorthand methods:

toast.success('Done!');
toast.error('Something went wrong.');
const id = toast.loading('Please wait…');

Promise Toast

Automatically shows a loading toast, then transitions to success or error.

toast.promise accepts either a Promise directly or a factory function () => Promise<T>.

await toast.promise(fetch('/api/save'), {
  loading: 'Saving…',
  success: 'Saved!',
  error: 'Failed to save.',
});

// factory function form
await toast.promise(() => fetchUser(), {
  loading: 'Loading user…',
  success: 'Loaded!',
  error: 'Failed.',
});

// Dynamic messages based on result / error
await toast.promise(fetchUser(), {
  loading: 'Loading user…',
  success: (user) => `Welcome, ${user.name}!`,
  error: (err) => `Error: ${err.message}`,
  duration: 4000, // duration for the success/error state
});

Update a Toast

Mutate any field of an existing toast by its id.

const id = toast.loading('Uploading…');

// later…
toast.update(id, { variant: 'success', message: 'Upload complete!' });

Dismiss a Toast

const { toast, dismiss } = useToast();

// via the API object
const id = toast.loading('Working…');
toast.dismiss(id);

// via the context shorthand
dismiss(id);

Custom Duration

// per toast (ms)
toast({ message: 'Stays 6 s.', duration: 6000 });

// globally via theme
<ToastProvider theme={{ defaultDuration: 5000 }}>

Action Button

An inline CTA rendered inside the toast. onClick can be async — the button shows a loading state while the promise resolves.

toast({
  message: 'File deleted.',
  action: {
    label: 'Undo',
    onClick: async () => {
      await restoreFile();
    },
  },
});

Custom Icon

Replace the colored dot with any React node.

toast({
  message: 'New message.',
  icon: <MailIcon size={16} />,
});

Custom Toast

Render a fully custom React component. All standard toast behaviour (auto-dismiss, hover-pause, swipe) still applies.

toast.custom(
  <div style={{ padding: 12, background: '#1e1e2e', borderRadius: 12, color: '#fff' }}>
    <strong>Custom UI</strong>
    <p>Anything goes here.</p>
  </div>
);

// with extra options
toast.custom(<MyCard />, { duration: 5000, id: 'my-card' });

Pause on Hover

The auto-dismiss timer automatically pauses when the mouse is over a toast and resumes when it leaves. No configuration needed.


Swipe to Dismiss

On touch devices, swiping a toast horizontally by more than 80 px dismisses it. Built-in, no configuration needed.


Dark & Light Mode

<ToastProvider mode="dark">  {/* default */}
<ToastProvider mode="light">

Default values per mode:

| Token | Dark | Light | |---|---|---| | background | #0c0b0b | #ffffff | | border | #1a1a1a | #e5e5e5 | | textColor | #f5f5f5 | #111111 | | boxShadow | subtle dark shadow | subtle light shadow |


Positioning

<ToastProvider position="top-right">

| Value | Description | |---|---| | top-left | Top-left corner | | top-center | Top center | | top-right | Top-right corner | | bottom-left | Bottom-left corner | | bottom-center | Bottom center | | bottom-right (default) | Bottom-right corner |

Responsive behavior (mobile ≤ 768px)

Any top-* position → top-center, any bottom-*bottom-center. Toasts are horizontally centered and never pinned to the edge.


Limit Visible Toasts

Cap how many toasts are shown at once. Oldest toasts are dropped when the limit is exceeded.

<ToastProvider maxToasts={3}>

Custom Theme

Override any visual token. Unset values fall back to the mode defaults.

<ToastProvider
  mode="dark"
  theme={{
    background: '#1a1a2e',
    border: '#2a2a4a',
    textColor: '#e0e0ff',
    boxShadow: '0 8px 32px rgba(0,0,0,0.4)',
    defaultDuration: 5000,
    dotColors: {
      default: '#6366f1',
      success: '#00ff88',
      destructive: '#ff4466',
      loading: '#facc15',
    },
  }}
>

Custom CSS Classes

Apply Tailwind (or any CSS) classes globally or per toast.

// global — applied to every toast
<ToastProvider toastClassName="my-toast" containerClassName="my-container">

// per toast
toast({ message: 'Hi', className: 'border-blue-500' });

Animations

Toasts animate in based on their position and fade + scale out on dismiss.

| Position | Enter animation | |---|---| | top-left / bottom-left | Slide in from left | | top-right / bottom-right | Slide in from right | | top-center | Slide in from top | | bottom-center | Slide in from bottom | | Exit (all) | Fade out + scale down |

Animations are automatically disabled when prefers-reduced-motion: reduce is set.


Accessibility

  • Toast container has role="region" and aria-label="Notifications"
  • Each toast has role="status" and aria-live="polite" / aria-atomic="true"
  • The dot/dismiss button has aria-label="Dismiss"
  • Action button carries aria-label from the label text

Next.js App Router

// components/providers.tsx
'use client';
import { ToastProvider } from '@iryanraushan/notchify';

export function Providers({ children }: { children: React.ReactNode }) {
  return <ToastProvider mode="dark" position="bottom-right">{children}</ToastProvider>;
}
// app/layout.tsx
import { Providers } from '@/components/providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Standalone Components

Toast and ToastViewport are exported as low-level components for advanced / headless use cases where you want to render toasts outside of ToastProvider.

import { Toast, ToastViewport } from '@iryanraushan/notchify';

| Component | Description | |---|---| | Toast | Single toast item — accepts all ToastProps including theme, mode, style | | ToastViewport | The fixed positioned container that wraps toasts |

Both components read from ToastContext when available, and fall back to their own props otherwise.


API Reference

<ToastProvider />

| Prop | Type | Default | Description | |---|---|---|---| | mode | "dark" \| "light" | "dark" | Color mode | | position | ToastPosition | "bottom-right" | Toast position on desktop | | theme | ToastTheme | {} | Visual overrides | | maxToasts | number | — | Max toasts visible at once | | toastClassName | string | — | Class applied to every toast | | containerClassName | string | — | Class applied to the viewport container | | children | ReactNode | — | Your app |


ToastTheme

| Key | Type | Description | |---|---|---| | background | string | Toast background color | | border | string | Toast border color | | textColor | string | Message text color | | boxShadow | string | Toast shadow | | defaultDuration | number | Default auto-dismiss duration in ms | | dotColors | { default?, success?, destructive?, loading? } | Dot color per variant |


useToast()

const { toast, dismiss, theme, mode, position } = useToast();

| Key | Type | Description | |---|---|---| | toast | ToastAPI | Full toast API (see below) | | dismiss(id) | function | Remove a toast by id | | theme | ToastTheme | Resolved theme object | | mode | ToastMode | Current mode | | position | ToastPosition | Current position |


ToastAPI

| Method | Signature | Description | |---|---|---| | toast(options) | (options) => string | Show a toast, returns id |

Note: toast.promise accepts Promise<T> or () => Promise<T> as its first argument. | toast.success(msg, opts?) | (string, opts?) => string | Success variant shorthand | | toast.error(msg, opts?) | (string, opts?) => string | Destructive variant shorthand | | toast.loading(msg, opts?) | (string, opts?) => string | Loading variant, persists | | toast.custom(node, opts?) | (ReactNode, opts?) => string | Fully custom UI | | toast.promise(p, msgs) | (Promise, msgs) => Promise | Loading → success/error lifecycle | | toast.update(id, opts) | (string, Partial<ToastItem>) => void | Mutate an existing toast | | toast.dismiss(id) | (string) => void | Remove a toast by id |


toast(options)ToastOptions

| Option | Type | Default | Description | |---|---|---|---| | message | string | — | Notification text (required for standard toasts) | | variant | "default" \| "success" \| "destructive" \| "loading" | "default" | Visual style | | type | ToastVariant | — | Deprecated — use variant instead | | duration | number | 3000 | Auto-dismiss time in ms | | id | string | auto | Custom id (useful for update) | | icon | ReactNode | — | Custom icon replaces the dot | | action | ToastAction | — | Inline action button | | className | string | — | Extra CSS class for this toast | | custom | ReactNode | — | Fully custom component |


ToastAction

| Key | Type | Description | |---|---|---| | label | ReactNode | Button label | | onClick | () => void \| Promise<void> | Click handler (async supported) |


toast.promise messages — ToastPromiseMessages<T>

| Key | Type | Description | |---|---|---| | loading | string | Message shown while pending | | success | string \| (data: T) => string | Message (or factory) on resolve | | error | string \| (err: unknown) => string | Message (or factory) on reject | | duration | number | Duration for the resolved/rejected state |


Exported Types

import type {
  ToastOptions,
  ToastItem,
  ToastVariant,
  ToastMode,
  ToastPosition,
  ToastTheme,
  ToastAction,
  ToastAPI,
  ToastPromiseMessages,
  ToastContextValue,
  ToastProviderProps,
} from '@iryanraushan/notchify';

License

MIT © Raushan Kumar