# CHANGELOG
All notable changes to this project will be documented in this file.
> For v0.x changelog, see the [v0 branch](https://github.com/chakra-ui/zag/blob/v0/CHANGELOG.md)
## [1.46.0](./#1.46.0) - 2026-10-11
### Added
- **Dialog, Drawer**: Expose `api.modal` for conditional backdrop rendering.
```tsx
api.modal &&
```
- **Pin Input**
- Add `Ctrl`/`Cmd` + `ArrowLeft`/`ArrowRight` navigation to the first or last input, respecting RTL.
- Add `ArrowUp`/`ArrowDown` navigation to the first or last input, matching `Home`/`End`.
- Add a development warning when `count` doesn't match the number of rendered inputs.
- **Toast**: Allow `toaster.update(id, updater)` to derive updates from the current toast, including queued toasts.
```ts
toaster.update(id, (prev) => ({ duration: (prev.duration ?? 5000) + 1000 }))
```
- **TOC**: Add `getNavProps()` so the root can wrap page content separately from the navigation.
```tsx
```
### Fixed
- **Aria Hidden**: Keep nested dialogs, drawers, modal popovers, and other popups accessible to screen readers when
their portals mount before the parent modal opens.
- **Carousel**: Prevent slide content or size changes from interrupting dragging or smooth scrolling.
- **Checkbox**: Initialize the hidden input's indeterminate state when `checked` or `defaultChecked` is
`"indeterminate"`.
- **Color Picker**: Fix `api.setFormat` not changing the format or calling `onFormatChange`.
- **Date Picker**
- Label the calendar grid with the visible month in the picker's locale.
- Remove the English-only `aria-roledescription` from the content and table parts.
- Apply `translations.placeholder` to the input.
- **Focus Visible**: Keep `HTMLElement.prototype.focus` writable when another tool, such as Storybook, defines it as an
accessor.
- **Nested Popups**: Fix nested popups rendering behind their parent when using `--layer-index` with `lazyMount` or
`unmountOnExit`. Preserve layer order during exit animations.
> Affected Components: Cascade Select, Color Picker, Combobox, Hover Card, Menu, Popover, Select.
- **Pagination**: Omit `href` and mark unavailable first, previous, next, and last triggers as disabled when
`type="link"`.
- **Pin Input**: Run native form validation on Enter, including incomplete values. Focus a visible input when validation
fails.
- **Popover**: Fix missing `aria-labelledby` and `aria-describedby` on lazily mounted content.
- **Scroll Area**: Apply `data-dragging` only to the scrollbar and thumb being dragged.
- **Solid**: Fix stale values in consecutive state updates inside `batch()`.
- **Splitter**: Call `onCollapse` and `onExpand` on the first collapse or expand after mounting.
- **Toast**
- Apply updates, dismissal, and removal to queued toasts.
- Prevent dismissed promise toasts from reappearing when the promise settles.
- Keep timers paused when creating or updating toasts.
- Preserve toast updates made inside subscription callbacks.
- Keep `aria-labelledby` and `aria-describedby` in sync as titles and descriptions mount or unmount.
- **Tooltip**: Preserve the next tooltip's open delay when an open tooltip unmounts.
- **Tour**: Use visible labels as action triggers' accessible names. Use `attrs: { "aria-label": ... }` to label
icon-only actions.
- **Tree View**: Focus the rename input when pressing `F2` in adapters such as Vue.
### Changed
- **TOC**: Move `aria-labelledby` from `getRootProps()` to `getNavProps()`. Apply `getNavProps()` to the navigation
element to keep its accessible name.
## [1.45.0](./#1.45.0) - 2026-10-05
### Added
- **Initial Focus**: `initialFocusEl` can now return `false` to open without moving focus. Returning `null` still uses
the default.
```jsx
const service = useMachine(dialog.machine, {
initialFocusEl: () => false,
})
```
> Affected Components: Color Picker, Dialog, Drawer, Floating Panel, Popover.
- **Image Cropper**: Add `api.setCrop(rect)` to place the crop area programmatically, in viewport coordinates. The rect
is constrained like `initialCrop`, and unlike `api.reset()` it keeps the current zoom, rotation, flip, and pan.
- **Pagination**: Add `api.type` and `api.getPageUrl(page)`, so custom components can tell whether the controls are
buttons or links and build a page's URL without re-reading the machine props.
```jsx
api.type // "button" | "link"
api.getPageUrl(3) // "/page/3", or `undefined` when `getPageUrl` is not provided
```
### Fixed
- **Carousel**: Fix `allowMouseDrag` advancing a full page per pointer move in Vue.
- **Floating Panel**
- Fix `strategy: "absolute"` placing the panel outside its boundary. Absolute panels are now positioned against their
offset parent and clipped by a scrolling boundary.
- Fix the panel not following its boundary element when an ancestor scrolls.
- **Focus Visible**: Fix `Illegal invocation` thrown during setup when another tool has replaced
`HTMLElement.prototype.focus` with an accessor.
- **Hotkeys**: Fix an unhandled `TypeError` when a `keyup` event without a valid `key` reaches `document`.
- **Image Cropper**: Fix `Alt+Arrow` resizing ignoring `aspectRatio` and `cropShape: "circle"`.
- **Listbox**: Fix range selection anchoring on the highlighted item instead of the one you clicked.
- Shift+click with `highlightOnHover` now selects the full range.
- Successive shift+clicks extend from the originally clicked item, so a range can shrink as well as grow.
- Shift+arrow can reverse direction without leaving items behind.
- **Merge Props**: Fix inline style values containing semicolons being truncated, such as quoted CSS custom properties
and data URLs.
- **Popover**: Fix `autoFocus={false}` being ignored for modal popovers.
- **Popper**: Fix popper-based components ignoring a stylesheet `z-index` on the positioner when the content has no
stacking level.
- **Presence**: Fix an element with an exit animation staying mounted forever in WebKit, invisible but still
intercepting clicks.
- **Progress**: Fix the formatter not updating when `formatOptions` changes to a subset of the previous options.
- **Splitter**: Fix slow dragging in large documents. The global cursor style is now only written when the cursor
changes.
- **Svelte**: Fix `derived_inert` warnings when deferred machine cleanup runs after a component unmounts.
- **Tabs**: Fix programmatic tab selection triggering link navigation. Keyboard link activation now bubbles and can be
canceled by your click handlers.
- **TOC**: Fix `onActiveChange` reporting the previous `activeIds` and `activeItems`, which kept a controlled TOC stuck
on the old section.
- **Tour**
- Fix the step card, spotlight and backdrop collapsing into the top-left corner when the step's target element is
replaced.
- Fix the backdrop leaving part of the page undimmed on resize or when content loads in. The spotlight now only
animates between steps.
- Fix tours in an iframe or custom root measuring the outer page, and crashing on mount without `visualViewport` (e.g.
jsdom).
## [1.44.0](./#1.44.0) - 2026-09-12
### Added
- **Date Picker**: Add `data-invalid` to label and control so they can be styled when `invalid`.
- **DOM Query**: Add `getCaretPosition` for anchoring a popup, like a mention menu, to the caret.
- **Hotkeys**: Export `getPlatform` and `SequenceStep` type.
### Fixed
- **Color Picker**: Fix screen readers offering the color area and channel slider thumbs as adjustable when disabled.
- **Date Picker**
- Fix every year cell rendering as disabled outside the decade in view, even with no `min` or `max` set. Selectability
is `min`/`max` only now; outside cells keep `data-outside-range`.
- Fix `End` in the year view moving focus two years past the last cell.
- **Dismissable**: Fix popups closing themselves the instant they open when their content is kept mounted and
re-parented on open. `whenNode`'s `defer: true` now always waits for the commit.
- **File Upload**: Prevent large folder uploads from freezing the tab. Processing 50,000 files now takes milliseconds.
- **Focus Visible**: Fix a programmatic click leaving `:focus-visible` styling in place. Same-position pointer moves are
now ignored only for trusted events, so synthetic clicks restore pointer modality.
- **General**: Fix `api.setOpen` applying only the first of several calls made in the same tick, so opening then closing
from one handler left the component open. The last call now wins, as it does for Drawer's `api.setSnapPoint`.
- **Menu**: Fix `onSelect` reporting the highlighted item rather than the one clicked, so clicking while another item
was highlighted selected the wrong value.
- **Pin Input**: Fix `Ctrl/Meta+X` not cutting on non-React frameworks. Deletion is no longer treated as an invalid
typed character, so cutting shifts the remaining digits.
- **Vanilla**: Fix `spreadProps` replacing the whole `style` attribute, wiping inline styles set by other code like the
layer stack's `--layer-index` or popper's `--x`/`--y`.
### Changed
- **Date Utils**: `getDecadeRange` no longer accepts `{ strict }` and always returns ten years, not twelve. Drop the
option if you passed `{ strict: true }`; the twelve-year form is gone.
- **Vanilla**: `normalizeProps` and `mergeProps` now return `style` as an object, not a CSS string, and `spreadProps`
applies it per property. Use `toStyleString` for the string form.
## [1.43.3](./#1.43.3) - 2026-08-20
### Fixed
- Fix the exported `Partial` type shadowing TypeScript's built-in `Partial` and breaking `@vue/compiler-sfc` on
`interface X extends Partial`.
Optional properties are now written explicitly as `?: T | undefined`, so types like `IntlTranslations` and
`ElementIds` are plain interfaces. Passing a single translation key still works.
## [1.43.2](./#1.43.2) - 2026-08-20
### Added
- **File Upload**: Add `data-invalid` to the label props so you can style the label when the file upload is invalid.
- **Hotkeys**
- Add `target` option to scope a command to a DOM subtree. Pass an element or a function returning one, and the
command only fires for events inside it. Targeted commands win over global ones on the same hotkey, and the same
hotkey on different targets is no longer a conflict.
```ts
store.register({
id: "grid.down",
hotkey: "ArrowDown",
action: moveDown,
options: { target: () => gridEl },
})
```
- Add `normalizeHotkey`, which resolves equivalent hotkeys (`mod+k`, `Meta+K`) to one canonical string.
- Add Linux detection. `formatHotkey` now shows `Meta` as `Super` on Linux instead of `Win`.
- Export the `Platform` type.
- **Presence**: Add `onEnterComplete`, called when the enter animation finishes. Without an enter animation, it fires on
the next frame after mount. It doesn't fire on the initial render.
### Fixed
- **Hotkeys**
- Fix `isPressed` returning `false` for bare modifiers like `"shift"` or `"mod"`.
- Fix commands registered with `enabled: false` never firing, even after `enable()` or `setEnabled(id, true)`.
- Fix `subscribe` not reporting `pressedKeys` until at least one command was registered.
- Fix commands with `capture: false` never firing if the store was already listening.
- Fix `HotkeyRecorder.stop()` clearing the previous value when nothing new was recorded.
- Fix `HotkeyRecorder.cancel()` keeping a partial recording instead of restoring the previous value.
- Fix `addScope`, `removeScope` and `toggleScope` not resetting in-progress sequences like `setScope` does.
- Fix `formatHotkey("meta+K")` showing `Win` instead of `Super` on Linux. Android still shows `Win`.
- **Scroll Snap**: Fix `findSnapPoint` returning wrong positions for `center` and `end` aligned items and in RTL, which
broke carousel page detection.
- **Translations**: Fix `translations` requiring every message. Override one message and the rest fall back to the
defaults. `@zag-js/types` now exports a `Partial` compatible with `exactOptionalPropertyTypes`. Affects Carousel, Date
Input, Date Picker, Editable, Floating Panel, Image Cropper, Marquee, Pin Input, Progress, Rating Group, Signature
Pad, and Tags Input.
## [1.43.1](./#1.43.1) - 2026-08-16
### Fixed
- **Auto Resize**: Prevent XSS in `autoResizeInput()` by measuring input text with `textContent` instead of parsing it
as HTML.
- **Cascade Select, Listbox**: Preserve keyboard highlighting when scrolling moves content beneath a stationary pointer.
Applies to `highlightOnHover` in Listbox and `highlightTrigger: "hover"` in Cascade Select.
- **Combobox, Menu, Select**: Preserve keyboard highlighting when scrolling moves content beneath a stationary pointer,
including in Safari.
- **Core**: Fix `getExitEnterStates()` throwing during state transitions in browsers without `Array.prototype.at()`,
such as Safari below 15.4.
- **Date Picker**
- Allow individual `translations` overrides with `Partial`; omitted messages use defaults.
- Label the view trigger with the actual next view through `translations.viewTrigger`. Disable it when no further view
is available.
- Fix `translations.dayCell()` announcing the generic "Choose" label for dates between a selected range's start and
end. It now announces "In range".
- **Dismissable**: Handle `Escape` immediately after a dialog, popover, or menu mounts, including under CPU load.
- **Drawer, Navigation Menu, Steps, Tour**: Add `type="button"` to `getTriggerProps()` (steps, navigation-menu) and
`getCloseTriggerProps()` (drawer, tour). Without it these buttons defaulted to `type="submit"` and submitted an
ancestor form on click.
- **Floating Panel**
- Fix closing a panel not removing it from the stack, so the next panel now becomes topmost.
- Fix stack order not applying to the positioner, so focusing a panel raises it above its siblings.
- **Focus Visible**
- Fix the interaction modality being reported as `pointer` when content scrolls under a resting cursor. Safari emits
move events at an unchanged position in that case.
- Fix `trackFocusVisible()` classifying pointer focus as virtual when activating a label briefly moves focus to an
overlay container. Clicking a checkbox, radio or switch label added `data-focus-visible` to the control.
- **Image Cropper**: Keep fixed crop selections focusable and support `+`/`-` zoom shortcuts. Arrow keys pan the image;
`Alt+Arrow` does nothing in fixed mode.
- **QR Code**: Include the rendered overlay in `getDataUrl()` and in the file produced by `getDownloadTriggerProps()`.
The export previously contained only the QR matrix, so a logo or badge placed over the code was dropped.
- **Splitter**: Fix the element from `getResizeTriggerProps()` matching `:focus-visible` after a pointer drag. It still
takes focus, so keyboard resizing keeps working, but no longer shows the focus ring.
## [1.43.0](./#1.43.0) - 2026-07-28
### Added
- **Dialog, Drawer**: Add `data-autofocus` and `data-no-autofocus` to control which element gets focus on open. Mark
chrome controls like the close button with `data-no-autofocus` to skip them, or mark the real target with
`data-autofocus`.
```jsx
{/* skipped on open, still in tab order */}
{/* receives initial focus */}
```
Priority: `initialFocusEl` → `[data-autofocus]` → first tabbable without `[data-no-autofocus]` → content root.
- **Focus Trap**: Add a `persistentElements` option to declare portalled content as part of the trap when it isn't
reachable via `aria-controls`/`aria-expanded`.
- **Image Cropper**: Add exact natural-image `corners` and `outputSize` to `getCropData()`, plus a `maxSize` option to
limit `getCroppedImage()` output dimensions.
- **Popper**: Add an `applyStyles` option to control whether computed position styles are written directly to the DOM.
### Fixed
- **Auto Resize**: Prevent programmatic updates to a controlled textarea's `value` from dispatching synthetic `input`
events and disrupting controlled state.
- **Color Picker**: Prevent `Enter` from committing a partial channel value during IME composition.
- **Date Input**
- Keep segment text in sync when typing over a committed date.
- Fix in-progress edits being discarded while focus catches up after auto-advance. Fast typing and
`ArrowUp`/`ArrowDown`/`Home`/`End` now apply to the active segment.
- **Date Picker**
- Ignore cell clicks, clear actions, and presets when disabled or read-only. Read-only pickers keep roving-focus
navigation. Disabled pickers are out of the tab order.
- Respect `minView`, `maxView`, and `defaultView` when resolving the initial view.
- Respect controlled `open` over `defaultOpen` during initialization.
- Enforce `maxSelectedDates` on month and year cells in `multiple` selection mode.
- Align keyboard range selection with pointer selection. Selecting a third date now restarts the range, and the hover
preview updates for `Enter`, `Home`, `End`, and `PageUp`/`PageDown`.
- Resume incomplete range selection when reopening the calendar.
- **Drawer**: Prevent backdrop flicker when closing with an asynchronous controlled `open` setter.
- **Focus Trap**
- Avoid stealing focus from an element focused by the app during deactivation, such as a newly opened dialog.
- Prevent nested trap deactivation from throwing when the outer trap has no connected focusable element.
- Restore the focus ring after keyboard deactivation, such as `Escape`, using `focusVisible: true`.
- **Frameworks**
- Prefer controlled `value` over `defaultValue` in Vue and Svelte, matching React and Solid.
- Skip machine exit actions in Solid and Svelte when disposed before startup.
- **Image Cropper**: Fix `getCroppedImage()` and `getCropData()` returning a different region from the visible crop
after rotating or flipping the image.
- **Marquee**: Keep the configured `speed` consistent across content widths, including content smaller than the
viewport.
- **Popover**: Fix Tab looping back into portalled content when the trigger is the last tabbable element. Resume
navigation after the trigger.
- **React**: Keep `useMachine`'s `send`, `prop`, `context`, `computed`, `refs`, and `getStatus` references stable for
use in effect dependencies.
- **Remove Scroll**: Lock `` when it is the page's scroll container.
- **Signature Pad**: Keep in-progress strokes in `onDraw.currentPath` until drawing ends, preventing controlled `paths`
from going out of sync.
- **Solid**: Treat `value: null` as controlled instead of falling back to internal state.
- **Splitter**
- Size collapsed panels to `collapsedSize` instead of `minSize`.
- Keep keyboard resizing working when a hovered resize trigger receives focus.
- **Tour**
- Run cleanup and report `completed` when dismissing a tour from a step's `effect`.
- Preserve tooltip step positioning while the tour closes.
- Fix `action: "skip"` not skipping the step.
## [1.42.0](./#1.42.0) - 2026-06-29
### Added
- **Number Input**: Add `largeStep` (defaults to `10 * step`, applied on `Shift`) and `smallStep` (defaults to
`step / 10`, applied on `Alt`) props for configurable keyboard stepping. The defaults preserve the previous stepping
magnitudes.
- **Slider**: Add `largeStep` prop (defaults to `10 * step`), applied on `Shift` or `PageUp`/`PageDown`. The default
preserves the previous stepping magnitude.
### Fixed
- **Cascade Select**: Select the highlighted leaf node on `Enter` in non-React frameworks.
- **Date Input**
- Allow typing dates using the locale's native numerals (e.g. Arabic-Indic `٠-٩`, Devanagari `०-९`) in addition to
ASCII digits. Latin-locale behavior is unchanged.
- Preserve `CalendarDate` and `CalendarDateTime` values when using a custom `formatter` without an explicit
`timeZone`. Use the formatter's resolved time zone instead of shifting by the viewer's UTC offset.
- **Date Picker**
- Allow typing dates using the locale's native numerals (e.g. Arabic-Indic `٠-٩`, Devanagari `०-९`) in addition to
ASCII digits.
- Add date reordering on blur for range selection to match other selection paths.
- Fix day view briefly flashing when closing the date picker from the month or year view.
- Fix `visibleRangeText` returning a stale value when multiple date pickers with different `selectionMode` (or
`timeZone`) share the same visible range. This previously surfaced as SSR hydration mismatches.
- **Menu**: Prevent context menus from flashing at the top-left corner and position long-press menus at the touch point.
- **Number Input**
- Fix `api.setValue(number)` throwing when `formatOptions` is defined.
- Fix `Cmd`/`Ctrl` + arrow keys producing values off the `step` grid (e.g. non-integer values when `step: 1`).
- **Slider**: Fix `Cmd`/`Ctrl` + arrow keys producing values off the `step` grid (e.g. non-integer values when
`step: 1`).
- **Tags Input**: Fix native form submit so `FormData` reflects the current tags. The hidden input previously kept its
initial value after tags were added, removed, or cleared.
- **Toast**: Fix toast height measurement including the `scale` transform in overlap mode, causing a height flicker when
expanding the stack. Height is now measured from the untransformed element.
## [1.41.2](./#1.41.2) - 2026-06-05
### Fixed
- **Date Input**: Fix date segment placeholders for locales with explicit script subtags.
- **Drawer**
- Fix controlled drawer flickering when swiped or backdrop-closed while the `open` setter is asynchronous (e.g.
history API or a delayed state update).
- Keep nested-drawer layout metrics in machine state so swipe and backdrop-close transitions stay visually stable.
- **Image Cropper**: Fix `getCroppedImage` and `getCropData` returning the wrong region when the image is displayed at a
size different from its natural resolution (e.g. `width/height: 100%`).
- **Pin Input**: Apply `data-filled` only to filled inputs on initial render.
- **Signature Pad**: Forward `dir` to the DOM.
## [1.41.1](./#1.41.1) - 2026-05-26
### Fixed
- **Dismissable**: Fix layer `pointer-events` being wiped by frameworks (Svelte, Vue) whose spread updates rewrite the
entire `style` attribute.
- **Drawer**
- Fix controlled drawers snapping back to open before the close animation when dismissed via swipe.
- Fix indent and indent-background snapping back into place after the close animation instead of transitioning in
sync.
- Fix `--drawer-swipe-progress` jumping to `1` at the start of a dismiss swipe; it now goes smoothly from `0` (at
rest) to `1` (fully dismissed).
- Fix drawer freezing mid-drag on release when its content mounts lazily which left snap points unmeasured.
## [1.41.0](./#1.41.0) - 2026-05-22
### Added
- **Floating Components**: Add `data-side` to placement-aware parts based on the current placement.
> Affected Components: Cascade Select, Color Picker, Combobox, Date Picker, Hover Card, Menu, Popover, Select,
> Tooltip, Tour.
- **Date Input**
- Add `hideTimeZone` prop. The `timeZoneName` segment now renders automatically when the value is a `ZonedDateTime`,
and can be hidden via `hideTimeZone: true`.
- Arrow navigation and auto-advance after typing now reach read-only focusable segments (e.g. `timeZoneName`). Typing
the final editable segment (e.g. "P" on `dayPeriod`) advances focus to the trailing read-only segment instead of
staying put.
- **Splitter**
- Add CSS unit support for `defaultSize`, `minSize`, and `maxSize`. The splitter now accepts `px`, `em`, `rem`, `vh`,
and `vw` in addition to percentages, and resolves them to percentages after hydration.
```tsx
const service = useMachine(splitter.machine, {
panels: [
{ id: "nav", minSize: "240px", maxSize: "480px" },
{ id: "main", minSize: 30 },
],
defaultSize: ["240px", "60vw"],
})
```
- Add `resizeBehavior` per panel. Set to `"preserve-pixel-size"` to keep a panel's pixel size constant when the parent
splitter group resizes. Leave at least one panel as `"preserve-relative-size"` (the default) so the layout can
absorb the change.
```tsx
panels: [
{ id: "nav", minSize: 20 },
{
id: "main",
minSize: "240px",
maxSize: "480px",
resizeBehavior: "preserve-pixel-size",
},
{ id: "aside", minSize: 20 },
]
```
- Allow non-panel children inside the splitter root for fixed toolbars, rails, or status areas that should not be
managed as panels. Use partial trigger ids (`"left:"`, `":right"`) to bind handles around the fixed element.
```tsx
Left
Fixed sized element
Right
```
- **TOC**: Add `api.scrollTo(value, details?)` for programmatically scrolling to a heading. The optional
`details.behavior` controls the scroll behavior; when omitted, the platform default applies.
```tsx
api.scrollTo("installation", { behavior: "smooth" })
```
### Fixed
- **Accordion**: Remove redundant `aria-disabled` from accordion item triggers.
- **Color Picker**: Invoke `onValueChangeEnd` when the user picks a color with the EyeDropper API, consistent with
ending a drag on the area or channel sliders.
- **Combobox**: Fix `Enter` no longer submits the form when an item is highlighted (regardless of `allowCustomValue`),
or when the typed value will be rejected by `allowCustomValue: false`.
- **Date Input**
- Fix min/max handling to preserve entered segments while editing. Values are now clamped segment-by-segment on blur,
so `06/15/1999` with min `2000-01-01` becomes `06/15/2000` instead of snapping to `01/01/2000`.
- Fix range mode keyboard navigation so `ArrowRight` moves from the last segment of the start date to the first
segment of the end date.
- Fix time-only formatters (no `year` segment) never firing `onValueChange` — `era` is now only required when `year`
is present.
- Use the latest `IncompleteDate` when checking whether `setSegmentValue` can commit.
- Keep AM/PM arrow navigation in sync when `hourCycle` changes, preserving in-progress edits.
- Update the visible AM/PM segment when typing "A" or "P".
- **Date Picker**
- Fix `VALUE.CLEAR` not resetting `activeIndex` and `hoveredValue` in range mode when `getInputProps` inputs are not
rendered.
- Allow date entry in locales with multi-character separators (e.g. `cs-CZ`, `sk-SK`, `hu-HU`, `ko-KR` which use
`". "`).
- Allow native month/year `