Documentation

Everything you need to get started with toast-23.

Getting Started

Install toast-23 and create your first toast in minutes.

Installation

Install toast-23 using your preferred package manager:

npmbash
npm install toast-23
yarnbash
yarn add toast-23
pnpmbash
pnpm add toast-23

Quick Start

1Import the stylesheet

Import the toast-23 CSS in your app's entry point.

main.tsxtsx
// main.tsx, layout.tsx, or _app.tsx
import "toast-23/styles.css";

2Wrap your app with the provider

The Toast23Provider manages toast state and renders toast containers.

App.tsxtsx
import { Toast23Provider } from "toast-23";

export default function App() {
  return (
    <Toast23Provider
      position="top-right"
      maxVisible={5}
      duration={5000}
    >
      <YourApp />
    </Toast23Provider>
  );
}

3Use the hook in any component

Call useToast() to get the toast API with variant shortcuts.

MyComponent.tsxtsx
import { useToast } from "toast-23";

function MyComponent() {
  const toast = useToast();

  return (
    <div>
      <button onClick={() => toast("Hello world!")}>
        Default
      </button>
      <button onClick={() => toast.success("Saved!")}>
        Success
      </button>
      <button onClick={() => toast.error("Oops!")}>
        Error
      </button>
    </div>
  );
}

Usage Guide

Learn how to use every feature of toast-23.

Variants

toast-23 includes 5 visual variants, each with distinct colors for both light and dark mode:

success

toast.success()

Completed actions, saved data

error

toast.error()

Failed operations, validation errors

warning

toast.warning()

Potential issues, important notices

info

toast.info()

General information, updates

default

toast()

Neutral messages

Loading Toast

Create a loading notification. Most likely, you'll want to update it afterwards. For a friendly alternative, check out toast.promise(), which takes care of that automatically.

LoadingToast.tsxtsx
const toast = useToast();

// Show loading toast
const toastId = toast.loading("Waiting...");

// Later, update it to success
toast.success("Done!", { id: toastId });

Tip: Loading toasts are persistent by default (duration: 0) and non-dismissible. Update them with a new variant when your operation finishes.

Promise API

Track async operations with automatic state transitions. The toast updates automatically when the promise resolves or fails.

Simple Usage

PromiseSimple.tsxtsx
const toast = useToast();

const myPromise = fetchData();

toast.promise(myPromise, {
  loading: "Loading",
  success: "Got the data",
  error: "Error when fetching",
});

Advanced

Provide a function to the success/error messages to incorporate the result/error. The third argument accepts toast options:

PromiseAdvanced.tsxtsx
toast.promise(
  myPromise,
  {
    loading: "Loading",
    success: (data) => `Successfully saved ${data.name}`,
    error: (err) => `This just happened: ${err.toString()}`,
  },
  {
    duration: 5000,
    position: "bottom-center",
  }
);

Using an Async Function

You can also provide a function that returns a promise, which will be called automatically:

PromiseAsync.tsxtsx
toast.promise(
  async () => {
    const { id } = await fetchData1();
    await fetchData2(id);
  },
  {
    loading: "Loading",
    success: "Got the data",
    error: "Error when fetching",
  }
);

Tip: It's recommended to set a minWidth on your promise toasts to prevent layout jumps from different message lengths.

Custom (JSX)

Create a fully custom notification with JSX. Custom toasts render your content without any default styles.

CustomJSX.tsxtsx
toast.custom(<div>Hello World</div>);

Render JSX with Default Styles

To render custom JSX content with the default toast styling, pass JSX directly to toast() instead:

JSXWithStyles.tsxtsx
toast(
  <span>
    Custom and <b>bold</b>
  </span>,
  {
    title: "My Custom Toast",
  }
);

Dismiss from Custom Content

Use the returned toast id to add dismiss buttons inside your custom content:

CustomDismiss.tsxtsx
const toastId = toast.custom(
  <div className="my-custom-toast">
    <p>Something happened</p>
    <button onClick={() => toast.dismiss(toastId)}>
      Dismiss
    </button>
  </div>
);

Positioning

Set a default position on the provider, and optionally override per-toast:

Positioning.tsxtsx
// Default position for all toasts
<Toast23Provider position="bottom-right">
  <App />
</Toast23Provider>

// Override position for a specific toast
toast.success("Saved!", { position: "top-left" });

Click a position to preview a real toast notification:

Toast Options

Every toast method accepts an optional options object:

Options.tsxtsx
toast.success("Saved!", {
  title: "Auto-save",
  duration: 6000,
  position: "bottom-right",
  dismissible: true,
});

// Persistent toast (must be dismissed manually)
const id = toast.info("Processing...", { duration: 0 });

// Later, dismiss it
toast.dismiss(id);

Default Durations

Every type has its own default duration. You can overwrite them with toast options per-toast or globally via the provider.

TypeDuration
success5000ms (provider default)
error5000ms (provider default)
warning5000ms (provider default)
info5000ms (provider default)
default5000ms (provider default)
loadingInfinity (persistent)
custom5000ms (provider default)
Durations.tsxtsx
// Override provider default duration
<Toast23Provider duration={3000}>
  <App />
</Toast23Provider>

// Override per-toast
toast.success("Quick!", { duration: 2000 });

Dismiss & Remove

Programmatically control toast lifecycle with dismiss (animated exit) and remove (instant removal).

Dismiss a Single Toast

Triggers the exit animation, then removes after removeDelay (default 1000ms):

DismissSingle.tsxtsx
const toastId = toast.loading("Loading...");

// Later...
toast.dismiss(toastId);

Dismiss All Toasts

Omit the id to dismiss all visible toasts at once:

DismissAll.tsxtsx
toast.dismiss();

Remove Toasts Instantly

To remove toasts instantly without any exit animation, use toast.remove():

RemoveInstant.tsxtsx
// Remove a specific toast
toast.remove(toastId);

// Remove all toasts
toast.remove();

Configure Remove Delay

By default, toasts are kept in the DOM for 1000ms after being dismissed (to play exit animation). Configure per-toast or globally:

RemoveDelay.tsxtsx
// Per-toast
toast.success("Created!", { removeDelay: 500 });

// Globally via provider
<Toast23Provider
  duration={5000}
  // All toast options can be set as defaults
>
  <App />
</Toast23Provider>

Update & Deduplicate

Update an Existing Toast

Each toast call returns a unique id. Pass it as the id option to update an existing toast in-place:

UpdateExisting.tsxtsx
const toastId = toast.loading("Loading...");

// Later, update to success
toast.success("This worked", {
  id: toastId,
});

Prevent Duplicate Toasts

To prevent duplicates, provide a unique permanent id. If a toast with that id already exists, it will be updated instead of creating a new one:

PreventDuplicates.tsxtsx
toast.success("Copied to clipboard!", {
  id: "clipboard",
});

Dark Mode

toast-23 supports dark mode in two ways:

Automatic

Respects the user's system preference via prefers-color-scheme: dark. No configuration needed.

Manual Toggle

Add the dark class to any ancestor element (typically <html>). Compatible with Tailwind CSS and Next.js themes.

Customization

toast-23 is fully customizable. Override any CSS class to match your brand or design system.

Title is Optional

Toasts work perfectly without a title — just pass your message directly:

TitleOptional.tsxtsx
// Without title — clean and minimal
toast.success("Changes saved successfully!");

// With title — more descriptive
toast.success("Changes saved!", {
  title: "Auto-save",
});

Custom Duration

Control how long each toast stays visible, or make it persistent:

CustomDuration.tsxtsx
// Quick notification (3 seconds)
toast("Quick message", { duration: 3000 });

// Longer display (10 seconds)
toast.info("Please read carefully", { duration: 10000 });

// Persistent — stays until manually dismissed
const id = toast.warning("Action required", { duration: 0 });
toast.dismiss(id);

Dismiss Control

Choose whether users can manually dismiss a toast:

DismissControl.tsxtsx
// Non-dismissible toast (no X button)
toast.info("Processing...", { dismissible: false });

// Dismissible (default behavior)
toast.success("Done!", { dismissible: true });

Override Variant Colors

Customize the look of any variant by overriding its CSS class:

custom-overrides.csscss
.toast23-item--success {
  background: #d1fae5;
  border-color: #6ee7b7;
}
.toast23-progress--success {
  background: #10b981;
}
.toast23-icon--success {
  color: #059669;
}

Custom Fonts & Sizing

Change the toast font, size, border radius, or width:

custom-fonts.csscss
.toast23-item {
  font-family: "Inter", sans-serif;
  border-radius: 1rem;
  max-width: 400px;
}
.toast23-title {
  font-size: 0.9rem;
}
.toast23-message {
  font-size: 0.85rem;
}

Progress Bar Styling

Adjust the progress bar height, color, or border radius:

custom-progress.csscss
.toast23-progress {
  height: 4px;
  border-radius: 0;
}
/* Custom color for all variants */
.toast23-progress--info {
  background: linear-gradient(90deg, #6366f1, #8b5cf6);
}

Per-Toast Position Override

Each toast can override the provider's default position:

PositionOverride.tsxtsx
// Default position is "top-right" from provider
toast.success("Saved!", { position: "bottom-center" });
toast.error("Oops!", { position: "top-left" });

SSR Support

toast-23 is fully SSR-compatible. All browser APIs are guarded behind useEffect.

Next.js (App & Pages Router)
Remix
Gatsby
Astro (React islands)
Angular (standalone API)
Vue.js (standalone API)

Next.js

Works with both the App Router and Pages Router. Add the provider and stylesheet in your root layout or _app.tsx.

app/layout.tsxtsx
// app/layout.tsx (App Router)
"use client";
import "toast-23/styles.css";
import { Toast23Provider } from "toast-23";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Toast23Provider position="top-right">
          {children}
        </Toast23Provider>
      </body>
    </html>
  );
}

Remix

Import the stylesheet in your root route and wrap your Outlet with the provider.

app/root.tsxtsx
// app/root.tsx
import "toast-23/styles.css";
import { Toast23Provider } from "toast-23";
import { Outlet } from "@remix-run/react";

export default function App() {
  return (
    <html>
      <body>
        <Toast23Provider position="top-right">
          <Outlet />
        </Toast23Provider>
      </body>
    </html>
  );
}

Gatsby

Use wrapRootElement in gatsby-browser.js and gatsby-ssr.js to add the provider.

gatsby-browser.jsjsx
// gatsby-browser.js & gatsby-ssr.js
import "toast-23/styles.css";
import React from "react";
import { Toast23Provider } from "toast-23";

export const wrapRootElement = ({ element }) => (
  <Toast23Provider position="top-right">
    {element}
  </Toast23Provider>
);

Astro

Use toast-23 inside React islands. Mark the wrapper component with client:load.

index.astroastro
---
// src/pages/index.astro
---
<html>
  <body>
    <ToastWrapper client:load />
  </body>
</html>
ToastWrapper.tsxtsx
// src/components/ToastWrapper.tsx
import "toast-23/styles.css";
import { Toast23Provider, useToast } from "toast-23";

function Inner() {
  const toast = useToast();
  return <button onClick={() => toast.success("Hello!")}>Toast</button>;
}

export default function ToastWrapper() {
  return (
    <Toast23Provider position="top-right">
      <Inner />
    </Toast23Provider>
  );
}

Angular

toast-23 ships a built-in createToast23() standalone API that works outside of React. No manual React root setup required — just install the peer dependencies and use the imperative API in an Angular service.

terminalbash
// Install peer dependencies
npm install react react-dom toast-23
npm install -D @types/react @types/react-dom
toast.service.tstypescript
// src/app/toast.service.ts
import { Injectable, OnDestroy } from '@angular/core';
import { createToast23, StandaloneToastApi } from 'toast-23';
import 'toast-23/styles.css';

@Injectable({ providedIn: 'root' })
export class ToastService implements OnDestroy {
  private toast: StandaloneToastApi;

  constructor() {
    this.toast = createToast23({
      position: 'top-right',
      maxVisible: 5,
      duration: 5000,
    });
  }

  success(msg: string) { this.toast.success(msg); }
  error(msg: string)   { this.toast.error(msg); }
  warning(msg: string) { this.toast.warning(msg); }
  info(msg: string)    { this.toast.info(msg); }
  show(msg: string)    { this.toast(msg); }

  ngOnDestroy() {
    this.toast.destroy();
  }
}
example.component.tstypescript
// Usage in any Angular component
import { Component } from '@angular/core';
import { ToastService } from './toast.service';

@Component({
  selector: 'app-example',
  template: `<button (click)="notify()">Show Toast</button>`,
})
export class ExampleComponent {
  constructor(private toast: ToastService) {}

  notify() {
    this.toast.success('Hello from Angular!');
  }
}

Vue.js

Use the built-in createToast23() standalone API as a Vue plugin. No manual React root setup needed — toast-23 handles it internally.

terminalbash
// Install peer dependencies
npm install react react-dom toast-23
npm install -D @types/react @types/react-dom
plugins/toast.tstypescript
// src/plugins/toast.ts
import { Plugin, inject } from 'vue';
import { createToast23, type StandaloneToastApi } from 'toast-23';
import 'toast-23/styles.css';

const TOAST_KEY = Symbol('toast');

export const toast23Plugin: Plugin = {
  install(app) {
    const toast = createToast23({
      position: 'top-right',
      maxVisible: 5,
      duration: 5000,
    });

    // Expose via provide/inject
    app.provide(TOAST_KEY, toast);

    // Cleanup on app unmount
    app.config.globalProperties.$toast = toast;
    const originalUnmount = app.unmount.bind(app);
    app.unmount = () => {
      toast.destroy();
      originalUnmount();
    };
  },
};

export function useToast23() {
  return inject<StandaloneToastApi>(TOAST_KEY)!;
}
main.tstypescript
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { toast23Plugin } from './plugins/toast';

createApp(App).use(toast23Plugin).mount('#app');
ExampleComponent.vuevue
<!-- ExampleComponent.vue -->
<script setup lang="ts">
import { useToast23 } from '../plugins/toast';
const toast = useToast23();
</script>

<template>
  <button @click="toast.success('Hello from Vue!')">
    Show Toast
  </button>
</template>

API Reference

Complete API documentation for every export in toast-23.

Provider Setup

<Toast23Provider>

Context provider that manages toast state and renders toast containers.

Usagetsx
import { Toast23Provider } from "toast-23";

<Toast23Provider
  position="top-right"
  maxVisible={5}
  duration={5000}
>
  <App />
</Toast23Provider>
PropTypeDefaultDescription
childrenReactNodeYour application content (required)
positionToastPosition"top-right"Default screen corner for toasts
maxVisiblenumber5Maximum simultaneously visible toasts
durationnumber5000Default auto-dismiss duration (ms). 0 = persistent.

Toast Hook

useToast()

React hook that returns the ToastApi. Must be called inside a <Toast23Provider>.

Usagetsx
import { useToast } from "toast-23";

function MyComponent() {
  const toast = useToast();
  // toast is a callable function with method shortcuts
}

Note: Calling useToast() outside of a <Toast23Provider> will throw an error.

Standalone API

createToast23()

Standalone / imperative API for use outside React — works with Angular, Vue, Svelte, or vanilla JavaScript. Internally bootstraps a minimal React root.

Usagetypescript
import { createToast23 } from "toast-23";
import "toast-23/styles.css";

const toast = createToast23({
  position: "top-right",
  maxVisible: 5,
  duration: 5000,
});

toast.success("Saved!");
toast.error("Oops!");
toast("Hello, world!");

// Cleanup when done
toast.destroy();
OptionTypeDefaultDescription
positionToastPosition"top-right"Default screen corner for toasts
maxVisiblenumber5Maximum simultaneously visible toasts
durationnumber5000Default auto-dismiss duration (ms). 0 = persistent.

Return value: A StandaloneToastApi object with the same .success(), .error(),.warning(), .info(), .promise(), and .dismiss() methods as useToast(), plus a .destroy() method for cleanup.

Methods

ToastApi

The object returned by useToast(). A callable function with additional method properties.

MethodSignatureReturns
toast()(message: string | ReactNode, options?: ToastOptions)string (id)
toast.success()(message: string, options?)string (id)
toast.error()(message: string, options?)string (id)
toast.warning()(message: string, options?)string (id)
toast.info()(message: string, options?)string (id)
toast.loading()(message: string, options?)string (id)
toast.custom()(content: ReactNode, options?)string (id)
toast.dismiss()(id?: string)void
toast.remove()(id?: string)void
toast.promise()<T>(promise | () => Promise, opts, toastOpts?)Promise<T>

Options Interface

ToastOptions

Configuration object accepted by all toast methods.

types.tstypescript
interface ToastOptions {
  id?: string;
  title?: string;
  variant?: ToastVariant;
  duration?: number;
  position?: ToastPosition;
  dismissible?: boolean;
  removeDelay?: number;
}

Promise Handling

PromiseOptions<T>

Configuration for toast.promise().

types.tstypescript
interface PromiseOptions<T> {
  loading: string;
  success: string | ((data: T) => string);
  error: string | ((err: unknown) => string);
}

Type Aliases

Exported type aliases for use in your TypeScript code.

types.tstypescript
type ToastVariant = "success" | "error" | "warning" | "info" | "default";
type ToastPosition =
  | "top-right" | "top-left" | "top-center"
  | "bottom-right" | "bottom-left" | "bottom-center";

import type {
  ToastVariant,
  ToastPosition,
  ToastOptions,
  ToastApi,
  Toast23ProviderProps,
  PromiseOptions,
  StandaloneOptions,
  StandaloneToastApi,
} from "toast-23";

CSS Class Reference

All CSS classes used by toast-23. Override these to customize the look and feel.

ClassDescription
.toast23-containerFixed-position container
.toast23-container--{position}Position modifier (top-right, etc.)
.toast23-itemIndividual toast element
.toast23-item--{variant}Variant modifier (success, error, etc.)
.toast23-iconIcon wrapper
.toast23-contentMessage content area
.toast23-titleTitle heading
.toast23-messageMessage body text
.toast23-dismissDismiss button
.toast23-progressProgress bar (auto-dismiss indicator)
.toast23-queue-badgeQueued toast count indicator

Releases

Changelog for toast-23.

v1.0.1February 2026

Initial Release

  • 5 toast variants: success, error, warning, info, default
  • 6 position options with per-toast override
  • Promise API with loading → success/error transitions
  • Loading toast shortcut
  • Custom JSX toasts with toast.custom()
  • Dismiss all / Remove instantly APIs
  • Update existing toasts & prevent duplicates via id
  • Hover-pause with smooth progress bar reversal
  • Dark mode (automatic + manual toggle)
  • Queue system with +N badge
  • Full TypeScript support
  • Accessible (ARIA live regions, keyboard support)
  • Zero dependencies (React peer dep only)
  • Standalone API for Angular, Vue, Svelte, vanilla JS
  • SSR compatible (Next.js, Remix, Gatsby, Astro)