Changelog
What changed, when, and why you should care.
1.2.0
The "Sonner issues tab as a roadmap" release. Eight features, every one of them pulled from a real GitHub issue where someone asked Sonner for it and got silence. 39 new tests.
Added
- Animation duration customization.
--popser-transition-durationand--popser-anchored-transition-durationCSS custom properties. Pure CSS, zero JS. (Sonner #690) - Popover API support. Toast viewport uses
popover="manual"+showPopover(). Toasts render in the top layer, above dialogs and modals. Progressive enhancement. (Sonner #655, #667) - Expanded limit.
expandedLimitprop on<Toaster>. Collapsed showslimit, hover expands toexpandedLimit. (Sonner #637) - Close button position.
closeButtonPosition:"header"(default) or"corner"(absolute top-right). Per-toast override supported. (Sonner #747) - AbortSignal for promise toasts.
signal,aborted, andonAbortontoast.promise(). Cancel in-flight requests. Default aborted type:"warning". (Sonner #681) - Entry direction.
enterFrom:"top"|"bottom"|"left"|"right". Per-toast animation direction override. (Sonner #671) - RTL support.
dirprop on<Toaster>:"ltr"|"rtl"|"auto". Flips positions, swipe directions, and animations. - Toast history.
toast.getHistory()andtoast.clearHistory(). Opt-in ring buffer viahistoryLengthprop.ToastHistoryEntrytype exported.
Changed
- 7 CSS files updated for new features.
types.tsexpanded with new option interfaces.index.tsbarrel export updated withToastHistoryEntry.
1.1.2
The shadcn registry actually works now. Also fixed some docs that were lying about import names.
Added
- shadcn registry served from the docs site. Direct install:
npx shadcn add https://popser.vcui.dev/r/popser.json. - Namespace install: configure
@vcode-shin yourcomponents.json, thennpx shadcn add @vcode-sh/popser. /llms.txt— concise index of all docs pages with links and descriptions./llms-full.txt— full documentation concatenated into one plain text file. JSX demo components stripped out.
Fixed
- shadcn docs referenced
<Popser>but the registry component exports<Toaster>. Fixed everywhere. - README CSS examples used HSL values. The token system is OKLCH. Now they match.
- Factual accuracy sweep across all docs: corrected @import file count (6 not 7), Base UI release date (late 2025 not early 2026), Sonner classNames count, update() API comparison, CSS bundling description, migration example, and removed unverifiable quotes.
Changed
- Registry homepage updated from GitHub to popser.vcui.dev.
No code changes
Zero changes to src/. Same build output as 1.1.0.
1.1.1
The paperwork patch. No code changed. The docs got a promotion.
Added
- Documentation site: popser.vcui.dev — full docs, interactive demos, changelog.
- Why I built this — the honest version of the README's "Why" section.
- Why Base UI, not Radix — the technical argument for headless primitives in 2026.
- Collaborate — contribution guide with actual context, not just a checklist.
- Interactive toast demos across docs pages (types, anchored, custom, styling).
- Open Graph images and sitemap for the documentation site.
Changed
README.mdrewritten with bundle size table, docs site links, and contributing section.CONTRIBUTING.mdrewritten with architecture reference and human tone.package.jsonhomepage now points to popser.vcui.dev.- Comparison docs (
popser-sonner.md,popser-toast.md) updated with v1.1.0 bundle sizes.
No code changes
Zero changes to src/. Same build output as 1.1.0. If you're already on 1.1.0, this is just metadata.
1.1.0
The diet patch. Same features, half the calories.
The numbers
JS bundle:
| Raw | Gzipped | |
|---|---|---|
| 1.0.0 | 29.0 KB | 6.6 KB |
| 1.1.0 | 13.4 KB | 4.8 KB |
| -54% | -27% |
CSS bundle (via @vcui/popser/styles/min):
| Raw | Gzipped | |
|---|---|---|
| 1.0.0 | 24.9 KB | 5.4 KB |
| 1.1.0 | 14.3 KB | 2.6 KB |
| -43% | -52% |
Total: 7.4 KB gzipped. Sonner ships ~17 KB. We're less than half.
What actually happened
JS minification — the dist was shipping formatted, readable JavaScript. Lovely for education. Terrible for your users' bandwidth. Added minify: true to tsup. Problem solved.
Flat CSS bundle — @vcui/popser/styles uses @import directives pointing to 6 sibling files. Fine if your bundler resolves CSS imports. Not fine if you're using a <link> tag like it's 2014. New @vcui/popser/styles/min export inlines everything into one minified file.
// Still works, still modular
import "@vcui/popser/styles";
// New: single file, minified, no @imports
import "@vcui/popser/styles/min";Promise handler dedup — toast.promise() had near-identical successHandler and errorHandler blocks. ~100 lines of copy-paste. Extracted into buildPromiseHandler(). Same behaviour, fewer lines, easier to maintain.
Dead code cleanup — three constants (DEFAULT_EXPAND, DEFAULT_RICH_COLORS, DEFAULT_THEME) were exported but never imported anywhere. tsup tree-shook them from the dist anyway, but dead code in source is still dead code. Removed.
Spinner hoist — the loading spinner creates 12 <div> elements. Previously allocated a new array on every render. Now hoisted to module scope. The React reconciler thanks us.
No breaking changes
Everything from 1.0.0 works exactly the same. The @vcui/popser/styles import is unchanged. The new @vcui/popser/styles/min is purely additive.
1.0.0
The "we're not prefixing everything with 0. anymore" release. Same library, bigger number, calmer energy.
Added
- Close buttons now visible on all toasts when stack is expanded (hover mode). Previously only the front toast showed its close button on hover. Now when you expand the stack, every toast gets one. Revolutionary, I know.
- Full documentation rewrite. Modular docs for every feature: API, Toaster, styling, promise toasts, anchored toasts, custom toasts, shadcn, testing. Plus updated comparison docs against Sonner and Base UI Toast. The README is no longer a wall of text pretending to be documentation.
Changed
- Version number. That's the breaking change. The API is identical to 0.4.0. If your code worked yesterday, it works today.
0.4.0
The "everything should have been smooth from the start" release. Theme detection actually works now, animations don't jank, and the expand/collapse transition stops looking like a PowerPoint slide from 2003.
Added
theme="system"now resolves to actual"light"or"dark"viamatchMedia("prefers-color-scheme"). Previously it just passed"system"as a string todata-themeand hoped your CSS would figure it out. Now it does the work.- Anchored toast enter/exit: dedicated
scale(0.95)fade animation instead of inheriting the viewport slide. - Expanded close buttons: hover-mode close buttons show at
opacity: 0.5when the stack is expanded. Mobile close button opacity bumped from 0.5 to 0.7.
Changed
- Expanded stack layout completely rewritten. Toasts stay
position: absolutein both collapsed and expanded states. Expand/collapse is now a smooth animated transition using--toast-offset-yoffsets instead of the old flex-column layout swap that caused a jarring jump. This is the big one. - Mobile forces
bottom-centerposition regardless of thepositionprop. Because top-right toasts on a 375px screen is a crime. - Mouse handlers moved from individual
<Toast.Root>elements to the viewport. Fewer event listeners, same behavior. - Exit animations: toasts now slide back in the direction they entered (bottom toasts slide down to exit, top toasts slide up). Previously they slid in the opposite direction, which looked wrong.
- All stacking transitions use
cubic-bezier(0.32, 0.72, 0, 1)easing. The old linear 400ms felt like moving furniture. - Arrow CSS rewritten: proper per-side positioning with border triangles. Each
data-sidegets its own transform, border pair, and edge pinning. The old "rotate 45deg and hope" approach is gone. - Spinner:
contain: contentreplaceswill-change: opacity. Less GPU layer promotion, same visual. - Action button hover:
filter: brightness(1.15)replacesopacity: 0.9. Looks better on colored backgrounds. will-change: transform, opacityadded to stacking transitions for GPU acceleration.- Expanded viewport uses
::beforepseudo-element to extend hover area instead of changing layout. Prevents Floating UI coordinate breakage for anchored toasts. - Gap-filling
::afterpseudo-elements on expanded toasts prevent mouseLeave when moving between stacked items.
Removed
onMouseEnter/onMouseLeaveprops fromPopserToastRoot. Mouse tracking is viewport-level now.
0.3.1
Changed
- Package renamed from
popserto@vcui/popser. Scoped under the@vcuiorg because npm naming is a land grab and I arrived late. - All imports, docs, and registry references updated to
@vcui/popser.
0.3.0
Anchored toasts. Pin a toast to a button, a click event, or arbitrary coordinates. Think tooltips but for notifications.
Added
- Anchored toasts via
anchorprop. AcceptsElement,MouseEvent, or{x, y}coordinates. anchorSide,anchorAlign,anchorOffset,anchorAlignOffset,anchorPositionMethod,anchorSticky,anchorCollisionBoundary,anchorCollisionPadding— the full Floating UI integration.arrow: show arrow pointing at anchor (default: false). WitharrowPaddingfor edge spacing.anchor-resolver.ts: resolvesMouseEventand{x, y}to synthetic DOM elements with automatic cleanup.- Only one anchored toast visible at a time. New one closes the previous.
- Anchored toasts excluded from viewport stacking (no collapse/expand).
PopserAnchortype exported.data-anchoredattribute on anchored toast roots.
Changed
- Sourcemaps disabled in build output. Your bundle debugger was never going to trace through Base UI anyway.
0.2.0
The real first release. 0.1.x was a series of increasingly frantic patches that should have been one version. This is the one where it actually works properly.
Added
toast.custom((id) => jsx): render arbitrary JSX, bypass default layout. Renders withdata-type="custom".toast.message(): explicit default-type toast.toast.dismiss(): alias fortoast.close(). Sonner compatibility.toast.getToasts(): returns array of active toast IDs.durationoption: alias fortimeout. Because Sonner users shouldn't have to read a migration guide for a timer prop.onAutoClose(id)callback: fires when toast auto-dismisses by timeout.onDismiss(id)callback: fires when user manually dismisses.deduplicateoption: prevents duplicate toasts with the same string title.- Per-toast
dismissibleprop.ariaLabelprop on<Toaster>. - Promise handlers accept
ReactNode(JSX), not just strings. - Promise extended results: return
{ title, description, timeout, icon, action }from success/error handlers. - Promise conditional skip: return
undefinedto dismiss silently. - Promise
finallyhandler. Lazy evaluation: pass() => promise. - Per-toast
classNameswith 3-level merge (Toaster > toastOptions > per-toast). - Action and cancel accept both
PopserActionobjects and rawReactNode. - All callbacks receive toast ID as argument.
- Internal toast data stored under
data.__popsernamespace. data-popser-idattribute on every toast root for E2E test targeting.- CSS:
prefers-reduced-motiondisables all animations. Swipe-direction-specific exit animations.
Changed
- Stacking rewritten: absolute positioning with CSS variables instead of flex layout.
- Close button hover mode shows on toast hover, always visible on mobile.
- Promise toast:
queueMicrotask(() => getManager().close(id))when handler returnsundefined.
Fixed
- Content behind non-front toasts properly hidden with
opacity: 0transition. - Reduced motion: all transitions disabled, not just animations.
0.1.0
First release. Everything is new. Nothing is battle-tested. Ship it.
Added
- Imperative toast API:
toast(),.success(),.error(),.info(),.warning(),.loading() - Promise toasts:
toast.promise()with loading/success/error states - Toast management:
toast.close(),toast.update() <Toaster>component with configurable position, limit, timeout, theme- Built-in SVG icons for all toast types + CSS spinner for loading
- Action and cancel buttons per toast
- Close button with always/hover/never visibility modes
- Rich colors support via
richColorsprop - CSS custom properties for full theming (
--popser-*prefix). Light and dark mode tokens (OKLCH). - Mobile responsive behavior with configurable
mobileBreakpoint - Swipe to dismiss with configurable direction(s)
useToaster()hook for React context consumers- shadcn registry support (
npx shadcn add @vcode-sh/popser) - Default styles via
@vcui/popser/styles. Separate tokens via@vcui/popser/tokens. Unstyled mode for full CSS control. - Stacking with expand-on-hover
- ARIA live regions with configurable priority. F6 keyboard navigation.
data-popser-*attributes on every element for stable CSS/test selectors- ESM + CJS dual output with TypeScript declarations
- Singleton toast manager with proper cleanup. Error boundary in Toaster.