Infinite Drag Canvas
An infinitely pannable portfolio canvas that scatters cards across a boundless space, with drag (including momentum), keyboard panning, and one-click recentering.
This is a WebberUI Pro component
Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.
npx shadcn@latest add "https://webberui.com/r/infinite-drag-canvas.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<InfiniteDragCanvas />
Installation
npx shadcn@latest add "https://webberui.com/r/infinite-drag-canvas.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/infinite-drag-canvas.
Usage
import {
InfiniteDragCanvas,
CanvasItem,
} from "@/components/ui/infinite-drag-canvas";
<InfiniteDragCanvas className="h-[400px]">
<CanvasItem x={-120} y={-40}>
<div className="rounded-xl border bg-white p-4 shadow">Aurora</div>
</CanvasItem>
<CanvasItem x={140} y={80}>
<div className="rounded-xl border bg-white p-4 shadow">Drift</div>
</CanvasItem>
</InfiniteDragCanvas>;The center of the canvas is the world-space origin (0, 0); a CanvasItem aligns its own center to the x / y you give it and pans along with the canvas.
Props
InfiniteDragCanvas
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Put CanvasItem children here |
offset | { x: number; y: number } | — | Controlled pan position, driven by external state |
defaultOffset | { x: number; y: number } | { x: 0, y: 0 } | Initial pan position in uncontrolled mode |
onOffsetChange | (offset) => void | — | Reports pan changes (drag / momentum / recenter / keyboard, with coordinates rounded) |
momentum | boolean | true | Whether to glide with momentum after release |
showGrid | boolean | true | Whether to show the dot-grid background that pans along |
gridSize | number | 40 | Spacing of the dot-grid background (px) |
showRecenter | boolean | true | Whether to show the built-in "back to center" button |
keyboardStep | number | 64 | Distance each arrow-key press pans once focused (px) |
className | string | — | Extra styles (use h-* to override the canvas height) |
CanvasItem
| Prop | Type | Default | Description |
|---|---|---|---|
x | number | — | World-space X (px), with the canvas center as origin |
y | number | — | World-space Y (px), with the canvas center as origin |
children | ReactNode | — | Card content |
className | string | — | Extra styles |
How it works
- Dragging, touch, and momentum gliding are handled by Motion's built-in
dragwith no drag constraints, which is what lets the space extend infinitely. - The dot-grid background pans modulo
gridSize, so covering the viewport plus one cell is enough to create the illusion of infinite repetition; the dot color usescurrentColorand switches with light and dark mode automatically. offset/defaultOffsetsupport controlled and uncontrolled modes: passoffsetand external state drives the pan position; omit it and the component manages it internally, reporting throughonOffsetChange.- The built-in "back to center" button glides smoothly back to
defaultOffset(or the origin) with a spring.
Accessibility
- The canvas is
role="group"and focusable (tabIndex=0), with a descriptivearia-label. - Once focused, arrow keys pan and
Homereturns to center; the arrow keys prevent the page from scrolling. - When the user has "reduce motion" enabled at the system level, momentum gliding is turned off and recentering and keyboard panning jump instantly (with no change to the DOM structure).
- While a drag is in progress,
CanvasItemdisables pointer events so links inside are not triggered by accident and text is not selected.
Spotlight Card
A cursor spotlight card — a border glow and a fainter background bloom track the cursor together and fade out on leave, with each card in a row staying independent.
Flip Reveal Card
A 3D flip card that rotates around the X or Y axis to reveal its front and back, supporting hover, click, and fully controlled use.