WebberUI

Waypoint Stage

Place each passage of content by coordinate on an oversized canvas, and let scroll drive a camera that pans, zooms, and rotates along a waypoint path for a Prezi-style spatial narrative.

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/waypoint-stage.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.

1
0.25
0.72
<WaypointStage />

Installation

npx shadcn@latest add "https://webberui.com/r/waypoint-stage.json?t=<install token>"

Or, once registries are configured in components.json, install it as @webberui/waypoint-stage.

Usage

Wrap a number of Waypoints in a WaypointStage; each waypoint declares its coordinates on the canvas with x / y, and its zoom / rotate decide the composition the camera arrives at. Scrolling drives the camera to fly to them in order.

import { Waypoint, WaypointStage } from "@/components/ui/waypoint-stage";

<WaypointStage>
  <Waypoint x={0} y={0} label="Opening">
    <h2 className="text-4xl font-bold">Welcome</h2>
  </Waypoint>
  <Waypoint x={800} y={-320} zoom={1.4} rotate={-8} label="Key point">
    <p className="max-w-sm">Fly here to zoom in on a tilted composition.</p>
  </Waypoint>
  <Waypoint x={-360} y={420} zoom={1.2} rotate={6} label="Closing">
    <p>Then turn to somewhere else.</p>
  </Waypoint>
</WaypointStage>

By default the window is the scroll container and the stage is pinned at h-screen. If it sits inside a nested overflow-y-auto container, pass that container's ref to container and override the stage height with stageClassName to the container's visible height (such as h-[300px]).

Props

WaypointStage

PropTypeDefaultDescription
childrenReactNodePut Waypoint children here
pacenumber1Scroll length of each leg (in units of the pinned stage height)
dwellnumber0.25Proportion of the leg spent lingering after arriving at each waypoint (0–0.49)
zoomOutnumber0.72How far the camera pulls back between two waypoints (1 means no pull-back)
smoothbooleantrueWhether to smooth the camera work with a spring
showPathbooleantrueWhether to draw the dashed path connecting the waypoints
showNavbooleantrueWhether to show the clickable navigation dots that jump between waypoints
gridbooleantrueWhether to lay down the faint dot-grid texture
containerRefObject<HTMLElement | null>Ref of the nested scroll container; defaults to the window as scroll container
stageClassNamestring"h-screen"Height of the pinned stage container
classNamestringThe outer scroll track container

Waypoint

PropTypeDefaultDescription
xnumberX coordinate of the waypoint's center on the canvas (px)
ynumberY coordinate of the waypoint's center on the canvas (px)
zoomnumber1Zoom when the camera arrives; >1 zooms in, <1 zooms out
rotatenumber0Rotation angle of this waypoint on the canvas (degrees); it is straightened on arrival
labelstringName used by the navigation dots and assistive technology
childrenReactNodeWaypoint content

How it works

  • The camera is implemented in pure CSS transform (translate + scale + rotate, with the origin anchored to the viewport center) — no WebGL needed. Content is centered on its own coordinates, so the camera arrives with it straightened and centered.
  • The transition uses a "pull back first, then push in" keyframe; zoomOut controls how far it pulls back midway, and dwell controls how long it lingers after arriving.
  • The track height is computed automatically from the number of waypoints and pace: more waypoints and a larger pace mean a longer scroll distance and more leisurely camera work.
  • For nested scrolling, always pass both container and a matching stageClassName, so useScroll correctly treats that container as its scroll source.

Accessibility

  • When the user has "reduce motion" enabled at the system level, the camera work is disabled entirely and the waypoints are instead laid out in order as static vertical passages, avoiding the discomfort that sweeping zoom and rotation can cause.
  • The navigation dots are focusable buttons carrying aria-label and aria-current, operable with the keyboard and jumping smoothly to their waypoint.
  • The stage also carries an sr-only ordered list for assistive technology to announce, decoupled from the animated visuals.
  • The ResizeObserver and the springs are all cleaned up on unmount by Motion and the effects.

On this page