Skip to content
Motion for React · Next.js 16

A working reference of twelve interaction patterns — scroll timelines, drag physics, spring tuning, pointer fields. Each one animates transform, opacity, filter and clip-path only, adapts itself to touch input, and stands down when you ask for reduced motion.

Patterns
12
Animated props
4
Target fps
60
01 / Velocity marquee

Track speed and direction derive from scroll velocity. Scroll upward and the whole field reverses.

scroll-linked+drag physics+spring tuning+layout projection+pointer fields+clip reveals+
02Scroll-driven sequence

One timeline, many readers

Four chapters and five frames, all mounted at once, all driven by a single spring-smoothed progress value.

01

Bind the timeline, not the handler

A single useScroll gives one progress value for the whole section. Every element below reads from it through useTransform, so adding the tenth animated layer costs about the same as the first.

02

Keep every panel mounted

Panels crossfade on opacity instead of mounting and unmounting. Nothing re-decodes, nothing re-measures, and scroll position never jumps as content leaves the tree.

03

Smooth the input, not the output

Wheel and trackpad scroll arrives as a step function. Wrapping progress in useSpring once, at the source, smooths every derived value downstream for the price of a single spring.

04

Stay on the compositor

Opacity, scale, translate and clip-path are the only properties touched here. No width, no top, no margin, nothing that would force layout on a scroll frame.

03Drag physics

Throw it and let the spring decide

Pointer offset multiplied by release velocity decides whether a card commits or snaps home. Rotation and the two stamps all read from the same x motion value, so the card reacts continuously to the gesture without a single React re-render.

Deck empty

In deck
6 / 6
Threshold
9000 px·px/s
Release
spring 300 / 38
Rotation
x → ±14°

Drag the top card in any direction. A short, slow drag returns it to centre; a decisive flick sends it out along the vector you let go on. The arrow buttons drive the same code path, so pointer, touch and keyboard all get identical physics.

06Operating rules

The constraints that keep it at sixty

Hover or tap a row. The lean is a variant on the list item, not an imperative animate() call against an element id — so it cannot target the wrong row, and it resets itself.

0 / 11 acknowledged

07Pointer field

A field that answers the cursor

Gridlines are one element. The spotlight is a mask position built from two motion values, so pointer movement never re-renders React. The ripple is a CSS animation with a per-cell delay, so a tap costs one render of plain divs.

Input
coarse / touch
Grid
measuring
Cells
—
Hooks per cell
0
Renders per move
n/a

The previous build of this effect rendered 1,410 cells and called two hooks inside each one. Same idea, a few hundred plain nodes, and nothing allocated on pointer move.

08Pointer trail

Leave something behind

Position and lifetime are written straight to element style from the pointer handler. A mouse draws a trail; a finger drops a single frame where it lands.

motionreactnextspringscrolldrag
motionreactnextspringscrolldrag
motionreactnextspringscrolldrag
motionreactnextspringscrolldrag

The heart's memory
magnifies the good

09Spring tuning

Two vocabularies, one spring

Duration and bounce, or stiffness, damping and mass. Changing any control replays the preview immediately — springs can be interrupted mid-flight and still resolve, which is why every gesture on this site uses one.

Preview
Output
const transition = {
  type: "spring",
  duration: 0.80,
  bounce: 0.25,
}
Duration and bounce
Duration and bounce
0.80s
0.25
Stiffness, damping, mass
Stiffness, damping, mass
180
18
1.0

Editing a control switches to the mode it belongs to, so the panel can never show values that are not driving the preview.

10View transitions

Swapping a whole panel without a flash

AnimatePresence with mode="wait" holds the incoming panel until the outgoing one has finished leaving, which is what stops the two overlapping mid-swap.

01 / 3

Reveal by clipping

The outgoing panel closes from the bottom while the incoming one opens from the top. clip-path is composited, so a full-bleed wipe costs the same as a small one.

exit:  { clipPath: "inset(100% 0 0 0)" }
enter: { clipPath: "inset(0 0 100% 0)" }
11Get in touch

Tell me about the thing you're building

Validated with one shared module on both sides, rate-limited per address, and credentials read only from server-side environment variables.

  • A reply within about 12 hours
  • Happy to sign an NDA first
  • Fixed scope or ongoing retainer

Or skip the form

tanviranjum010@gmail.com

Chattogram, Bangladesh · UTC+6. Available for new work.

Service
Budget
0 / 5000