docs/overview/changelog

Changelog

What changed in each release.

The package follows semver. Until 1.0.0, a minor version (0.x.0) may break the API; each breaking change is listed under its release.

v0.4.0#

October 22, 2026.

New features#

  • Cropper, at @logic-ui/react/cropper: an image cropper ported from advanced-cropper and react-advanced-cropper, in one headless entry point. Cropper.Root, Viewport, Image, Stencil, Handle, Grid and Preview render no styles: they expose data-* attributes and CSS variables. One pointer gesture engine resizes from corners and edges, moves the crop, pans the image, pinches and zooms with the wheel. ZoomIn, ZoomOut, Rotate, Flip and Reset act on it, the keyboard moves, resizes and zooms, and actionsRef draws the crop with getCanvas(). stencilSize keeps the crop a fixed size on screen while the image moves behind it, and Cropper.Viewport's padding keeps the crop clear of bars laid over it.

v0.3.0#

October 12, 2026.

Breaking changes#

  • Lightbox.Media's width and height set the box's shape when both are given, so every slide can keep the same shape. The loaded media's size is used only when they are missing.
  • Lightbox.Media keeps the object-fit you set on your media. It is contained only when you set none, instead of always.

New features#

  • fill on Lightbox.Media makes the box fill the slide, inside its padding, whatever the shape of the media. The flights and the pull to dismiss use that box.

Fixes#

  • A flick past the first or last item springs back at the normal pace, instead of snapping back in about 30 ms.
  • A zoomed image keeps its zoom until it has slid out of sight, instead of snapping to 1x while it slides out. Swiping back to it before then keeps its zoom.

v0.2.0#

October 12, 2026.

Breaking changes#

  • Built on @base-ui/react 1.9.0 (was 1.8.0) and @base-ui/utils 0.5.0. Select, Combobox and Autocomplete unmount on close whether or not actionsRef is set; call eventDetails.preventUnmountOnClose() in onOpenChange to keep them mounted. onItemHighlighted receives the originating event. See the Base UI 1.9.0 release notes.
  • Gallery is now Lightbox, at @logic-ui/react/lightbox. Every part, type, hook and constant is renamed (GalleryRoot → LightboxRoot, GALLERY_ZOOM_SCALE → LIGHTBOX_ZOOM_SCALE), and the CSS variable --gallery-dismiss-progress is now --lightbox-dismiss-progress. The lightbox is the full-screen viewer only: the page around the triggers is yours.
  • Items are picked by value, not by index. Root takes value / defaultValue / onValueChange (the value of the item shown, never null: it stays on the last item after closing, and defaults to the first) instead of index / defaultIndex / onIndexChange. Whether the viewer is open stays apart, in open / defaultOpen / onOpenChange. An item's value is its id, or what the new itemToValue returns. Trigger takes value instead of index; pressing it calls onValueChange when it shows another item, then onOpenChange. The open item stays open when items are added, removed or reordered.
  • Item is now Slide, and takes no index: return it from the function passed to Viewport. With thumbnails, the slides are their tab panels.
  • Image is replaced by Media, which wraps your own <img>, <video> or <iframe>: <Lightbox.Media width={photo.width} height={photo.height}><img src={photo.src} alt={photo.alt} /></Lightbox.Media>. It takes the natural width and height; the src and alt go on your element. The whole box flies and follows the pull to dismiss, and only an <img> zooms. LightboxImageStatus is LightboxMediaStatus.
  • Items can have any shape. GalleryItemData is removed. The parts take what they need as props, and Description takes a function child to show the item's caption: {(photo) => photo.caption}.
  • Value is now Counter.
  • ZoomValue's function child receives its state, {(state) => state.scale}, instead of the scale.
  • Removed List and More. Lay out the triggers with your own elements.
  • Removed Toolbar and Separator. Use Base UI's Toolbar and render each action as a Toolbar.Button: <Lightbox.Next render={<Toolbar.Button />} />.
  • Removed Delete and Download. Build them as buttons in the popup.
  • Image's data-loaded is replaced by data-loading and data-error, on Media and its Slide.
  • data-dragging is now data-swiping, on Popup, Backdrop, Viewport and ThumbnailList, like Base UI's drawer. ThumbnailList's state field is swiping.
  • data-popup-open marks the trigger the image will fly back to: the one of the item shown, or the one closeTarget picks. Before, it marked every trigger of the item shown.
  • onValueChange and onOpenChange receive eventDetails as their second argument, like Base UI's callbacks.
  • Removed loadImage, loadLightboxImage and LIGHTBOX_PRELOAD_TIMEOUT. The viewer loads its media itself: before it opens, it is mounted out of sight until the <img> or <video> in Media has loaded (a video, its size), for up to LIGHTBOX_OPEN_TIMEOUT (3 s). The trigger has data-pending meanwhile. The thumbnail and the viewer can show different URLs with no extra code.
  • Viewport and Slide no longer clip. Add overflow-hidden to the viewport in a viewer smaller than the screen.
  • Closed sets are string unions, not enums, named after their part like Base UI's. Write the strings ("loading", "open", "left") instead of LightboxMediaStatus.Loading and the like. LightboxPhase is now LightboxRootPhase, and LightboxActivationDirection is LightboxRootActivationDirection. ThumbnailList's orientation is typed LightboxThumbnailListOrientation.
  • The CSS variable namespaces end in CssVariables, like Base UI's: LightboxPopupCssVariables instead of LightboxPopupCssVars, and the same for Backdrop, Slide, ThumbnailIndicator and Viewport.

New features#

  • onOpenChange can return a Promise, and the viewer waits for it: up to 3 s before it opens, 300 ms before it closes (LIGHTBOX_OPEN_TIMEOUT, LIGHTBOX_CLOSE_TIMEOUT). Use it to get something ready, such as moving a live element into the viewer. eventDetails.cancel() works until the wait ends, after an await too, and a cancelled change never happened.
  • Lightbox.createHandle() links triggers placed outside Root to it, like Base UI's detached triggers, and opens or closes the lightbox from code.
  • Any media. Media shows a video or an embed as well as an image, and reads its loading state from the media's own events. A video pauses when its slide is no longer shown.
  • ThumbnailList and Thumbnail: a strip of thumbnails inside the viewer, a tab list that is a single tab stop, with aria-selected on the item shown. ThumbnailIndicator is one element that glides to the active thumbnail, with its box as CSS variables.
  • Caption: a slide's own text, which moves with it and describes it for screen readers.
  • Animation attributes. Popup, Viewport, Slide, Title, Description, Counter, Caption and ThumbnailIndicator have data-activation-direction (left, right or none), the side the item shown came from. Title and Description with a function child, Counter and Caption have data-starting-style for a frame when the item changes, to animate the new text in. Title takes a function child, like Description.
  • Popup's className, style and render functions receive zoomed, controlsHidden and activationDirection. Viewport is a labelled carousel region.
  • Content marked with Base UI's data-base-ui-swipe-ignore keeps its drags, such as a 3D model that rotates: the viewer doesn't swipe or pull to dismiss from it. "x" or "y" keeps only the drags along that axis.
  • The arrow keys stay with a widget inside the popup that uses them itself, such as a Base UI toolbar, a text field or a video player, instead of also changing the image.
  • Where the image lands. closeTarget on Root picks the trigger the image flies back to and focus returns to: "active" (the item shown, the default), "opener" (the trigger that opened the viewer) or "nearest" (the nearest item with a trigger, for a "+5" tile). scrollToTrigger (on by default) scrolls the page behind the viewer to keep that trigger in view.
  • Crossfade. When the image flies to or from a trigger that shows something else (another item, or a trigger with the new crossfade prop, such as an avatar or a tile with a count), a copy of the trigger flies along and fades, so the image grows out of its look and turns back into it on the way home. Trigger has data-crossfade.
  • Change reasons. eventDetails.reason tells what changed the item ("swipe", "trigger-press", "escape-key" …), and eventDetails.cancel() keeps the current item: a cancelled swipe or pull to dismiss springs back. onOpenChangeComplete is called once the image has landed.
  • A smoother flight. It is computed on screen, so the shape and corners change evenly; a flight cut short starts the next from where the image is; after a pull to dismiss, the flight back starts at the speed of the finger. --lightbox-flight-duration and --lightbox-flight-easing on Popup and Backdrop let transitions go with it, and --lightbox-flight-movement-x, -y and --lightbox-flight-scale on Popup tell where the image flies, so other content can go there too.
  • Gesture variables. Popup gets --lightbox-swipe-movement-x, --lightbox-swipe-progress and --lightbox-swipe-strength during a swipe, --lightbox-zoom-scale and --lightbox-zoom-progress, and, with Backdrop, --lightbox-dismiss-movement-x and -y.
  • Custom transitions. Each Slide has --lightbox-slide-progress, its distance from the centre in slides, and is placed with translate, leaving transform free for a cube, a fade or a coverflow. Viewport's neighbors renders more items on each side (1 by default), and --lightbox-swipe-distance on it sets how far the finger travels for one slide, for slides that sit closer together than the viewport is wide.
  • Changing value while the viewer is open slides to the new item, as Next does.
  • Popup takes focus when it opens, so its keys work at once; pass initialFocus to focus another element.
  • Optional props accept undefined, for apps with exactOptionalPropertyTypes.

From Base UI 1.9.0#

  • Every component exports its data-* attributes and CSS variables as namespaces, such as DialogPopupDataAttributes and DialogPopupCssVariables.
  • Menu can filter its items with Menu.FilterProvider and Menu.Input (preview).
  • data-base-ui-swipe-ignore takes "x" or "y" in Drawer too.

v0.1.0#

October 8, 2026. The first release.

New components#

  • Gallery (now Lightbox): thumbnails that open a full-screen image viewer. The image flies out of its thumbnail and back, and the viewer handles swipe, pinch to zoom, double tap, pan, pull to dismiss and the keyboard. Comes with toolbar parts for navigation, zoom, download and delete.
  • Clipboard: a button that copies a value and shows that it was copied. Root, Trigger and Indicator expose data-copied.

Core components#

  • Dialogs, menus, popovers, selects, tabs and the other core components, each under its own path, such as @logic-ui/react/dialog.
  • @logic-ui/react/utils exports the hooks the primitives are built on, such as useControlled, useStableCallback and useTimeout.

Requirements#

  • React 19.
  • Built on @base-ui/react 1.8.0.