Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@
},
{
"path": "dist/index.mjs",
"limit": "5 KB"
"limit": "5.5 KB"
},
{
"path": "headless/index.js",
"limit": "2 KB"
"limit": "2.5 KB"
},
{
"path": "headless/index.mjs",
"limit": "2 KB"
"limit": "2.5 KB"
}
],
"devDependencies": {
Expand Down
2 changes: 2 additions & 0 deletions site/components/docs-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export default function DocsLayout({ meta, children }) {
</TableItem>
<TableHeader>Guides</TableHeader>
<TableItem href="/docs/styling">Styling</TableItem>
<TableItem href="/docs/multi-toaster">Multi Toaster</TableItem>

<TableHeader>Releases</TableHeader>
<TableItem href="/docs/version-2">New in 2.0</TableItem>
</div>
Expand Down
98 changes: 98 additions & 0 deletions site/pages/docs/multi-toaster.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import Layout from '../../components/docs-layout';
import toast, { Toaster } from 'react-hot-toast';

export const meta = {
title: 'Multiple Toasters',
};

export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

# Multiple Toasters

React Hot Toast supports multiple toaster instances in your app, They can be used and configured independently of each other. This is useful for having notifications in different areas of your app.

You can use multiple toasters by creating a [`Toaster`](/docs/toaster) with a unique `toasterId`:

```jsx
<Toaster toasterId="sidebar" />
```

## Example

This example shows two toasters, each maintaining their own state and configuration.

<div className="not-prose flex gap-4 flex-col md:flex-row my-4">
<div className="relative min-h-[200px] bg-toast-200 text-toast-800 rounded-lg p-4 overflow-hidden flex-1 flex flex-col gap-2">
<p className="text-lg flex-1 text-center text-toast-300 flex items-center justify-center">Area 1</p>
<Toaster
toasterId="area1"
position="top-center"
containerStyle={{ position: 'absolute' }}
/>
<button
onClick={() => toast('Notification for Area 1', { toasterId: 'area1' })}
className="bg-toast-600 text-white px-4 py-2 rounded-lg hover:bg-toast-600 w-full"
>
Show Toast in Area 1
</button>
</div>

<div className="relative min-h-[200px] rounded-lg p-4 overflow-hidden flex-1 flex flex-col gap-2" style={{ backgroundColor: 'rgba(154, 134, 253, 0.15)' }}>
<p className="text-lg flex-1 text-center text-[#876fff84] flex items-center justify-center">Area 2</p>
<Toaster
toasterId="area2"
position="top-center"
containerStyle={{ position: 'absolute' }}
toastOptions={{
className: '!text-white px-4 py-2 border !rounded-full',
style: {
backgroundColor: 'rgb(154, 134, 253)',
borderColor: 'rgba(154, 134, 253, 0.3)'
}
}}
/>
<button
onClick={() => toast('Notification for Area 2', { toasterId: 'area2' })}
className="text-white px-4 py-2 rounded-lg bg-[#9a86fd] w-full"
>
Show Toast in Area 2
</button>
</div>
</div>

## Basic Usage

You can create multiple toasters providing unique `toasterId` to each `<Toaster />` component:

```jsx
// Create a toaster with a unique id
<Toaster toasterId="area1" />

// Create another toaster with a unique id
<Toaster toasterId="area2" toastOptions={{ ... }} />
```

To create a toast in a specific toaster, you can pass the `toasterId` to the `toast` function.

```jsx
// Create a toast in area 1
toast('Notification for Area 1', {
toasterId: 'area1',
});
```

When no `toasterId` is provided, it uses `"default"` as the `toasterId`.

### Positioning the toaster

When placing a toaster in a specific area of your app, set the position to `absolute` and the parent element to `relative`.

```jsx
<div style={{ position: 'relative' }}>
<Toaster
toasterId="area1"
position="top-center"
containerStyle={{ position: 'absolute' }}
/>
</div>
```
3 changes: 3 additions & 0 deletions site/pages/docs/toast.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ toast('Hello World', {

// Additional Configuration
removeDelay: 1000,

// Toaster instance
toasterId: 'default',
});
```

Expand Down
5 changes: 5 additions & 0 deletions site/pages/docs/toaster.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This component will render all toasts. Alternatively you can create own renderer
gutter={8}
containerClassName=""
containerStyle={{}}
toasterId="default"
toastOptions={{
// Define default options
className: '',
Expand Down Expand Up @@ -67,6 +68,10 @@ Customize the style of toaster div. This can be used to change the offset of all

Changes the gap between each toast. Defaults to `8`.

### `toasterId` Prop

You can change the toasterId to have a different toaster instance. Learn more about [multiple toasters](/docs/multi-toaster). Defaults to `"default"`.

### `toastOptions` Prop

These will act as default options for all toasts. See [`toast()`](/docs/toast) for all available options.
Expand Down
89 changes: 70 additions & 19 deletions site/pages/docs/use-toaster.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,79 @@ export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

# `useToaster()` API

The `useToaster()` hook provides you a **headless system that will manage the notification state** for you. This makes building your own notification system much easier.
The `useToaster()` hook provides a **headless toast management system** for building custom notification UIs. It manages toast state and lifecycle without rendering any components.

It solves the following problems for you:
It handles pausing on hover, auto-removal, and provides a 1-second removal delay with `visible` flag for smooth animations.

- Built-in dispatch system with [`toast()`](/docs/toast)
- Handlers to pause toasts on hover
- Automatically remove expired toasts
- Support for unmount animations. Removal is delayed by 1s, but sets `visible` on the toast to `false`.
**Alternative**: Use [`useToasterStore()`](/docs/use-toaster-store) if you already have a toaster instance and only need the state.

### Importing from headless
### Importing

You can import only the core of the library with `react-hot-toast/headless`. It won't include any styles, dependencies or custom components.
```jsx
import { useToaster } from 'react-hot-toast';
```

You can also import from the headless entry point to exclude UI components:

```jsx
import { useToaster } from 'react-hot-toast/headless';
```

Be aware: [react-hot-toast 2.0](/docs/version-2) adds support for **custom render functions**, an easier method to render custom notification components.
**Note**: [React Hot Toast 2.0](/docs/version-2) includes **custom render functions** for easier custom components.

It's recommended to only have one `<Toaster/>` or `useToaster()` in your app at a time. If you need the current state without the handlers, you should use [`useToasterStore()`](/docs/use-toaster-store) instead.
## API Reference

## Usage with React Native
### Parameters

```tsx
useToaster(
toastOptions?: DefaultToastOptions,
toasterId?: string
)
```

| Parameter | Type | Default | Description |
| -------------- | --------------------- | ----------- | ----------------------------------------------- |
| `toastOptions` | `DefaultToastOptions` | `undefined` | Default options for all toasts in this instance |
| `toasterId` | `string` | `'default'` | Unique identifier for this toaster instance |

### Returns

```tsx
{
toasts: Toast[];
handlers: {
startPause: () => void;
endPause: () => void;
updateHeight: (toastId: string, height: number) => void;
calculateOffset: (toast: Toast, options?: OffsetOptions) => number;
};
}
```

#### `toasts`

Array of all toasts in this toaster instance, including hidden ones for animation purposes.

#### `handlers`

Headless mode is perfectly suited to add notifications to your React Native app. You can check out [this example](<https://snack.expo.io/@timo/react-hot-toast---usetoaster()---react-native>).
- **`startPause()`**: Pause all toast timers (useful for hover states)
- **`endPause()`**: Resume all toast timers
- **`updateHeight(toastId, height)`**: Update toast height for offset calculations
- **`calculateOffset(toast, options)`**: Calculate vertical offset for toast positioning

## Multiple Toasters

You can create multiple independent toaster instances by providing a unique `toasterId`. See the [Multiple Toasters](/docs/multi-toaster) guide for detailed examples.

```jsx
const sidebar = useToaster({ duration: 5000 }, 'sidebar');
toast('Sidebar notification', { toasterId: 'sidebar' });
```

## Examples

### Basic Example
### Basic Implementation

```jsx
import toast, { useToaster } from 'react-hot-toast/headless';
Expand All @@ -58,20 +103,20 @@ const Notifications = () => {
);
};

// Create toasts anywhere
// Create toasts from anywhere
toast('Hello World');
```

### Animated Example
### Animated Implementation

Instead of mapping over `visibleToasts` we'll use `toasts`, which includes all hidden toasts. We animate them based on `toast.visible`. Toasts will be removed from 1 second after being dismissed, which give us enough time to animate.
This example uses all `toasts` (including hidden ones) to enable smooth animations. The `toast.visible` property controls opacity, while the 1-second removal delay provides time for exit animations.

You can play with the demo on [CodeSandbox](https://codesandbox.io/s/react-hot-toast-usetoaster-headless-example-zw7op?file=/src/App.js).
**Live Demo**: [CodeSandbox](https://codesandbox.io/s/react-hot-toast-usetoaster-headless-example-zw7op?file=/src/App.js)

```jsx
import { useToaster } from 'react-hot-toast/headless';

const Notifications = () => {
const AnimatedNotifications = () => {
const { toasts, handlers } = useToaster();
const { startPause, endPause, calculateOffset, updateHeight } = handlers;

Expand All @@ -92,11 +137,12 @@ const Notifications = () => {
});

const ref = (el) => {
if (el && typeof toast.height !== "number") {
if (el && typeof toast.height !== 'number') {
const height = el.getBoundingClientRect().height;
updateHeight(toast.id, height);
}
};

return (
<div
key={toast.id}
Expand All @@ -119,3 +165,8 @@ const Notifications = () => {
);
};
```


## Usage with React Native

The headless API works perfectly with React Native. View the [React Native example](<https://snack.expo.io/@timo/react-hot-toast---usetoaster()---react-native>) for implementation details.
5 changes: 3 additions & 2 deletions src/components/toaster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,15 @@ export const Toaster: React.FC<ToasterProps> = ({
toastOptions,
gutter,
children,
toasterId,
containerStyle,
containerClassName,
}) => {
const { toasts, handlers } = useToaster(toastOptions);
const { toasts, handlers } = useToaster(toastOptions, toasterId);

return (
<div
id="_rht_toaster"
data-rht-toaster={toasterId || ''}
style={{
position: 'fixed',
zIndex: 9999,
Expand Down
Loading