WebberUI

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.

How to install Pro components →See the plans →

Loading preview…
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.

40
64
<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

PropTypeDefaultDescription
childrenReactNodePut 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) => voidReports pan changes (drag / momentum / recenter / keyboard, with coordinates rounded)
momentumbooleantrueWhether to glide with momentum after release
showGridbooleantrueWhether to show the dot-grid background that pans along
gridSizenumber40Spacing of the dot-grid background (px)
showRecenterbooleantrueWhether to show the built-in "back to center" button
keyboardStepnumber64Distance each arrow-key press pans once focused (px)
classNamestringExtra styles (use h-* to override the canvas height)

CanvasItem

PropTypeDefaultDescription
xnumberWorld-space X (px), with the canvas center as origin
ynumberWorld-space Y (px), with the canvas center as origin
childrenReactNodeCard content
classNamestringExtra styles

How it works

  • Dragging, touch, and momentum gliding are handled by Motion's built-in drag with 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 uses currentColor and switches with light and dark mode automatically.
  • offset / defaultOffset support controlled and uncontrolled modes: pass offset and external state drives the pan position; omit it and the component manages it internally, reporting through onOffsetChange.
  • 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 descriptive aria-label.
  • Once focused, arrow keys pan and Home returns 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, CanvasItem disables pointer events so links inside are not triggered by accident and text is not selected.

On this page