Notification
freeNotification gives toast and inline feedback messages a consistent content frame.
motion
Preview
Dark
Invite sent
Taylor will receive an email with workspace access instructions.
import Notification from '@/components/ui/Notification';
export default function UsageDemo() {
return (
<div className="flex justify-center">
<Notification
closable
type="success"
title="Invite sent"
>
Taylor will receive an email with workspace access instructions.
</Notification>
</div>
);
}Installation
Add this component with the NateUI CLI.
npx nateui@latest add NotificationExamples
Basic
Preview
Dark
Export ready
The report is available in your downloads folder.
import Notification from '@/components/ui/Notification';
export default function BasicDemo() {
return (
<div className="flex justify-center">
<Notification title="Export ready">
The report is available in your downloads folder.
</Notification>
</div>
);
}Status
Preview
Dark
Payment captured
The invoice is marked as paid.
Sync scheduled
New records will sync during the next run.
Review needed
Two imported rows need field mapping.
Upload failed
The CSV could not be processed.
import Notification from '@/components/ui/Notification';
const messages = [
{
type: 'success',
title: 'Payment captured',
body: 'The invoice is marked as paid.',
},
{
type: 'info',
title: 'Sync scheduled',
body: 'New records will sync during the next run.',
},
{
type: 'warning',
title: 'Review needed',
body: 'Two imported rows need field mapping.',
},
{
type: 'danger',
title: 'Upload failed',
body: 'The CSV could not be processed.',
},
] as const;
export default function StatusDemo() {
return (
<div className="flex w-full max-w-md flex-col gap-4">
{messages.map((message) => (
<Notification
key={message.type}
type={message.type}
title={message.title}
width="100%"
>
{message.body}
</Notification>
))}
</div>
);
}Custom Icon
Preview
Dark
Automation complete
The enrichment workflow updated 24 customer records.
import { PiSparkle } from 'react-icons/pi'
import Avatar from '@/components/ui/Avatar';
import Notification from '@/components/ui/Notification';
export default function CustomIconDemo() {
return (
<div className="flex justify-center">
<Notification
title="Automation complete"
customIcon={
<Avatar shape="round"
className="text-primary bg-transparent"
icon={<PiSparkle className="h-4 w-4" />}
/>
}
>
The enrichment workflow updated 24 customer records.
</Notification>
</div>
);
}Closable
Preview
Dark
Draft restored
The last autosaved version is back in the editor.
import { useState } from 'react';
import Button from '@/components/ui/Button';
import Notification from '@/components/ui/Notification';
export default function ClosableDemo() {
const [visible, setVisible] = useState(true);
return (
<div className="flex w-full max-w-md flex-col items-start gap-4">
{visible ? (
<Notification
closable
duration={0}
onClose={() => setVisible(false)}
title="Draft restored"
type="info"
width="100%"
>
The last autosaved version is back in the editor.
</Notification>
) : (
<Button onClick={() => setVisible(true)}>
Restore notification
</Button>
)}
</div>
);
}Width
Preview
Dark
Compact
Use a fixed width for short toast stacks.
Full row
Use a fluid width when the message sits inside a page region.
import Notification from '@/components/ui/Notification';
export default function WidthDemo() {
return (
<div className="flex w-full max-w-lg flex-col gap-4">
<Notification
title="Compact"
type="info"
width={280}
>
Use a fixed width for short toast stacks.
</Notification>
<Notification
title="Full row"
type="success"
width="100%"
>
Use a fluid width when the message sits inside a page region.
</Notification>
</div>
);
}Duration Callback
Preview
Dark
Background import
The import is taking longer than expected.
Waiting for the timeout callback...
import { useState } from 'react';
import Notification from '@/components/ui/Notification';
export default function DurationDemo() {
const [status, setStatus] = useState(
'Waiting for the timeout callback...',
);
return (
<div className="flex w-full max-w-md flex-col gap-2">
<Notification
duration={2000}
onClose={() => setStatus('Timeout callback fired.')}
title="Background import"
type="warning"
width="100%"
>
The import is taking longer than expected.
</Notification>
<div className="text-xs text-muted-foreground">
{status}
</div>
</div>
);
}Toast
Preview
Dark
import Button from '@/components/ui/Button';
import Notification from '@/components/ui/Notification';
import toast from '@/components/ui/toast';
export default function ToastDemo() {
return (
<Button
onClick={() =>
toast.push(
<Notification type="success" title="Changes saved">
Your profile updates are live.
</Notification>,
{ placement: 'top-center' },
)
}
>
Save changes
</Button>
);
}Toast Placement
Preview
Dark
import { useState } from 'react';
import Button from '@/components/ui/Button';
import Notification from '@/components/ui/Notification';
import Select from '@/components/ui/Select';
import toast from '@/components/ui/toast';
const PLACEMENTS = [
'top-start',
'top-center',
'top-end',
'bottom-start',
'bottom-center',
'bottom-end',
] as const;
const options = PLACEMENTS.map((value) => ({ value, label: value }));
export default function ToastPlacementDemo() {
const [placement, setPlacement] = useState(options[2]);
return (
<div className="flex items-center gap-2">
<Select
size="sm"
className="min-w-36"
isSearchable={false}
value={placement}
options={options}
onChange={(option) => setPlacement(option)}
/>
<Button
onClick={() =>
toast.push(
<Notification
type="info"
title={`Placed at ${placement.value}`}
/>,
{ placement: placement.value },
)
}
>
Push toast
</Button>
</div>
);
}Duration
Preview
Dark
import Button from '@/components/ui/Button';
import Notification from '@/components/ui/Notification';
import toast from '@/components/ui/toast';
export default function ToastUndoDemo() {
function notificationNeverClose() {
toast.push(
<Notification closable title="Success" type="success" duration={0} />,
);
}
function closeAfter2000ms() {
toast.push(
<Notification closable title="Success" type="success" duration={2000} />,
);
}
return (
<div>
<Button className="mr-2" onClick={notificationNeverClose}>
Persist
</Button>
<Button className="mr-2" onClick={closeAfter2000ms}>
Close after 2s
</Button>
</div>
);
}Custom Close
Preview
Dark
import Button from '@/components/ui/Button';
import Notification from '@/components/ui/Notification';
import toast from '@/components/ui/toast';
export default function ToastStackingDemo() {
function closeNotification(
key: string | undefined | Promise<string | undefined>,
) {
Promise.resolve(key).then((resolvedKey) => {
if (resolvedKey) toast.remove(resolvedKey);
});
}
function openNotification() {
const notificationKey = toast.push(
<Notification title="Are you sure?" duration={0}>
<div>This action can't be undone.</div>
<div className="mt-4">
<Button
size="sm"
variant="solid"
className="mr-2"
onClick={() => closeNotification(notificationKey)}
>
Confirm
</Button>
<Button size="sm" onClick={() => closeNotification(notificationKey)}>
Close
</Button>
</div>
</Notification>,
);
}
return <Button onClick={openNotification}>Show toast</Button>;
}API
Notification renders a message container. When used through toast.push, the toast wrapper injects removal behavior around the same component.
| Prop | Description | Type | Default |
|---|---|---|---|
children | Main notification message content. | ReactNode | - |
className | Class names applied to the root notification. | string | - |
closable | Shows the close control. | boolean | false |
customIcon | Replaces the status icon area with custom content. | ReactNode | string | - |
duration | Delay in milliseconds before the timeout helper calls onClose. Pass 0 to disable it. | number | 3000 |
onClose | Called by the close control and by the timeout helper when enabled. | NotificationCloseHandler | - |
ref | Ref forwarded to the root notification element. | Ref<HTMLDivElement> | - |
title | Optional heading rendered above the message. | string | - |
titleClass | Class names applied to the title element. | string | - |
triggerByToast | Toast integration flag normally supplied by the toast wrapper. | boolean | - |
type | Status icon color and glyph. | NotificationStatus | - |
width | Width applied to the root notification style. | NotificationWidth | 350 |
...nativeDivProps | Native props applied to the root div. | NativeNotificationProps | - |
Toast
toast is an imperative service that mounts a notification into its own layer outside your component tree, so you can trigger feedback from event handlers without rendering the message inline. Pass a cloneable <Notification> element as the message; the wrapper injects onClose and removal behavior into it. push returns a key for later removal — the first call for a given placement resolves asynchronously, so normalize the return with Promise.resolve(key) before calling remove.
| Method | Description | Signature |
|---|---|---|
toast.push | Mounts message and returns a removal key. | (message: ReactNode, options?: ToastProps) => string | undefined | Promise<string | undefined> |
toast.remove | Removes the toast matching a key from push. | (key: string) => void |
toast.removeAll | Removes every mounted toast. | () => void |
ToastProps | Description | Type | Default |
|---|---|---|---|
placement | Corner or edge the toast stack anchors to. | NotificationPlacement | 'top-full' | 'bottom-full' | 'top-end' |
transitionType | Enter and exit animation style. | 'scale' | 'fade' | 'scale' |
offsetX | Horizontal distance from the placement edge. | string | number | 30 |
offsetY | Vertical distance from the placement edge. | string | number | 30 |
block | Stretches the toast to the full placement width. | boolean | false |
Types
type NotificationStatus = 'success' | 'warning' | 'danger' | 'info';
type NotificationWidth = number | string;
type NotificationCloseHandler = (
event?: MouseEvent<HTMLSpanElement>,
) => void;
type NativeNotificationProps = ComponentProps<'div'>;
type NotificationPlacement =
| 'top-start'
| 'top-center'
| 'top-end'
| 'bottom-start'
| 'bottom-center'
| 'bottom-end';