Cursor Lens Magnifier
A glass lens that follows the pointer and magnifies the live DOM beneath it.
- Category
- Components
- Added
- 2026-09-20
- Deps
- none
- Updated
- 2026-09-20
Props
"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>
);
}
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.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Crosshair Cursor Readout
componentsA drafting-tool crosshair with live X/Y coordinates that trails the pointer.
Cursor Label Follower
componentsA contextual chip that trails the pointer and names whatever it is over.
Snapping Cursor
componentsA cursor dot that latches onto interactive elements and takes their exact shape.