Skip to documentation
UI

Image

Image is an enhanced image component with a self-contained zoom-and-pan preview viewer, custom actions, and various display styles. Preview is powered by a Base UI dialog with FLIP open/close transitions, deep zoom, and gallery navigation — it no longer wraps Ant Design's Image.

import { Image } from '@lobehub/ui';
Data Display

Default

Loading preview

Wrap multiple Image components in Image.PreviewGroup to preview them together, with arrow buttons, a counter, and / keyboard navigation.

Loading preview

Fallback

Loading preview

Custom Actions

Loading preview

Dual Source Preview

preview.src lets the thumbnail stay lightweight while the viewer opens instantly against it, then swaps in a higher-resolution image in place once it finishes loading — no layout jump.

Loading preview

Deep Zoom

Ctrl/Cmd + wheel, double-click, or the toolbar's zoom buttons zoom in past the fitted size for pixel-level detail; drag to pan once zoomed. preview.maxScale raises the zoom ceiling for large source images.

Loading preview

Default Zoom

preview.defaultZoom picks the scale the viewer opens at. The default, auto, opens an image at its natural size when it is at most preview.autoZoomThreshold times the fitted size, and falls back to the fitted size beyond that. This keeps screenshots and text-bearing images legible on open — fitting a 2560px-wide screenshot into a 1400px viewport halves its text — while a photo many times the viewport still opens whole rather than showing a corner of itself.

Opening above the fitted size also means the image overflows the viewport, so it is immediately pannable. Scrolling down zooms back out to the fitted size before the next scroll dismisses the viewer.

Loading preview

Toolbar Addon

preview.toolbarAddon appends custom controls to the end of the preview toolbar — here, a button that opens the original file in a new tab.

Loading preview

Interaction Model

The preview viewer opens with a FLIP transition from the clicked thumbnail's on-screen position and returns to it on close (or fades, if the thumbnail is no longer available or prefers-reduced-motion is set).

Zoom & pan

  • Scrolling up always zooms in, at any scale. Scrolling down zooms out until the fitted size, which is the zoom floor.
  • Ctrl/Cmd + wheel (trackpad pinch) zooms from any state, in either direction.
  • The toolbar zoom buttons zoom around the viewport center; the wheel zooms around the cursor.
  • The toolbar's expand/shrink button toggles between the fitted size and 100%, and is disabled for an image small enough that the two coincide. The toolbar percentage beside it is a readout only.
  • 0 resets to the opening state, which also clears rotation and flips.
  • Double-click toggles between fitted and a zoomed-in target scale.
  • Dragging pans whenever the image overflows the viewport, clamped so it can't be dragged past its edges. An image that fits entirely stays centered — there is nothing off-screen to reveal.

Closing

  • Scrolling down while already at the fitted size closes the viewer once the accumulated scroll passes a threshold — the dismiss gesture is the continuation of zooming out. A large zoom-out overshoot re-arms after ~300ms of wheel idle rather than closing immediately, and a rotated or flipped image is never dismissed this way.
  • Clicking the backdrop, the close button, or pressing Esc while the viewer is untouched closes immediately. "Untouched" means still at the opening scale with no rotation or flip — so an image that opened at 100% under defaultZoom still closes on the first Esc or click.
  • Clicking the image itself closes after a short delay unless a second click arrives in time (in which case it double-click-zooms instead of closing).
  • Esc is layered: once zoomed away from the opening scale, or rotated or flipped, the first Esc resets; only a second Esc closes.

Gallery

  • Inside Image.PreviewGroup, the arrow buttons, on-screen counter, and / keys navigate between images; rotation and flips clear on every switch, and each image lands at its own defaultZoom scale.
  • Outside a gallery (a single previewable Image), / are left alone for the browser/page to handle.

Breaking Changes

The preview viewer was rewritten from scratch on top of a Base UI dialog and no longer wraps rc-image / Ant Design's Image preview. The following antd/rc-image passthrough is no longer supported:

  • PreviewGroup's items prop — build the group from Image children instead.
  • minScale, onVisibleChange, and other rc-image ImagePreviewType render/lifecycle props.
  • Any other undocumented antd Image/rc-image preview props that used to pass through via ...rest.

preview now only accepts boolean | { src?, defaultZoom?, autoZoomThreshold?, maxScale?, onOpenChange?, toolbarAddon? } (see below).

In apps without an app-level ToastHost (see @lobehub/ui/base-ui), a copy/download toast fired after the viewer has already closed is dropped rather than shown standalone. This is an accepted limitation — mount a ToastHost at the app root to avoid it.

APIs

Image

Image properties
actions
null | string | number | bigint | false | true | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<AwaitedReactNode>
alwaysShowActions
boolean
classNames
{ image?: string; wrapper?: string; }
height
string | number
isLoading
boolean
maxHeight
string | number

Defaults to "100%".

maxWidth
string | number

Defaults to "100%".

minHeight
string | number
minWidth
string | number
objectFit
'contain' | 'cover'

Defaults to "cover".

preview
false | true | ImagePreviewOptions
ref
null | ((instance: HTMLImageElement | null) => void | (() => VoidOrUndefinedOnly) | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) & ((instance: HTMLDivElement | null) => void | (() => VoidOrUndefinedOnly) | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | ((instance: HTMLImageElement | null) => void | (() => VoidOrUndefinedOnly) | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) & RefObject<HTMLDivElement | null> | RefObject<HTMLImageElement | null> & ((instance: HTMLDivElement | null) => void | (() => VoidOrUndefinedOnly) | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | RefObject<HTMLImageElement | null> & RefObject<HTMLDivElement | null>

Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref).

size
string | number
styles
{ image?: CSSProperties; wrapper?: CSSProperties; }
variant
'filled' | 'borderless' | 'outlined'

Defaults to "filled".

width
string | number

Also accepts all native HTML and ARIA attributes.

Additional props are spread onto the underlying <img> element.

PreviewGroup

PropertyDescriptionTypeDefault
enableWhether to enable the preview groupbooleantrue
previewPreview options for the whole groupboolean | ImagePreviewOptions-

Each Image resolves its own preview against the group's: an option object is merged over the group's (per-image keys win), and a per-image preview of any kind takes precedence over the group's. preview={false} on the group is therefore a default for children that don't set their own — a child passing preview={false} opts out of an enabled group, and a child passing an options object opts back into a disabled one.

ImagePreviewOptions

PropertyDescriptionTypeDefault
srcHigher-resolution source swapped in after the viewer opens with srcstring-
defaultZoomScale the viewer opens at. fit always shows the whole frame, actual always opens at 100%, and auto opens at 100% only when the image is within autoZoomThreshold of the fitted size'auto' | 'actual' | 'fit''auto'
autoZoomThresholdHow many times the fitted size an image may be and still open at 100% under defaultZoom: 'auto'. Ignored by the other policiesnumber2
maxScaleMaximum zoom scale relative to the fitted size. Raised automatically when defaultZoom resolves above it, so the opening scale is always reachablenumber8
onOpenChangeCalled when the preview viewer opens or closes(open: boolean) => void-
toolbarAddonAdditional content appended to the preview toolbarReactNode-