Power-User Systems

Infinite Canvas

A pannable, zoomable infinite board holding real DOM cards — one world element under a single translate-and-scale, so focus survives and nothing ever remounts when the camera moves. Drag the background to pan with inertia; the wheel zooms to the cursor by holding the world point under it fixed; cards drag independently and glide onto the grid on release; the corner minimap mirrors the extent and jumps the camera where you click. Camera state lives in one ref written by handlers and the loop, and every consumer — world transform, backdrop plane, minimap — derives from it in a single write pass per frame, so React never renders a camera frame. Arrow keys pan and plus and minus zoom when the board holds focus.

Install

bun add clsx tailwind-merge

Then copy 3 files into your project:

  • components/registry/infinite-canvas/infinite-canvas.tsx
  • hooks/use-animation-loop.ts
  • lib/utils.ts

Props

PropTypeDefaultDescription
frictionnumber0.92Glide decay after a thrown pan — low stops dead, high coasts.
zoomSpeednumber1Wheel-to-zoom gearing.
minZoomnumber0.25How far out the camera may pull.
maxZoomnumber3How far in the camera may push.
gridSnapbooleantrueDropped cards glide onto the nearest grid point.
gridSizenumber24World-space grid pitch — the snap target and the backdrop's cell size both.
minimapbooleantrueCorner map of the extent; click it to jump the camera.
backdrop"dots" | "lines" | "none""dots"The plane texture that moves with the camera — it is what makes the pan legible.

What It Demonstrates

Drag
Moving an element by grabbing it, often with momentum when released.
Momentum
Motion that carries velocity, especially after a drag or interruption.
Velocity
How fast and in which direction an element is moving. A spring carries it into the next animation when interrupted, so a flicked element keeps its speed.
Hardware acceleration
Animating transform and opacity lets the GPU keep motion smooth.

Related Components

Appears In