Custom Toasts
Render whatever you want. We give you the wrapper. You bring the chaos.
toast.custom() lets you render arbitrary JSX inside a toast. No default layout. No icon. No title slot. Just a wrapper with animations and whatever you shove inside it.
Usage
import { toast } from "@vcui/popser";
toast.custom((id) => (
<div style={{ display: "flex", gap: 12, alignItems: "center" }}>
<img src="/avatar.png" width={40} height={40} />
<div>
<strong>New message</strong>
<p>Hey, are you coming to the meeting?</p>
</div>
<button onClick={() => toast.close(id)}>Dismiss</button>
</div>
));You get the toast id. Use it to close things. Or don't. Let it time out. Your funeral.
Examples
Different shapes for different moods. All custom, all yours.
Anchored custom
Custom toasts work with anchoring too. Pin your own JSX to an element.
toast.custom(
(id) => <MyTooltip id={id} />,
{
anchor: buttonRef.current,
anchorSide: "top",
arrow: true,
timeout: 2000,
}
);What you get
Even in full custom mode, you're not completely abandoned:
- Toast root wrapper (
data-popser-root) - Enter/exit animations
- Swipe-to-dismiss (unless
dismissible: false) - Proper stacking in the viewport
data-popser-idfor testing- Anchor positioning (if
anchoris set) - Arrow (if
arrow: trueandanchoris set)
What you don't get
The entire default layout is skipped:
- No icon
- No title/description slots
- No action/cancel buttons
- No close button
- No content wrapper
You want any of that? Build it. That's the deal.
Options
Same options as other methods, minus the content ones. Everything else works.
toast.custom(
(id) => <MyComponent id={id} />,
{
id: "custom-toast",
timeout: 5000,
dismissible: true,
deduplicate: true,
anchor: element,
anchorSide: "top",
arrow: true,
onClose: (id) => console.log("closed", id),
onRemove: () => console.log("gone"),
data: { source: "chat" },
}
);Styling
Custom toasts are transparent by design. No background, no border, no shadow, no padding. Your JSX renders clean without fighting default chrome.
Want the standard toast look back? Pass style with popser's CSS variables:
toast.custom(
(id) => <MyComponent id={id} />,
{
style: {
background: "var(--popser-bg)",
border: "1px solid var(--popser-border)",
borderRadius: "var(--popser-radius, 8px)",
boxShadow: "var(--popser-shadow)",
padding: 12,
color: "var(--popser-fg)",
},
}
);Works in light and dark mode. The tokens resolve automatically.
Or target custom toasts globally in CSS — they get data-type="custom" on the root:
[data-popser-root][data-type="custom"] {
background: var(--popser-bg);
border: 1px solid var(--popser-border);
border-radius: var(--popser-radius, 8px);
box-shadow: var(--popser-shadow);
padding: 12px;
color: var(--popser-fg);
}All standard data attributes still apply (data-popser-id, data-rich-colors, data-anchored, etc.).