Infinite Drag Canvas
An endlessly pannable board that recycles a fixed set of tiles via modulo wrapping.
DOM + CSS0 deps1 file · 3 KB TSX
Props
"use client";
import { useRef, useState, type ReactNode } from "react";
export function InfiniteDragCanvas({
renderTile,
tile = 92,
gap = 8,
cols = 6,
rows = 5,
showCoords = true,
}: {
/** Given a logical cell coordinate, return its content. */
renderTile: (col: number, row: number) => ReactNode;
tile?: number;
gap?: number;
cols?: number;
rows?: number;
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}Details
About this pattern
An infinite canvas sounds like it needs infinite elements, and the version most people write does exactly that: append tiles as the user pans until the page stops responding. The cheap alternative is to notice that you only ever see a screen's worth. Keep the pan offset unbounded, but translate a fixed grid by that offset wrapped into a single cell pitch, and the tiles recycle continuously — the board pans forever while the node count never changes. Passing the current logical cell coordinate to the tile renderer is what keeps content stable per position rather than shuffling as nodes are reused. Two smaller traps are worth naming. JavaScript's modulo returns a negative result for negative operands, so a single `%` leaves a visible gap the moment you pan up or left; the double-modulo form is required. And the gesture uses pointer events with capture, which keeps the drag alive when the pointer leaves the element and provides touch support with no separate handlers — `touch-none` then stops the browser claiming the gesture for page scrolling.
Curator’s note
Built for viberdy 2.1. Modulo wrapping with a recycled node set — the naive append-as-you-pan version dies after a few screens.
