A magnifier for product and detail photos. In lens mode a round or rounded loupe follows the pointer and shows the picture under it at the chosen zoom; in side mode a pane opens beside the image and a box marks the area it shows, falling back to the lens when there is no room. The large file loads only on the first zoom, under the regular picture with a small spinner, then fades in. Scrolling changes the zoom while magnified. On touch, press and hold opens the lens above the finger so it is never covered, while a quick swipe still scrolls the page. The frame is a button: Enter magnifies, the arrow keys move, plus and minus zoom and Escape closes. Position and zoom reach the DOM as CSS variables, so moving costs no renders.
The import and the props worth knowing about, in one place.
import { ImageLoupe } from "@/components/beste/component/image-loupe";
<ImageLoupe
src="https://images.unsplash.com/photo-1783676167814-13057079dd43?q=80&w=1200&auto=format&fit=crop"
zoomSrc="https://images.unsplash.com/photo-1783676167814-13057079dd43?q=80&w=2400&auto=format&fit=crop"
alt="Van Gogh, a wheat field with cypresses under swirling clouds"
aspectRatio="4 / 3"
/>
<ImageLoupe
src={product.image}
zoomSrc={product.largeImage} // fetched on the first zoom
alt={product.name}
mode="side" // "lens" (default) | "side"
side="right" // pane on the right; the lens takes over without room
defaultZoom={3}
minZoom={1.5}
maxZoom={6}
wheelZoom={false} // keep the wheel for scrolling
lensShape="rounded" // "circle" (default) | "rounded"
lensSize={220}
tone="outline" // "muted" (default) | "outline" | "ghost"
size="lg" // "sm" | "default" | "lg"
onZoomChange={(zoom) => console.log(zoom)}
/>Every one of these is in the component already. They are listed because a props table cannot mention a gesture, so nothing else on this page can tell you they exist.
| Hover | The lens follows the pointer; the wheel changes the zoom. |
|---|---|
| Press and hold | On touch, opens the lens above the finger. A quick swipe still scrolls. |
| Enter | Magnifies from the keyboard; press again or Escape to close. |
| Arrows | Move the magnified area, Shift for fine steps. |
| + / - | Zoom in or out while magnified. |
Read from the component's own type, so this cannot drift from what it accepts.
| Prop | Type | Default | Description |
|---|---|---|---|
src* | string | — | |
alt* | string | — | Required: the picture is the content, so it must be described. |
zoomSrc | string | — | Larger file for the magnified view, fetched the first time someone zooms. Defaults to `src`. |
mode | "lens" | "side" | — | `lens` magnifies under the pointer; `side` opens a pane beside the image and marks the area it shows. |
side | "right" | "left" | — | Which side the pane opens on in `side` mode. Falls back to the lens when there is no room. |
zoom | number | — | |
defaultZoom | number | — | |
onZoomChange | (zoom: number) => void | — | |
minZoom | number | — | |
maxZoom | number | — | |
wheelZoom | boolean | — | Scrolling over the image changes the zoom while it is magnified. |
lensShape | "circle" | "rounded" | — | |
lensSize | number | — | Lens diameter in px. Defaults by `size`. |
aspectRatio | string | — | CSS aspect ratio of the frame. The picture covers it. |
hint | string | — | Hint in the corner. Set to an empty string to hide it. |
disabled | boolean | — | |
size | "sm" | "default" | "lg" | — | |
tone | "muted" | "outline" | "ghost" | — | |
className | string | — |