Lens

A React image zoom component - a magnifier that follows the pointer over any element. For product galleries.

Lens playground

AaGeist - specimen sheetHold the glass over the small print
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789
Set close and small, this paragraph exists to be read through glass. Every glyph in the magnified disc is live vector type drawn at the enlarged size, not a stretched screenshot of this one, which is why the edges stay razor sharp at six times magnification. The lens re-clones this column whenever it changes, keeps your text in the accessibility tree, hides only the copy, and hands the glass to the keyboard: focus the stage, steer with the arrow keys, leave with Escape. Chromatic fringing gathers at the rim and strengthens with travel speed, the way thick glass smears a moving image, and settles back to clean edges at rest.
. , : ; … ! ? ‘ ’ “ ” ( ) [ ] { } / \ | - – — @ & # % ‰ + × ÷ = ≠ ± ~ ° √ ∞ µ π Ω
Sweep the glass over the small print; focus the sheet and steer with the arrow keys.

Install Lens

1. New project? One command installs the package and wires everything - see Installation:

bash
pnpm dlx zyncat-ui init

2. Import the component - it loads its own stylesheet:

tsx
import { Lens } from '@zyncat/ui/lens';

Usage

tsx
import { Lens } from '@zyncat/ui/lens';
 
<Lens magnification={2.6} radius={132}>
<TypeSpecimen />
</Lens>

Compose Lens in your application. No Tailwind or external styling library is required; every value resolves from Zyncat UI's token vocabulary.

Lens props

Lens

PropTypeDefaultDescription
...htmlAttributesHTMLAttributes—Standard <div> attributes (aria-*, data-*, title, ...) forwarded to the stage.
children*ReactNode—The specimen the glass magnifies. It stays in the accessibility tree; the magnified copy is aria-hidden.
magnificationnumber2.6How much the glass magnifies, clamped to 1.2-6.
radiusnumber132Glass radius in pixels, clamped to 60-260.
chromaticbooleantrueChromatic fringing at the rim, which strengthens with travel speed.
classNamestring—Extra class(es) merged onto the stage.
styleLensStyle—Inline styles merged onto the stage.

LensStyle

PropTypeDefaultDescription
--lens-inkstring | number—`--lens-ink` - The ink of the glass's edge, vignette and shadow. Default: `var(--text-strong)`.
--lens-surfacestring | number—`--lens-surface` - The surface behind the glass. Default: `var(--bg-surface)`.
--lens-fringe-warmstring | number—`--lens-fringe-warm` - The chromatic fringe on the warm edge of the glass. Default: `oklch(0.72 0.16 12 / 0.3)`.
--lens-fringe-coolstring | number—`--lens-fringe-cool` - The chromatic fringe on the cool edge of the glass. Default: `oklch(0.7 0.15 265 / 0.34)`.

Frequently asked questions

Wrap it: <Lens magnification={2.6} radius={132}><img src="/product.jpg" /></Lens>. The glass follows the pointer with no lag and magnifies whatever sits under it, so there are no mouse events to wire up. magnification is clamped to 1.2-6 and radius, the glass radius in pixels, to 60-260.

These results are still owned by the jQuery era - elevateZoom, Magic Zoom Plus, YITH WooCommerce Zoom Magnifier - with react-image-magnify, react-medium-image-zoom and js-image-zoom on the React side. Lens differs in what it magnifies: rather than pairing a thumbnail with a separate high-resolution file, it re-renders the DOM you already have, which is why it works over text, SVG and charts as well as photos.

Any DOM. Lens clones its children into the glass and scales that clone about the pointer, so a div, a table, live text, an SVG chart or a map magnifies exactly the way an image does. Because the copy is real DOM and not a bitmap, magnified type is re-rasterised as vector type at the larger size, and a MutationObserver re-clones so content that changes stays live under the glass.

Text, SVG and CSS re-rasterise at the magnified size, so they stay sharp at any magnification. A photo is bounded by the file you hand it, as with any image zoom: serve a high-resolution source and constrain it with CSS width, and the browser has the full-resolution pixels to draw from when the glass magnifies. There is no second image URL prop - Lens magnifies the one child you pass.

A real effect, not a magnifying glass PNG, a GIF or a PowerPoint magnifier animation. The glass is drawn in the browser - rim, vignette, a specular highlight that trails the direction of travel, and chromatic fringing that strengthens with speed until chromatic={false} turns it off - over live DOM, so it magnifies what your app renders at runtime rather than a picture of it.

Yes. @zyncat/ui/lens ships compiled ESM with its 'use client' directive intact, so it works in the Next.js App Router with no transpilePackages config and no runtime dependencies. Your children are server-rendered and stay in the accessibility tree while the magnified copy is aria-hidden; arrow keys steer the glass and Escape dismisses it once the stage has focus, and under prefers-reduced-motion it settles into place instead of easing.

Set in Geist & Newsreader — animated by the house engineZyncat UI · Rev 0.11 · MIT · Built by Tabsir Ahammed · Source on GitHub