Motion+

Bobble hover

A 3x3 grid of tiles that bobble with a spring when hovered, with the bounce scaled to how fast the pointer was moving, using Motion for React.

React
Tutorial time
5 min
Difficulty

Tutorial

Introduction

The "Bobble hover" example builds a 3x3 grid of tiles that spring to life as your cursor sweeps across them. The trick is that each tile bobbles in proportion to how fast the pointer was moving when it arrived: drift slowly and you get a gentle nudge, flick across quickly and the tile pops, stretches and tilts before settling back to rest. It's a tactile, physical way to make a grid of images feel alive.

This example uses a handful of Motion APIs to read the pointer and drive the bounce:

  • Motion+ usePointerPosition – tracks the global pointer as a pair of motion values.
  • useVelocity – derives how fast the pointer is moving on each axis.
  • useTransform – combines the two axes into a single speed value.
  • useMotionValue – holds each tile's x, y, rotate and scale.
  • animate – springs each value back to rest with an injected velocity, creating the bobble.
  • motion – renders the tiles and exposes the onHoverStart gesture.

Get started

Let's start with the static layout: a centred 3x3 grid of tiles, each filled with a colour from a vivid palette. Each tile is a plain <div> for now; we'll upgrade it to a motion.div once we start animating.

"use client"

const GRID_SIZE = 3
const TILES = GRID_SIZE * GRID_SIZE

// A vivid spread so neighbouring tiles always differ, and bright enough that
// the dark index stays legible on each.
const HUES = [
	"#ffe14d", "#8df0cc", "#ff5da8",
	"#a98bff", "#ff7a59", "#c6f24e",
	"#5fd8f0", "#ffae4d", "#88a8ff",
]

function Tile({ index, hue }) {
	return (
		<div className="tile" style={{ background: hue }}>
			<span className="tile-index">{String(index + 1).padStart(2, "0")}</span>
		</div>
	)
}

export default function BobbleHover() {
	return (
		<>
			<div className="stage">
				<div className="grid">
					{Array.from({ length: TILES }, (_, i) => (
						<Tile key={i} index={i} hue={HUES[i % HUES.length]} />
					))}
				</div>
			</div>
			<Stylesheet />
		</>
	)
}

function Stylesheet() {
	return <style>{/** Copy styles from example source code */}</style>
}

This gives us a grid of nine coloured tiles with a small index label in each. Everything from here is about making them react to the pointer.

Related examples

Latest in React

Motion+

Unlock all 400+ examples

  • Source code for every Plus example.
  • Provide examples direct to your agent via Motion's MCP.
  • Lifetime access to new examples and APIs.