Skip to content

Cursor Lens Magnifier

A glass lens that follows the pointer and magnifies the live DOM beneath it.

Freeglasseditorialminimal
Category
Components
Added
2026-09-20
Deps
none
Updated
2026-09-20
Preview
REGISTRY / 2026viberdy.dev0.1 — 9.9 mmTOL ±0.05

Props

2
104
Invert inside lens
No runtime dependencies
"use client";

import { useRef, useState, type ReactNode } from "react";

export function CursorLensMagnifier({
  children,
  zoom = 2,
  size = 104,
  invert = false,
}: {
  children: ReactNode;
  zoom?: number;
  size?: number;
  /** Invert everything inside the glass, so the lens reads as a filter. */
  invert?: boolean;
}) {
  const boxRef = useRef<HTMLDivElement>(null);
  const [pos, setPos] = useState<{ x: number; y: number } | null>(null);
  const [box, setBox] = useState({ w: 0, h: 0 });

  function handleMove(e: React.MouseEvent<HTMLDivElement>) {
    const rect = boxRef.current?.getBoundingClientRect();
    if (!rect) return;
    setBox({ w: rect.width, h: rect.height });
    setPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
  }

  const lensTransform =
    "translate(" +
    (size / 2 - (pos?.x ?? 0) * zoom) +
    "px, " +
    (size / 2 - (pos?.y ?? 0) * zoom) +
    "px) scale(" +
    zoom +
    ")";

  return (
    <div
      ref={boxRef}
      onMouseMove={handleMove}
      onMouseLeave={() => setPos(null)}
      className="relative overflow-hidden"
      style={{ cursor: pos ? "none" : "default" }}
    >
      {children}

      {pos && (
        <div
          aria-hidden
          className="pointer-events-none absolute overflow-hidden rounded-full border border-white/30 shadow-lg"
          style={{
            width: size,
            height: size,
            left: pos.x - size / 2,
            top: pos.y - size / 2,
            filter: invert ? "invert(1)" : undefined,
          }}
        >
          {/*
           * A SECOND render of the same children, scaled about the pointer.
           * Offsetting by (size/2 - pointer * zoom) keeps the magnified point
           * pinned under the cursor, so the lens reads as glass over the
           * content rather than a separate floating panel.
           */}
          <div
            className="absolute left-0 top-0 origin-top-left"
            style={{ width: box.w, height: box.h, transform: lensTransform }}
          >
            {children}
          </div>
          <div className="absolute inset-0 rounded-full shadow-[0_0_0_1px_rgba(255,255,255,0.12)_inset]" />
        </div>
      )}
    </div>
  );
}
Notes

About this component

The usual way to build a magnifier is to snapshot the target to a canvas and scale the bitmap, which is fast but goes stale the moment anything underneath changes and turns crisp text into blur above about 2x. This one duplicates the DOM instead: the lens contains a second render of the same children, so the magnified layer is live, selectable, sharp at any zoom, and can never disagree with the original. The whole trick is the offset — translating by half the lens size minus the pointer position times the zoom, before scaling from the top-left corner, is what keeps the point under the cursor actually under the cursor, which is the difference between glass and a floating thumbnail. The cost is real and worth stating plainly: everything inside renders twice, so this belongs over a bounded region such as a specimen of type, a map, or a detailed diagram, and not over a section carrying its own animations or heavy subtrees. Since the effect is pointer-only and the duplicate is hidden from assistive technology, nothing here should be the sole route to information.

Curator’s note

Built for viberdy 2.0. The DOM-duplication approach was chosen over a canvas snapshot specifically so the magnified layer can never go stale.

Related

Pairs well with

Matched on shared tags and category — the entries most likely to be used alongside this one.

Featured
New

A drafting-tool crosshair with live X/Y coordinates that trails the pointer.

cursorcrosshairreadout
Featured
New

A contextual chip that trails the pointer and names whatever it is over.

cursorlabelspring
Featured
New

Snapping Cursor

components

A cursor dot that latches onto interactive elements and takes their exact shape.

cursormagneticsnap