
Motion
- 2 installs
- 2 repo stars
- Updated August 3, 2026
- fandhe-ai/agent-reference-skills
Reference the Motion animation library (formerly Framer Motion) for React components, hooks, and the vanilla JS API covering transitions, layout, drag, gestures, and scroll.
About
A structured reference for Motion (formerly Framer Motion) covering React components and hooks plus the vanilla JS API, transitions, layout, gestures, SVG, and scroll animations. A frontend developer loads it when building web animations.
- React (motion/react) and vanilla JS (motion) API coverage
- Accessibility, performance, and bundle-optimization guidance
Motion by the numbers
- 2 all-time installs (skills.sh)
- Ranked #1,294 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/fandhe-ai/agent-reference-skills --skill motionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 2 |
| Last updated | August 3, 2026 |
| Repository | fandhe-ai/agent-reference-skills ↗ |
What it does
Reference the Motion animation library (formerly Framer Motion) for React components, hooks, and the vanilla JS API covering transitions, layout, drag, gestures, and scroll.
Files
motion
Motion (旧 Framer Motion) のリファレンススキル。React 向け (motion/react) と vanilla JS (motion) の両 API、アニメーション概念、ガイドを収録する。
ディレクトリ構成
skills/motion/
SKILL.md
references/
react-components/
README.md
motion.md
animate-presence.md
layout-group.md
lazy-motion.md
motion-config.md
reorder.md
react-hooks/
README.md
use-animate.md
use-scroll.md
use-spring.md
use-transform.md
use-motion-value.md
use-in-view.md
use-drag-controls.md
use-reduced-motion.md
use-motion-template.md
use-velocity.md
use-motion-value-event.md
use-animation-frame.md
use-time.md
use-page-in-view.md
animation/
README.md
overview.md
transitions.md
layout.md
scroll.md
gestures.md
drag.md
svg.md
vanilla-js/
README.md
animate.md
scroll.md
in-view.md
hover.md
press.md
stagger.md
spring.md
timeline.md
motion-value.md
guides/
README.md
installation.md
accessibility.md
performance.md
reduce-bundle-size.md
upgrade-guide.md
migrate-from-gsap.md
samples/
README.md
basic-animation.md
scroll-animation.md
gesture-animation.md
layout-animation.md
animate-presence.md
scripts/
README.md
install.md探索手順
タスクからカテゴリを引き、カテゴリの README.md で目的のページを特定する:
1. 下記マッピング表でタスクに対応するカテゴリを探す 2. そのカテゴリの references/<category>/README.md を参照して目的のページを特定する 3. 該当ページの .md を Read して詳細を確認する
カテゴリの使い分け:
- React で
<motion.div>やフックを使う →react-components/react-hooks/ - React に依存しない素の JS で animate / scroll などを使う →
vanilla-js/ - transition / layout / drag / gesture / SVG / scroll の概念や prop の挙動を知る →
animation/ - インストール / アクセシビリティ / パフォーマンス / バンドル削減 / 移行 →
guides/
タスク → カテゴリ マッピング
| タスク | カテゴリ | 参照 README |
|---|---|---|
<motion.div> で要素をアニメーションさせたい | react-components | references/react-components/README.md |
| マウント / アンマウント時の exit アニメーション (AnimatePresence) | react-components | references/react-components/README.md |
| LayoutGroup / LazyMotion / MotionConfig / Reorder を使いたい | react-components | references/react-components/README.md |
| useAnimate / useScroll / useSpring / useTransform などフックを使いたい | react-hooks | references/react-hooks/README.md |
| 再レンダー無しで値を更新する motion value (useMotionValue) | react-hooks | references/react-hooks/README.md |
| ビューポート進入検知 (useInView) / Reduced Motion 検知 | react-hooks | references/react-hooks/README.md |
| transition (tween/spring/inertia/stagger) の指定方法 | animation | references/animation/README.md |
| layout / layoutId による位置・サイズ・共有要素アニメーション | animation | references/animation/README.md |
| スクロール連動 / hover・tap などジェスチャー / drag / SVG パス | animation | references/animation/README.md |
| React 非依存の素の JS で animate / scroll / inView を使いたい | vanilla-js | references/vanilla-js/README.md |
| hover / press / stagger / spring / timeline / motionValue (vanilla) | vanilla-js | references/vanilla-js/README.md |
| インストール・アクセシビリティ・パフォーマンス・バンドル削減 | guides | references/guides/README.md |
| Framer Motion / GSAP からの移行・アップグレード | guides | references/guides/README.md |
| 典型的な使い方を知りたい | samples | samples/README.md |
| インストール・import 方法を知りたい | scripts | scripts/README.md |
Drag
The drag prop makes an element draggable, with constraints, elasticity, momentum, and imperative control via useDragControls.
Signature / Usage
<motion.div drag /> // both axes
<motion.div drag="x" /> // horizontal only
<motion.div drag="y" /> // vertical onlyProps
| Name | Type | Description |
|---|---|---|
drag | `boolean \ | "x" \ |
dragConstraints | `{ top, left, right, bottom } \ | RefObject` |
dragElastic | number | Elasticity beyond constraints, 0-1 (default 0.5) |
dragMomentum | boolean | Inertia animation on release (default true) |
dragTransition | InertiaOptions | Physics of momentum, e.g. bounceStiffness/bounceDamping |
dragDirectionLock | boolean | Lock to the first dragged axis |
dragPropagation | boolean | Allow drag events to propagate to parents |
dragSnapToOrigin | boolean | Animate back to origin on release |
dragListener | boolean | Default pointer handler (default true); set false for manual control |
dragControls | DragControls | Value from useDragControls() for imperative start |
whileDrag | `object \ | string` |
onDragStart | (event, info) => void | Drag begins |
onDrag | (event, info) => void | Fires continuously while dragging |
onDragEnd | (event, info) => void | Drag completes |
onDirectionLock | (axis) => void | Fires when direction lock resolves |
Constraints (ref-based)
const constraintsRef = useRef(null)
<motion.div ref={constraintsRef}>
<motion.div drag dragConstraints={constraintsRef} dragElastic={0.1} />
</motion.div>Event info
function onDrag(event, info) {
info.point // pointer coordinates
info.delta // distance since last event
info.offset // distance from drag origin
info.velocity // current pointer velocity
}useDragControls (imperative start)
import { motion, useDragControls } from "motion/react"
function Scrubber() {
const dragControls = useDragControls()
return (
<>
<div onPointerDown={(e) => dragControls.start(e, { snapToCursor: true })} />
<motion.div drag="x" dragControls={dragControls} dragListener={false} />
</>
)
}Notes
- Set
dragMomentum={false}to stop without inertia. - Snap-to-grid: use
dragTransition={{ power: 0, modifyTarget: t => Math.round(t / 50) * 50 }}. - Set
draggable={false}on child<img>elements to avoid the browser ghost image. - Pair
dragControlswithdragListener={false}to start drag from a separate handle.
Related
- gestures.md
- transitions.md
- svg.md
Gestures
Animate and respond to hover, tap/press, focus, and pan interactions via while* props and event handlers.
Hover
<motion.a
whileHover={{ scale: 1.2 }}
onHoverStart={(event) => {}}
onHoverEnd={(event) => {}}
/>| Name | Type | Description |
|---|---|---|
whileHover | `object \ | string` |
onHoverStart | (event) => void | Pointer enters |
onHoverEnd | (event) => void | Pointer leaves |
Tap / Press
<motion.button whileTap={{ scale: 0.9, rotate: 3 }} />| Name | Type | Description |
|---|---|---|
whileTap | `object \ | string` |
onTapStart | (event, info) => void | Pointer presses down |
onTap | (event, info) => void | Press released on the same component |
onTapCancel | (event, info) => void | Pointer leaves the component during press |
Focus
<motion.a href="#" whileFocus={{ scale: 1.2 }} />| Name | Type | Description |
|---|---|---|
whileFocus | `object \ | string` |
Pan
<motion.div
onPan={(event, info) => console.log(info.offset.x)}
onPanStart={(event, info) => {}}
onPanEnd={(event, info) => {}}
/>| Name | Type | Description |
|---|---|---|
onPan | (event, info) => void | Fires during pointer movement |
onPanStart | (event, info) => void | Fires once movement exceeds 3px |
onPanEnd | (event, info) => void | Fires on pointer release |
info is a PanInfo with point, delta, offset, velocity.
Notes
- Tap is keyboard-accessible automatically:
EntertriggersonTapStart/whileTap, release triggersonTap, losing focus before release triggersonTapCancel. - Tap auto-cancels if the pointer moves more than 3px inside a draggable parent.
- Pan requires disabling touch scrolling on the relevant axis via the
touch-actionCSS rule for touch input. - Pan has no associated
while*animation prop. - Block parent gestures: use
-CaptureReact props +e.stopPropagation(), orpropagate={{ tap: false }}on the motion child.
Related
- drag.md
- overview.md
- scroll.md
Layout Animations
The layout prop animates size/position changes automatically using CSS transform; layoutId enables shared-element transitions between components.
Signature / Usage
// Animate any layout change (size + position)
<motion.div layout />
// Animate position only (avoids stretching of aspect-changing content)
<motion.div layout="position" />
// Shared-element transition: crossfade between two elements
<motion.div layoutId="underline" />Props
| Name | Type | Description |
|---|---|---|
layout | `boolean \ | "position" \ |
layoutId | string | Link two elements for shared-element crossfade transitions |
layoutScroll | boolean | Mark a scrollable container so scroll offset is measured correctly |
layoutRoot | boolean | Mark a position: fixed element so page scroll is accounted for |
layoutDependency | any | Only measure/animate when this value changes (perf) |
layoutAnchor | { x: number, y: number } | Anchor point (0-1) the animation pins to |
onLayoutAnimationStart | () => void | Fires when a layout animation starts |
onLayoutAnimationComplete | () => void | Fires when a layout animation completes |
LayoutGroup
Synchronizes layout animations across components that may not re-render together.
import { LayoutGroup, motion } from "motion/react"
<LayoutGroup>
<Item />
<Item />
</LayoutGroup>Transition Customization
Layout uses a dedicated layout transition key:
<motion.div
layout
transition={{
ease: "linear",
layout: { duration: 0.3 },
}}
/>Notes
- All layout animations run via the CSS
transformproperty for performance (no paint-triggering width/height changes). - Stretched/distorted children: apply
layoutto child elements for scale correction. - Set
border-radiusandbox-shadowthrough thestyleprop so they are scale-corrected. - Inline elements won't animate; ensure
displayis notinline. - Combine with
AnimatePresence+layoutIdfor smooth enter/exit shared transitions. - Use
scrollbar-gutter: stableto prevent scrollbar-driven layout jumps.
Related
- overview.md
- transitions.md
Animation Overview
How Motion for React animates elements via the motion component, animate/initial/exit props, keyframes, variants, and AnimatePresence.
Signature / Usage
import { motion } from "motion/react"
// Animate toward target values whenever `animate` changes
<motion.div
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
/>
// Skip the enter animation
<motion.div initial={false} animate={{ y: 100 }} />Props
| Name | Type | Description |
|---|---|---|
animate | `object \ | string \ |
initial | `object \ | string \ |
exit | `object \ | string` |
transition | object | Timing/easing/physics config (see transitions.md) |
variants | object | Named animation states that propagate to children |
custom | any | Per-element data passed to dynamic variant functions |
whileHover / whileTap / whileFocus / whileDrag / whileInView | `object \ | string` |
Keyframes
// Sequence of values
<motion.div animate={{ x: [0, 100, 0] }} />
// `null` = current value (natural interruption)
<motion.div animate={{ x: [null, 100, 0] }} />
// Custom keyframe timing (0-1 progress) via `times`
<motion.circle
animate={{ cx: [null, 100, 200] }}
transition={{ duration: 3, times: [0, 0.2, 1] }}
/>Variants
const list = { visible: { opacity: 1 }, hidden: { opacity: 0 } }
const item = {
visible: { opacity: 1, x: 0 },
hidden: { opacity: 0, x: -100 },
}
// Parent label propagates to children automatically
<motion.ul variants={list} initial="hidden" animate="visible">
<motion.li variants={item} />
<motion.li variants={item} />
</motion.ul>Dynamic variants resolve per element via custom:
const variants = {
visible: (i) => ({ opacity: 1, transition: { delay: i * 0.3 } }),
}
items.map((item, i) => <motion.div custom={i} variants={variants} />)AnimatePresence
Keeps exiting elements in the DOM until their exit animation finishes. Children need a stable key.
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>Animatable Values
- Independent transforms:
x,y,z,scale/scaleX/scaleY,rotate/rotateX/rotateY/rotateZ,skewX/skewY,transformPerspective. - Any CSS value:
opacity,filter,background-image,mask-image,box-shadow. - Colors: hex,
rgba,hsla,oklch,oklab,color-mix. - Value-type conversion:
width/heightto/from"auto", units ("100%"<->"calc(...)"). - CSS variables can be both animated (
--rotate) and used as targets (var(--action-bg)).
Notes
- Import from
motion/react. animatere-runs automatically on prop change; no manual trigger needed.- Use
MotionConfigto set default transitions for a subtree. useAnimateprovides imperative sequencing/timeline control for non-motion elements.
Related
- transitions.md
- gestures.md
- scroll.md
- layout.md
Animation
| Name | Description | Path |
|---|---|---|
| Animation Overview | motion component, animate/initial/exit, keyframes, variants, AnimatePresence, animatable values | overview.md |
| Transitions | transition prop: tween, spring, inertia, repeat, stagger, orchestration | transitions.md |
| Layout Animations | layout, layoutId, LayoutGroup, shared-element transitions, scale correction | layout.md |
| Scroll Animations | useScroll, useTransform, whileInView viewport options, useInView | scroll.md |
| Gestures | hover, tap/press, focus, pan while* props and event handlers | gestures.md |
| Drag | drag prop, constraints, elasticity, momentum, useDragControls | drag.md |
| SVG Animation | line drawing (pathLength/pathSpacing/pathOffset), path morphing, viewBox, attributes | svg.md |
Scroll Animations
Two approaches: scroll-linked animations driven by useScroll motion values, and scroll-triggered animations via the whileInView prop.
Scroll-Linked: useScroll
import { useScroll, motion } from "motion/react"
function Progress() {
const { scrollYProgress } = useScroll()
return <motion.div style={{ scaleX: scrollYProgress, originX: 0 }} />
}useScroll return values
| Name | Type | Description |
|---|---|---|
scrollX / scrollY | MotionValue<number> | Absolute scroll position in pixels |
scrollXProgress / scrollYProgress | MotionValue<number> | Normalized progress 0-1 |
useScroll options
| Name | Type | Description |
|---|---|---|
target | RefObject | Track a specific element's progress through the viewport |
offset | string[] | When tracking starts/ends, e.g. ["start end", "end start"] |
container | RefObject | Custom scroll container (defaults to window) |
Map scroll progress to CSS values with useTransform:
import { useTransform } from "motion/react"
const filter = useTransform(scrollYProgress, [0, 1], ["blur(0px)", "blur(10px)"])
return <motion.div style={{ filter }} />Scroll-Triggered: whileInView
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
/>viewport options
| Name | Type | Description |
|---|---|---|
once | boolean | Play only the first time the element enters view |
amount | `"some" \ | "all" \ |
margin | string | Margin around the viewport boundary |
root | RefObject | Custom scroll container ref |
useInView (state, non-motion elements)
import { useInView } from "motion/react"
const ref = useRef(null)
const isInView = useInView(ref)Notes
useScrollreturnsMotionValues; pass them tostyle(oruseTransform) — do not read them in render.whileInViewis best for one-shot reveal animations;useScrollis best for parallax/progress bars and scroll scrubbing.useInViewsets React state and works with plain (non-motion) elements.
Related
- overview.md
- transitions.md
SVG Animation
Motion provides a motion component for every SVG element, enabling line drawing, path morphing, attribute animation, and viewBox animation.
Line Drawing
<motion.path
d={d}
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
/>| Name | Type | Description |
|---|---|---|
pathLength | number (0-1) | Total drawn length of the stroke |
pathSpacing | number (0-1) | Length of the gap between segments |
pathOffset | number (0-1) | Where the drawn segment begins |
Supported elements: path, circle, ellipse, line, polygon, polyline, rect.
Path Morphing
Animate the d attribute between similar paths:
<motion.path d="M 0,0 l 0,10 l 10,10" animate={{ d: "M 0,0 l 10,0 l 10,10" }} />Paths must have identical numbers and types of instructions. Use a library like Flubber for dissimilar interpolation.
Animating viewBox
// Pan
<motion.svg viewBox="0 0 200 200" animate={{ viewBox: "100 0 200 200" }} />
// Zoom
<motion.svg viewBox="0 0 200 200" animate={{ viewBox: "-100 -100 300 300" }} />Attributes & Transforms
// Animate SVG attributes directly
<motion.circle cx={0} animate={{ cx: 50 }} />
// Use attribute (not CSS transform) shorthand
<motion.rect attrX={0} animate={{ attrX: 100 }} />
// Restore native SVG transform origin (top-left)
<motion.rect style={{ rotate: 90, transformBox: "view-box" }} />| Name | Description |
|---|---|
attrX / attrY | SVG x/y attribute (distinct from CSS x/y transform) |
attrScale | SVG scale attribute |
Drag with scaled viewBox
import { motion, MotionConfig, transformViewBoxPoint } from "motion/react"
function Component() {
const ref = useRef(null)
return (
<MotionConfig transformPagePoint={transformViewBoxPoint(ref)}>
<svg ref={ref} viewBox="0 0 100 100" style={{ width: 200, height: 200 }}>
<motion.circle drag />
</svg>
</MotionConfig>
)
}Notes
- By default Motion sets SVG transform origin to the element center (matching CSS); use
transformBox: "view-box"to restore native behavior. MotionValues pass through both attribute targets (cx={cx}) andstyle(style={{ opacity }}).- Components available:
motion.svg,motion.path,motion.circle,motion.ellipse,motion.rect,motion.line,motion.polygon,motion.polyline, and filter primitives likemotion.feTurbulence,motion.feDisplacementMap.
Related
- overview.md
- transitions.md
- drag.md
Transitions
The transition prop controls timing, easing, spring physics, repetition, and orchestration of animations.
Signature / Usage
<motion.div
animate={{ x: 100 }}
transition={{ ease: "easeOut", duration: 2, delay: 0.5 }}
/>Value-specific transitions and a default fallback:
<motion.li
animate={{
x: 0,
opacity: 1,
transition: {
default: { type: "spring" },
opacity: { ease: "linear" },
},
}}
/>Tween Options (duration-based, default for most values)
| Name | Type | Description |
|---|---|---|
duration | number | Length in seconds (default 0.3) |
ease | `string \ | number[] \ |
times | number[] | Keyframe positions (0-1) for multi-value animations |
Spring Options (type: "spring", default for transforms/layout)
| Name | Type | Description |
|---|---|---|
stiffness | number | Spring tension; higher = snappier (default 1) |
damping | number | Opposing force; 0 = infinite oscillation (default 10) |
mass | number | Object mass; higher = more sluggish (default 1) |
velocity | number | Initial velocity |
bounce | number | Bounciness 0-1 (default 0.25); duration-based mode |
duration / visualDuration | number | Spring time; visualDuration is easier to tune |
restSpeed | number | End when below this speed (default 0.1) |
restDelta | number | End when below this distance (default 0.01) |
Inertia Options (type: "inertia", used by drag momentum)
| Name | Type | Description |
|---|---|---|
power | number | Higher = farther target (default 0.8) |
timeConstant | number | Deceleration in ms (default 700) |
modifyTarget | (v) => number | Adjust target, e.g. snap-to-grid |
min / max | number | Boundary constraints with spring bounce |
bounceStiffness / bounceDamping | number | Boundary bounce physics |
Orchestration / Repeat
| Name | Type | Description |
|---|---|---|
delay | number | Start delay in seconds; negatives start mid-animation |
repeat | number | Repeat count; Infinity for perpetual |
repeatType | `"loop" \ | "reverse" \ |
repeatDelay | number | Wait between repetitions |
when | `"beforeChildren" \ | "afterChildren"` |
delayChildren | number | Delay child variant animations |
staggerChildren | `number \ | stagger()` |
import { stagger } from "motion/react"
const container = {
show: {
opacity: 1,
transition: {
delayChildren: 0.5,
staggerChildren: stagger(0.1, { from: "last" }),
},
},
}Notes
- Spring is the default for
x,y,scale,rotate, and layout animations; tween is the default for most other values. - Higher-specificity transitions replace inherited defaults; add
inherit: trueto merge with a parentMotionConfigtransition. path: arc()curves motion-value movement along an arc instead of a straight line.
Related
- overview.md
- layout.md
- drag.md
Accessibility
Respect the OS "Reduced Motion" setting to avoid motion sickness and usability issues. Motion provides MotionConfig and the useReducedMotion hook.
Signature / Usage
MotionConfig reducedMotion
Automatically disables transform and layout animations while preserving opacity and color changes.
import { MotionConfig } from "motion/react"
export function App({ children }) {
return (
<MotionConfig reducedMotion="user">
{children}
</MotionConfig>
)
}useReducedMotion hook
import { useReducedMotion } from "motion/react"
function Sidebar({ isOpen }) {
const shouldReduceMotion = useReducedMotion()
const animate = shouldReduceMotion
? { opacity: isOpen ? 1 : 0 }
: { x: isOpen ? 0 : "-100%" }
return <motion.div animate={animate} />
}Options / Props
| Name | Type | Description |
|---|---|---|
reducedMotion | `"user" \ | "always" \ |
Notes
- Prefer animating
opacityinstead oftransformwhen reduced motion is active. - Use
useReducedMotionto disable auto-playing background video and parallax effects for sensitive users. reducedMotion="user"keeps opacity/color transitions; only transform and layout animations are disabled.
Related
- Performance
Installation
Install Motion for React and import the motion component. Requires React 18.2 or higher.
Signature / Usage
npm install motion
# or
yarn add motion
# or
pnpm add motion"use client"
import { motion } from "motion/react"
export default function MyComponent() {
return <motion.div animate={{ scale: 1.5 }} />
}CDN (no install)
<script type="module">
import motion from "https://cdn.jsdelivr.net/npm/motion@latest/react/+esm"
</script>Notes
- Only compatible with React
18.2and higher. - The package is
motion; React APIs are imported frommotion/react. - Next.js App Router: add
"use client"at the top of files that usemotion. Alternatively import frommotion/react-clientto reduce client-side JavaScript. - Vite: no special configuration required.
Related
- Reduce bundle size
- Upgrade from Framer Motion
Migrate from GSAP
Motion separates animation values from options into distinct objects and uses declarative timelines, the Web Animations API, and ScrollTimeline for hardware-accelerated, tree-shakeable animations.
Signature / Usage
Basic animation
// GSAP
gsap.to("#box", { duration: 10, rotation: 360 })
// Motion
animate("#box", { rotate: 360 }, { duration: 10 })Keyframes
// Motion uses array syntax instead of fromTo
animate(".box", { opacity: [0, 0.5] })Timeline (declarative array)
const timeline = [
["#id", { x: 100 }, { duration: 1 }],
"My label",
["#id", { y: 100 }, { duration: 1 }],
]
animate(timeline, options)React with useAnimate
const [scope, animate] = useAnimate()
useEffect(() => {
animate(scope.current, { rotate: 360 }, { duration: 10 })
}, [])Options / Props
API equivalents:
| GSAP | Motion |
|---|---|
gsap.to() | animate() |
rotation | rotate |
repeat: -1 | repeat: Infinity |
ease: "none" | ease: "linear" |
.timeScale() | .speed |
.time() | .time |
.kill() | .stop() |
.progress(1) | .complete() |
ScrollTrigger (viewport) | inView() (Intersection Observer) |
scrollTrigger { scrub: true } | scroll(animation, { target, offset }) |
useGSAP | useAnimate |
Notes
- Motion separates values (
{ rotate: 360 }) from options ({ duration: 10 }) into two objects. inView()uses Intersection Observer (lower CPU than frame-based detection);scroll()enables hardware-accelerated scroll animations.- Bundle size: mini
animate~2.3kb (90% smaller than GSAP),scroll~75% of GSAP's, fullanimate~18kb. Tree-shaking ships only imported functions. - Hardware acceleration applies to
transform,filter,opacityvia the Web Animations API and ScrollTimeline. - Limitations: no
gsap.from()equivalent, no mutable option API, layout animations are React-only (not in the JS API),onUpdaterestricted to single-value animations.
Related
- Upgrade from Framer Motion
- Performance
Performance
Animate cheap, compositor-only properties (transform, opacity) to keep animations hardware-accelerated and smooth even when the main JS thread is busy.
Signature / Usage
The browser updates styles in three steps:
1. Layout — calculate element sizes and positions. 2. Paint — draw the page into graphical layers. 3. Composite — render layers to the viewport.
The best-performing styles trigger only the composite step. transform and opacity operate directly on a layer and are the safest values to animate.
Reduce layer size with will-change
element.style.willChange = "transform"
animate(element, { borderRadius: "50%" })Prefer compositor-friendly alternatives
// Instead of boxShadow
animate(element, { filter: "drop-shadow(...)" })
// Instead of borderRadius
animate(element, { clipPath: "inset(0 round 50%)" })Notes
- Cheap (composite only):
transform,opacity. Widely hardware-accelerated. - Emerging compositor support:
filter,background-color,clip-path, SVG (Chrome/Firefox). - Expensive (layout):
height,border-width,padding,position— recalculate dimensions of affected elements. - Expensive (paint):
box-shadow,border-radius— avoid layout but require costly repaints. - At 60fps the browser has ~16.7ms per frame; repaints can exceed 100ms.
- Motion uses the Web Animations API to hardware-accelerate animations when the browser supports it; treat it as progressive enhancement, not a requirement.
- Test on low-powered devices. The value you choose to animate is what you have the most control over.
Related
- Accessibility
- Reduce bundle size
Guides
| Name | Description | Path |
|---|---|---|
| Installation | Install Motion for React, imports, React 18.2+ requirement, Next.js/Vite/CDN setup | installation.md |
| Accessibility | Reduced Motion support via MotionConfig reducedMotion and useReducedMotion hook | accessibility.md |
| Performance | Compositor-only properties (transform/opacity), three rendering steps, will-change, hardware acceleration | performance.md |
| Reduce Bundle Size | m component + LazyMotion, domAnimation/domMax features, code-splitting, strict mode | reduce-bundle-size.md |
| Upgrade from Framer Motion | Rename to motion, import path change, breaking changes per major version | upgrade-guide.md |
| Migrate from GSAP | GSAP-to-Motion API mapping, declarative timelines, scroll, bundle size, limitations | migrate-from-gsap.md |
Reduce Bundle Size
Replace the full motion component (34kb) with the slim m component plus LazyMotion to cut the initial bundle to under 4.6kb, loading animation features on demand.
Signature / Usage
The m component + synchronous features
import * as m from "motion/react-m"
import { LazyMotion, domAnimation } from "motion/react"
function App({ children }) {
return (
<LazyMotion features={domAnimation}>
<m.div animate={{ opacity: 1 }} />
{children}
</LazyMotion>
)
}Lazy loading (code-splitting)
// features.js
import { domMax } from "motion/react"
export default domMaximport { LazyMotion } from "motion/react"
import * as m from "motion/react-m"
const loadFeatures = () =>
import("./features.js").then(res => res.default)
function App() {
return (
<LazyMotion features={loadFeatures}>
<m.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />
</LazyMotion>
)
}Strict mode
<LazyMotion features={domAnimation} strict>
{/* Throws if motion.div is used instead of m.div */}
</LazyMotion>Options / Props
| Name | Type | Description |
|---|---|---|
features | `FeatureBundle \ | LazyFeatureBundle` |
strict | boolean | Throws an error if the full motion component is used inside LazyMotion, enforcing m usage. |
Notes
mworks identically tomotionbut ships without preloaded features (animations, layout, drag).domAnimation(+15kb): animations, variants, exit animations, tap/hover/focus gestures.domMax(+25kb): everything indomAnimationplus pan/drag and layout animations.- Lazy loading defers feature code until after the initial render via a dynamic
import().
Related
- Installation
- Performance
Upgrade from Framer Motion
Framer Motion was renamed to Motion. Uninstall framer-motion, install motion, and update imports from "framer-motion" to "motion/react".
Signature / Usage
npm uninstall framer-motion
npm install motion// Before
import { motion } from "framer-motion"
// After
import { motion } from "motion/react"Notes
Breaking changes by major version:
- 12.0 — No breaking changes in Motion for React (see the JavaScript upgrade guide for vanilla JS changes).
- 11.0 — Multiple
MotionValueupdates in the same frame exclude intermediate values from velocity calc. Render scheduling moved from synchronous to microtask; Jest tests mustawait nextFrame()before asserting styles. - 10.0 — Removed
IntersectionObserverfallback forwhileInView. DeprecatedexitBeforeEnternow throws; usemode="wait". - 9.0 — Elements with tap listeners receive
tabindex="0";whileFocusmirrors:focus-visible. - 8.0 — Removed mouse/touch event polyfills. Use
onPointerDowninstead ofonMouseDown/onTouchStart. - 7.0 — React 18 is the minimum supported version.
- 6.0 — 3D moved to a separate package (
framer-motion/three→framer-motion-3d).
Related
- Installation
- Migrate from GSAP
AnimatePresence
Enables exit animations for motion components when they are removed from the React tree, unlocking the exit prop on direct children.
Signature / Usage
import { AnimatePresence, motion } from "motion/react"
;<AnimatePresence>
{show && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>Detects removal via conditional rendering, key prop changes, or list item changes.
Options / Props
| Name | Type | Description |
|---|---|---|
initial | boolean | Set false to skip initial animations for children present on first render |
mode | "sync" \ | "wait" \ |
custom | any | Dynamic data passed to exiting components, read via usePresenceData() |
onExitComplete | function | Fires when all exit animations finish |
propagate | boolean | When true, child exit animations trigger even when this AnimatePresence exits a parent one |
Notes
- All immediate children require unique, stable
keyprops (avoid array indices). AnimatePresencemust wrap the conditional logic; do not conditionally renderAnimatePresenceitself.- Children can read exit state via
useIsPresent()andusePresence()hooks.
Related
- motion
- Reorder
LayoutGroup
Groups motion components that might not render together but affect each other's layout, so sibling layout animations stay in sync.
Signature / Usage
import { LayoutGroup } from "motion/react"
function Accordion() {
return (
<LayoutGroup>
<AccordionItem header="Item 1" />
<AccordionItem header="Item 2" />
</LayoutGroup>
)
}Use when independent components each manage their own state (e.g. accordion items) yet need their layout animations to coordinate.
Options / Props
| Name | Type | Description |
|---|---|---|
id | string | Namespaces the group so multiple groups can use the same layoutId values without collision |
inherit | boolean | Controls whether children inherit the group's layout animation behavior |
Notes
- Works with
motioncomponents that have thelayoutprop enabled. layoutIdis global; supplyidto prevent collisions when severalLayoutGroups coexist.- Enables smooth shared element transitions across components with independent state.
Related
- motion
- AnimatePresence
LazyMotion
Reduces bundle size by loading Motion's animation features on demand, cutting the initial bundle from ~34kb to ~4.6kb when paired with the lightweight m component.
Signature / Usage
import { LazyMotion, domAnimation } from "motion/react"
import * as m from "motion/react-m"
export const MyComponent = () => (
<LazyMotion features={domAnimation}>
<m.div animate={{ opacity: 1 }} />
</LazyMotion>
)Use the m component (motion/react-m) instead of motion inside LazyMotion to gain the code-splitting benefit.
Options / Props
| Name | Type | Description |
|---|---|---|
features | feature bundle | Capabilities to load, e.g. domAnimation; can be passed synchronously or as an async loader to defer until after hydration |
strict | boolean | When true, throws if a standard motion component is used inside, preventing accidental bundle regressions |
Notes
- Always use
minsideLazyMotion; mixing standardmotioncomponents undermines the optimization. - Synchronous feature loading suits smaller subsets; async loading defers initialization until after site hydration.
domAnimationprovides a core feature set; larger feature bundles exist for more capabilities.
Related
- motion
- MotionConfig
MotionConfig
Wrapper that sets configuration defaults (transition, reduced-motion behavior, CSP nonce) for all descendant motion components.
Signature / Usage
import { motion, MotionConfig } from "motion/react"
export const MyComponent = () => (
<MotionConfig transition={{ duration: 1 }}>
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />
</MotionConfig>
)Options / Props
| Name | Type | Description |
|---|---|---|
transition | object | Fallback transition applied to all child motion components unless individually overridden |
reducedMotion | "never" \ | "user" \ |
nonce | string | Passes a nonce attribute to Motion's inline styles for Content Security Policy compliance |
Notes
- A "set it and forget it" way to define animation behavior once at a high level instead of repeating it per component.
- When reduced motion is active, transform and layout animations are disabled while opacity and color animations continue.
Related
- motion
- LazyMotion
motion
Drop-in replacement for HTML and SVG elements that adds animation props running at up to 120fps without triggering React re-renders.
Signature / Usage
import { motion } from "motion/react"
;<motion.div
initial={{ opacity: 0, x: -100 }}
animate={{ opacity: 1, x: 0 }}
whileHover={{ scale: 1.1 }}
transition={{ duration: 0.5 }}
/>There is a motion component for every HTML and SVG element (motion.div, motion.span, motion.circle, ...). Wrap custom components with motion.create(Component).
Options / Props
| Name | Type | Description |
|---|---|---|
initial | object \ | string \ |
animate | object \ | string \ |
exit | object \ | string \ |
transition | object | Timing, easing, and spring configuration |
variants | object | Named animation states for reuse and propagation |
whileHover | object \ | string |
whileTap | object \ | string |
whileFocus | object \ | string |
whileInView | object \ | string |
drag | boolean \ | "x" \ |
dragConstraints | object \ | ref |
dragMomentum | boolean | Applies inertia after release |
layout | boolean \ | "position" \ |
layoutId | string | Tracks shared element transitions across components |
style | object | Supports motion values and independent transforms |
onAnimationStart / onAnimationComplete | function | Animation lifecycle callbacks |
Notes
- Animations bypass React's render cycle, updating via the browser's native animation pipeline.
- Animating
transformandopacityoffers optimal performance. - Compatible with server-side rendering.
- For drag gestures on touch devices, disable scrolling with the
touch-actionCSS rule.
Related
- AnimatePresence
- LayoutGroup
- LazyMotion
- MotionConfig
React Components
| Name | Description | Path |
|---|---|---|
| motion | Animated drop-in replacement for HTML/SVG elements (initial, animate, exit, gestures, drag, layout) | motion.md |
| AnimatePresence | Exit animations when motion components are removed from the tree | animate-presence.md |
| LayoutGroup | Coordinates layout animations across independently-rendered components | layout-group.md |
| LazyMotion | On-demand feature loading to shrink bundle size, paired with the m component | lazy-motion.md |
| MotionConfig | Sets default transition, reducedMotion, and CSP nonce for descendants | motion-config.md |
| Reorder | Drag-to-reorder lists via Reorder.Group and Reorder.Item | reorder.md |
Reorder
Drag-to-reorder lists with automatic layout animations. Reorder.Group wraps the list and manages ordering; Reorder.Item represents each draggable element.
Signature / Usage
import { Reorder } from "motion/react"
import { useState } from "react"
function List() {
const [items, setItems] = useState([0, 1, 2, 3])
return (
<Reorder.Group axis="y" values={items} onReorder={setItems}>
{items.map((item) => (
<Reorder.Item key={item} value={item}>
{item}
</Reorder.Item>
))}
</Reorder.Group>
)
}Options / Props
Reorder.Group
| Name | Type | Description |
|---|---|---|
values | array | The array being reordered; each entry maps to a Reorder.Item value |
onReorder | function | Callback receiving the new order; use it to update state |
axis | "x" \ | "y" |
as | string | HTML element to render as (default "ul") |
Reorder.Item
| Name | Type | Description |
|---|---|---|
value | any | Identifies which entry in values this item represents |
as | string | HTML element to render as (default "li") |
Notes
- Items automatically animate to new positions when reordered, added, or removed.
- Pair with
AnimatePresencefor entry/exit effects. - Lists inside scrollable containers auto-scroll while dragging.
- Dragged items elevate above siblings via z-index; items require non-static positioning.
Related
- motion
- AnimatePresence
react-hooks
| Name | Description | Path |
|---|---|---|
| useAnimate | Manually start and control animations scoped to the component | use-animate.md |
| useAnimationFrame | Run a callback every frame with time/delta args | use-animation-frame.md |
| useDragControls | Manually initiate and control drag gestures | use-drag-controls.md |
| useInView | Detect when an element enters or leaves the viewport | use-in-view.md |
| useMotionTemplate | Compose motion values into a template-literal string value | use-motion-template.md |
| useMotionValue | Create a signal-like value that updates the DOM without re-render | use-motion-value.md |
| useMotionValueEvent | Fire lifecycle-managed events on motion value changes | use-motion-value-event.md |
| usePageInView | Track browser tab visibility to pause/resume work | use-page-in-view.md |
| useReducedMotion | Detect the Reduced Motion accessibility setting reactively | use-reduced-motion.md |
| useScroll | Track scroll progress as motion values for scroll-linked animation | use-scroll.md |
| useSpring | Motion value animating to its target with spring physics | use-spring.md |
| useTime | Per-frame elapsed-time motion value for perpetual animations | use-time.md |
| useTransform | Transform one or more motion values into a new derived value | use-transform.md |
| useVelocity | Track the velocity of another motion value | use-velocity.md |
useAnimate
Manually start and control animations, scoped to the current React component.
Signature / Usage
import { useAnimate } from "motion/react"
// or, smaller bundle with the mini animate function:
import { useAnimate } from "motion/react-mini"
function Component({ children }) {
const [scope, animate] = useAnimate()
useEffect(() => {
// Selectors are scoped to children of the element holding `scope`
animate("li", { opacity: 1 })
}, [])
return <ul ref={scope}>{children}</ul>
}Returns a tuple [scope, animate]:
scope— a ref attached to an HTML/SVG/motion element that establishes the animation boundary.animate— a scoped version of theanimate()function used to trigger animations.
Notes
- DOM queries passed to
animateare automatically limited to children of thescopeelement. - All animations created in scope are cleaned up automatically when the component unmounts.
- Can animate the
scopeelement directly or target children via CSS selectors. - Supports timeline sequences (arrays of animation definitions) for orchestration.
Related
- useInView
- useMotionValue
useAnimationFrame
Run a callback every frame, with elapsed time and delta arguments, using Motion's animation loop.
Signature / Usage
import { useAnimationFrame } from "motion/react"
function Component() {
const ref = useRef(null)
useAnimationFrame((time, delta) => {
ref.current.style.transform = `rotateY(${time}deg)`
})
return <div ref={ref} />
}Options / Props
| Argument | Description |
|---|---|
time | Cumulative duration (ms) since the callback was first invoked |
delta | Elapsed time (ms) between the current and previous frame |
Notes
- Integrates with Motion's animation loop for optimized performance.
- Runs without triggering React re-renders each frame.
- Pass
undefinedas the callback to pause (e.g. combined withusePageInView). - Related to the lower-level
frame()scheduler.
Related
- useTime
- usePageInView
useDragControls
Manually initiate and control drag gestures on motion components, as an alternative to automatic drag detection (e.g. starting a drag from a video scrubber click).
Signature / Usage
import { useDragControls } from "motion/react"
import { motion } from "motion/react"
function Component() {
const controls = useDragControls()
return (
<>
<div onPointerDown={(event) => controls.start(event)} />
<motion.div drag dragControls={controls} />
</>
)
}Options / Props
controls.start(event, options?) options:
| Name | Default | Description |
|---|---|---|
snapToCursor | false | Immediately snap the motion component to the cursor |
distanceThreshold | 3 (px) | Pointer travel distance before drag initializes |
touchAction | — | Apply touch-action: none to triggering elements for touch support |
Methods: controls.stop() ends the drag normally; controls.cancel() ends the drag and skips the onDragEnd callback.
Notes
- Set
dragListener={false}on the motion component to prevent automatic drag initiation, so dragging only starts viacontrols.start.
Related
- useMotionValue
- useVelocity
useInView
Lightweight (0.6kb) hook that detects when an element enters or leaves the viewport, enabling scroll-triggered state changes.
Signature / Usage
import { useInView } from "motion/react"
function Component() {
const ref = useRef(null)
const isInView = useInView(ref)
useEffect(() => {
console.log("Element is in view:", isInView)
}, [isInView])
return <div ref={ref} />
}Returns a boolean: true when the element is inside the viewport, false otherwise.
Options / Props
| Name | Default | Description |
|---|---|---|
root | window viewport | Ref to a scrollable parent to use as the tracking viewport |
margin | "0px" | Adjusts the detection area using CSS margin syntax (e.g. "0px 100px -50px 0px") |
once | false | Stops observing after the element enters view, then always returns true |
initial | false | Initial return value until element measurement completes |
amount | "some" | How much must be visible: "some", "all", or a number 0–1 |
Notes
- Works with any HTML element via ref.
marginwon't affect cross-origin iframes without an explicitroot.- Combine with
useAnimatefor enter animations.
Related
- useAnimate
- usePageInView
- useScroll
useMotionTemplate
Create a motion value from a string template containing other motion values, automatically updating whenever any contained motion value changes.
Signature / Usage
import { useMotionTemplate, useMotionValue } from "motion/react"
import { motion } from "motion/react"
function Component() {
const blur = useMotionValue(10)
const saturate = useMotionValue(50)
const filter = useMotionTemplate`blur(${blur}px) saturate(${saturate}%)`
return <motion.div style={{ filter }} />
}Uses tagged template literal syntax — interpolate motion values directly into a string.
Notes
- Returns a motion value rendering the template string with the current motion value states.
- Supports mixing static text with multiple dynamic motion values.
- Works seamlessly with
styleprops on motion components (e.g.filter,transform).
Related
- useMotionValue
- useSpring
- useTransform
useMotionValueEvent
Fire events when a motion value changes, with handlers tied to the component lifecycle and cleaned up automatically on unmount.
Signature / Usage
import { useMotionValue, useMotionValueEvent } from "motion/react"
function Component() {
const x = useMotionValue(0)
useMotionValueEvent(x, "change", (latest) => {
console.log("x changed to", latest)
})
return null
}Signature: useMotionValueEvent(motionValue, eventName, callback).
Options / Props
| Event | Description |
|---|---|
change | Fires when the value updates; receives the latest value |
animationStart | Fires when animation begins |
animationComplete | Fires when animation finishes |
animationCancel | Fires when animation is cancelled |
Notes
- Handlers are cleaned up automatically when the component unmounts.
- Wraps the motion value's
on()method for convenience. - For custom cleanup logic, use
motionValue.on("change", cb)insideuseEffectand return the unsubscribe.
Related
- useMotionValue
- useVelocity
useMotionValue
Create a MotionValue — a composable, signal-like value that updates the DOM directly without triggering React re-renders.
Signature / Usage
import { useMotionValue } from "motion/react"
const x = useMotionValue(0)
x.get() // 100
x.set(100) // update without re-rendering React
// Subscribe to changes
const unsubscribe = x.on("change", (latest) => console.log(latest))
// Compose into dependent values
const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0])
return <motion.div style={{ x, opacity }} />Accepts an initial string or number.
Options / Props
| Method | Description |
|---|---|
get() | Returns the current value |
set(value) | Updates the value without a React re-render |
jump(value) | Sets value, resets velocity to 0, ends active animations, ignores attached effects |
getVelocity() | Velocity per second for numeric values (0 for strings/colors) |
isAnimating() | true if currently animating |
stop() | Halts active animations |
on(event, cb) | Subscribe to "change", "animationStart", "animationComplete", "animationCancel"; returns an unsubscribe fn |
destroy() | Cleans up subscribers (usually automatic) |
Notes
- Motion values track both state and velocity.
- Synchronize motion across components and compose via
useTransform,useSpring,useVelocity. - Prefer
useMotionValueEventover manualon()in components for automatic cleanup.
Related
- useTransform
- useSpring
- useVelocity
- useMotionValueEvent
- useMotionTemplate
usePageInView
Track document visibility state to pause animations, video, or other work when the user switches tabs and resume on return.
Signature / Usage
import { usePageInView } from "motion/react"
function Component() {
const videoRef = useRef(null)
const isInView = usePageInView()
useEffect(() => {
const video = videoRef.current
if (!video) return
isInView ? video.play() : video.pause()
}, [isInView])
return <video ref={videoRef} />
}Returns a boolean: true when the page is the active browser tab. Defaults to true on the server and the initial client render.
Notes
- Improves performance: reduces CPU usage, extends battery life.
- Pair with
useAnimationFrameto pause loops:useAnimationFrame(isInView ? update : undefined).
Related
- useInView
- useAnimationFrame
useReducedMotion
Detect whether the device has the Reduced Motion accessibility setting enabled, reactively re-rendering when it changes.
Signature / Usage
import { useReducedMotion } from "motion/react"
import { motion } from "motion/react"
export function Sidebar({ isOpen }) {
const shouldReduceMotion = useReducedMotion()
const closedX = shouldReduceMotion ? 0 : "-100%"
return (
<motion.div
animate={{ opacity: isOpen ? 1 : 0, x: isOpen ? 0 : closedX }}
/>
)
}Returns a boolean: true if Reduced Motion is enabled, false otherwise.
Notes
- Reactive: actively responds to changes and re-renders components with the latest setting.
- Use cases: replace motion-sickness-inducing animations with opacity changes, disable video autoplay, turn off parallax.
- Part of Motion's accessibility features for respecting user motion preferences.
Related
- useAnimate
useScroll
Create scroll-linked animations by tracking scroll progress as motion values. Can leverage the browser ScrollTimeline API for hardware acceleration.
Signature / Usage
import { useScroll } from "motion/react"
import { motion } from "motion/react"
// Page scroll progress bar
function ProgressBar() {
const { scrollYProgress } = useScroll()
return <motion.div style={{ scaleX: scrollYProgress }} />
}
// Track an element's progress through the viewport
function Section() {
const ref = useRef(null)
const { scrollYProgress } = useScroll({ target: ref })
return <div ref={ref} />
}Returns four motion values:
scrollX/scrollY— absolute scroll position in pixels per axis.scrollXProgress/scrollYProgress— normalized progress0–1within the defined offsets.
Options / Props
| Name | Default | Description |
|---|---|---|
container | viewport | Ref to a scrollable element to track instead of the window |
target | scroll container | Ref to an element whose progress within the container is tracked |
axis | "y" | Tracked axis ("x" or "y") |
offset | ["start start", "end end"] | Intersection points defining progress boundaries |
trackContentSize | false | Auto-update when content size changes |
Notes
- Offsets accept named edges (
start,center,end), percentages, pixels, and viewport units. - Returned values are motion values, composable with
useTransformanduseSpring. - GPU-accelerated when driving
opacity,transform,clipPath, orfilter.
Related
- useTransform
- useSpring
- useMotionValueEvent
useSpring
Create a motion value that animates to its latest target using spring physics. Can run standalone with manual control or automatically follow another motion value.
Signature / Usage
import { useSpring, useMotionValue } from "motion/react"
// Manual control with a number or unit string
const x = useSpring(0)
const y = useSpring("100vh")
// Track and smooth another motion value (e.g. scroll progress)
const scrollProgress = useMotionValue(0)
const smoothed = useSpring(scrollProgress)
// With spring transition options
const animated = useSpring(0, { stiffness: 300 })Options / Props
| Name | Default | Description |
|---|---|---|
stiffness / damping / mass | — | Standard spring transition parameters |
skipInitialAnimation | false | Instantly jump to the initial value when tracking sources (like useScroll) that update after DOM measurement |
Notes
.set(value)updates the value and animates to the target with the spring..jump(value)updates immediately without animation.- Accepts number or unit-type strings (
px,%,vh, etc.). - Useful for "following" patterns: pointer tracking, scroll progress smoothing.
Related
- useMotionValue
- useScroll
- useTransform
useTime
Create a motion value that updates every frame with the elapsed time (ms) since creation. Useful for perpetual animations.
Signature / Usage
import { useTime, useTransform } from "motion/react"
import { motion } from "motion/react"
function Component() {
const time = useTime()
const rotate = useTransform(
time,
[0, 4000], // for every 4 seconds...
[0, 360], // ...rotate 360deg
{ clamp: false }
)
return <motion.div style={{ rotate }} />
}Returns a motion value emitting milliseconds since instantiation, updating once per frame.
Notes
- Creates a new independent motion value on each call.
- Effective for continuous, self-sustaining animations.
- Compose with
useTransformand other motion value hooks. clamp: falseallows values to grow unbounded for infinite loops.
Related
- useTransform
- useAnimationFrame
useTransform
Create a new motion value that transforms the output of one or more motion values. Composable and animatable without triggering React re-renders.
Signature / Usage
import { useTransform } from "motion/react"
// 1. Transform function — reads motion values via .get()
const doubledX = useTransform(() => x.get() * 2)
// 2. Value mapping — map a single value from input to output range
const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0])
// 3. Multiple output values from one input
const { opacity, scale, filter } = useTransform(offset, [100, 600], {
opacity: [1, 0.4],
scale: [1, 0.6],
filter: ["blur(0px)", "blur(10px)"],
})Options / Props
| Name | Default | Description |
|---|---|---|
clamp | true | If true, clamps output to within the range; if false, keeps mapping beyond it |
ease | — | Easing function(s) to ease mixing between values (must be JavaScript functions) |
mixer | — | Custom mixer; returns a function accepting progress 0–1 |
Notes
- The function form automatically subscribes to motion values read via
.get()and recalculates per frame. - Input ranges must be monotonically increasing or decreasing numbers.
- Output ranges must contain values of the same type (numbers, colors, units, strings).
- Input and output ranges must have equal length.
Related
- useMotionValue
- useScroll
- useSpring
useVelocity
Create a motion value that tracks the velocity of another motion value, enabling velocity-based animations and interactions.
Signature / Usage
import { useMotionValue, useVelocity, useTransform } from "motion/react"
import { motion } from "motion/react"
function Component() {
const x = useMotionValue(0)
const xVelocity = useVelocity(x)
const scale = useTransform(
xVelocity,
[-3000, 0, 3000],
[2, 1, 2],
{ clamp: false }
)
return <motion.div drag="x" style={{ x, scale }} />
}Accepts a numerical motion value; returns a new motion value updating with the source's velocity.
Notes
- Works with any numerical motion value.
- Chainable — pass a velocity value back into
useVelocityto compute acceleration. - Integrates with
useMotionValueEventto listen for velocity changes. - Useful for drag interactions and scroll-based effects.
Related
- useMotionValue
- useTransform
- useDragControls
animate()
Core function to animate HTML/SVG elements, motion values, single values, or JS objects.
Signature / Usage
import { animate } from "motion"
const box = document.getElementById("box")
const animation = animate(
box,
{ opacity: 0, x: 100 },
{ duration: 0.5, ease: "easeOut" }
)
animation.pause()
animation.time = 0.25
animation.play()
await animation // resolves on completiontarget: HTML/SVG element(s), CSS selector string, motion value, JS object, or single value.values: object of animatable props ({ opacity: 0, x: 100 }), keyframe arrays ({ x: [0, 100, 0] }), or a target value.options: transition configuration (see below).
Options / Props
| Name | Type | Default | Description |
|---|---|---|---|
type | `"tween" \ | "spring" \ | "inertia"` |
duration | number | 0.3 | Length in seconds |
ease | string \ | array \ | function |
delay | number | 0 | Delay before start (seconds); accepts stagger() |
repeat | number | 0 | Repeat count (Infinity for perpetual) |
repeatType | `"loop" \ | "reverse" \ | "mirror"` |
bounce | number | 0.25 | Spring bounciness (0–1) |
stiffness | number | 1 | Spring stiffness |
damping | number | 10 | Spring opposing force |
onUpdate | function | — | Callback with latest value |
Notes
- Animatable props include CSS styles, independent transforms (
x,y,z,rotate,scale), CSS variables ("--rotate"), SVG path props (pathLength), colors, and arbitrary numbers. - Returns
AnimationControls: - Read-only:
duration. - Read/write:
time(seconds),speed(1 = normal, -1 = reverse). - Methods:
play(),pause(),complete(),cancel()(revert to initial),stop()(commit and prevent restart),then(cb).
Related
- scroll
- stagger
- spring
- timeline
- motion-value
hover()
Gesture helper that detects hover start/end, filtering fake touch-emulated hover events.
Signature / Usage
import { hover } from "motion"
hover("button", (element, startEvent) => {
console.log("hover started")
// Returned function fires on hover end
return (endEvent) => {
console.log("hover ended")
}
})
// Cancel all handlers
const cancel = hover("a", callback)
cancel()element: singleElement, array of elements, or CSS selector string.callback:(element, startEvent) => void | ((endEvent) => void). Returning a function runs it on hover end.- Returns a function that cancels all active handlers.
Options / Props
| Name | Type | Default | Description |
|---|---|---|---|
passive | boolean | true | Set false to allow event.preventDefault() |
once | boolean | false | Fire the gesture only once per element |
Notes
- Automatically filters touch-emulated fake hover events and manages event listeners.
Related
- press
- animate
inView()
Detects when elements enter the viewport via IntersectionObserver.
Signature / Usage
import { inView, animate } from "motion"
const stop = inView(
"#carousel li",
(element, enterInfo) => {
animate(element, { opacity: 1 })
// Returned function fires on viewport exit
return (leaveInfo) => {
animate(element, { opacity: 0 })
}
},
{ root: document.querySelector("#carousel") }
)
stop() // stop detectiontarget: selector string,Element, or array of elements.callback:(element, info) => void | ((leaveInfo) => void).infois anIntersectionObserverEntry. Returning a function runs it on exit.- Returns a function that stops detection.
Options / Props
| Name | Type | Default | Description |
|---|---|---|---|
root | Element | window | Scrollable parent used as the viewport |
margin | string | 0 | Viewport boundary adjustment (px/%) |
amount | `"some" \ | "all" \ | number` |
Related
- scroll
- animate
motionValue()
Creates a reactive value that tracks state and velocity of an animated property.
Signature / Usage
import { motionValue, animate } from "motion"
const x = motionValue(0)
const unsubscribe = x.on("change", (latest) => console.log(latest))
animate(x, 100)motionValue(initialValue)returns aMotionValue.- Starting a new animation on the same value automatically ends the previous one.
Options / Props
| Method | Description |
|---|---|
get() | Returns the latest state |
set(value) | Updates the value to a new state |
getVelocity() | Returns latest velocity (0 if non-numerical) |
jump(value) | Moves to new state, resets velocity to 0, ends active animations |
isAnimating() | Returns whether currently animating |
stop() | Terminates the active animation |
on(event, cb) | Subscribes to an event; returns an unsubscribe function |
destroy() | Cleans up subscribers to this value |
Notes
- Events available via
on():change,animationStart,animationCancel,animationComplete.
Related
- animate
- spring
press()
Gesture helper that detects press start/end, with keyboard accessibility.
Signature / Usage
import { press } from "motion"
press("button", (element, startEvent) => {
console.log("pressed:", element)
// Returned function fires on press end
return (endEvent, info) => {
console.log("press", info.success ? "completed" : "cancelled")
}
})element:Element, array of elements, or CSS selector string.callback:(element, startEvent) => void | ((endEvent, info) => void).info.successindicates whether the press completed on the element.- Returns a function that removes all associated event listeners.
Options / Props
| Name | Type | Default | Description |
|---|---|---|---|
passive | boolean | true | Set false to allow event.preventDefault() |
once | boolean | false | Fire the gesture only once per element |
Notes
- Filters secondary pointer events (right-clicks, multi-touch).
- Keyboard accessible by default (Enter key support).
Related
- hover
- animate
Vanilla JS
| Name | Description | Path |
|---|---|---|
| animate | Core function to animate elements, motion values, single values, or objects | animate.md |
| scroll | Link a callback or animation to scroll progress | scroll.md |
| inView | Detect when elements enter the viewport (IntersectionObserver) | in-view.md |
| hover | Hover start/end gesture helper, filters fake touch hover | hover.md |
| press | Press start/end gesture helper, keyboard accessible | press.md |
| stagger | Dynamic delay function offsetting elements sequentially | stagger.md |
| spring | Spring animation generator / transition type | spring.md |
| timeline | Sequence multiple animations via an array of segments | timeline.md |
| motionValue | Reactive value tracking state and velocity | motion-value.md |
scroll()
Links a callback or an animation to scroll progress.
Signature / Usage
import { scroll, animate } from "motion"
// Track scroll progress (0–1)
const cancel = scroll((progress) => {
console.log(progress)
})
// Drive an animation with scroll
const animation = animate(
"div",
{ transform: ["none", "rotate(90deg)"] },
{ ease: "linear" }
)
scroll(animation)
// Horizontal scroll on a specific container
scroll(callback, {
container: document.getElementById("carousel"),
axis: "x",
})
cancel() // stop tracking- First argument: a callback
(progress, info?) => void(progress is 0–1) or ananimate()animation object. - Returns a cleanup function that cancels the scroll link.
Options / Props
| Name | Type | Default | Description |
|---|---|---|---|
container | Element \ | Window | window |
axis | `"x" \ | "y"` | "y" |
target | Element | scrollable area | Element whose progress to track within container |
offset | string[] | ["start start", "end end"] | Intersection points defining tracked region |
trackContentSize | boolean | false | Auto-detect content size changes |
Notes
- When passing an animation, its playback is scrubbed by scroll position rather than time.
offsetstrings combine a target edge and a container edge, e.g."start end".
Related
- animate
- in-view
spring()
Creates a spring animation generator that can be sampled at specific times.
Signature / Usage
import { spring } from "motion"
const generator = spring({ keyframes: [0, 100], bounce: 0.3 })
const { value, done } = generator.next(10) // sample at 10ms- Returns a generator with
.next(timeMs)returning{ value, done }. - Also usable as a transition
type: "spring"withinanimate().
Options / Props
| Name | Type | Default | Description |
|---|---|---|---|
keyframes | [number, number] | required | Start and end values |
duration | number | 800 | Duration in milliseconds |
visualDuration | number | — | Override duration (seconds); when bulk motion completes |
bounce | number | 0.25 | Bounciness from 0 (none) to 1 (extreme) |
stiffness | number | 1 | Spring stiffness; higher = more sudden |
damping | number | 10 | Opposing force; 0 = infinite oscillation |
mass | number | 1 | Mass; higher = more sluggish |
velocity | number | — | Initial velocity |
restSpeed | number | 0.1 | End threshold when speed drops below |
restDelta | number | 0.01 | End threshold when distance drops below |
Notes
- Prefer
bounce+visualDurationfor intuitive tuning, orstiffness/damping/massfor physics-based control.
Related
- animate
- motion-value
stagger()
Produces a dynamic delay function that offsets each animated element sequentially.
Signature / Usage
import { animate, stagger } from "motion"
animate(
"li",
{ opacity: 1 },
{ delay: stagger(0.1) }
)The first <li> waits 0s, the second 0.1s, the third 0.2s, and so on.
duration(number): delay increment in seconds per element.- Returns a delay function compatible with
animate()options.
Options / Props
| Name | Type | Default | Description |
|---|---|---|---|
startDelay | number | 0 | Initial delay before the stagger sequence begins (negative starts mid-way) |
from | `"first" \ | "center" \ | "last" \ |
ease | function \ | string | "linear" |
Related
- animate
- timeline
timeline (animation sequences)
Sequence multiple animations by passing an array of segments to animate().
Signature / Usage
import { animate } from "motion"
const sequence = [
["ul", { opacity: 1 }, { duration: 0.5 }],
["li", { x: [-100, 0] }, { at: 1 }],
["nav", { rotate: 180 }, { at: "<" }],
]
animate(sequence)- Each segment is a tuple
[subject, targetValues, transitionOptions]. - Segments run sequentially by default;
atreschedules timing without changing order. - A second
optionsobject applies global settings (e.g.defaultTransition).
Options / Props
| Name | Type | Description |
|---|---|---|
at | number | Absolute start time (seconds), e.g. { at: 1 } |
at | string label | Start at a named label, e.g. { at: "my-label" } |
at | "<" | Start at the same time as the previous segment |
at | "+0.5" / "-0.2" | Relative to the end of the previous segment |
at | "<0.5" / "<-0.2" | Relative to the start of the previous segment |
duration | number | Segment length in seconds |
delay | number | Initial delay before the segment |
defaultTransition | object | Shared transition for all segments (global options) |
Related
- animate
- stagger
AnimatePresence
Animate components as they mount and unmount from the React tree by wrapping conditional or keyed children in AnimatePresence.
import { AnimatePresence, motion } from "motion/react"
import { useState } from "react"
const slides = [
{ id: 1, title: "Slide One" },
{ id: 2, title: "Slide Two" },
{ id: 3, title: "Slide Three" },
]
export default function Slideshow() {
const [index, setIndex] = useState(0)
const slide = slides[index]
return (
<div>
<AnimatePresence mode="wait">
<motion.div
key={slide.id}
initial={{ opacity: 0, x: 300 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -300 }}
transition={{ ease: "easeOut", duration: 0.4 }}
>
<h2>{slide.title}</h2>
</motion.div>
</AnimatePresence>
<button onClick={() => setIndex((i) => (i + 1) % slides.length)}>
Next
</button>
</div>
)
}Notes
- Every direct child needs a unique, stable
key(use a domain id, never an array index) soAnimatePresencecan detect what entered or left. mode="wait"defers the entering element until the exiting one finishes;"sync"(default) animates both at once;"popLayout"removes exiting elements from layout so siblings can reflow immediately.- Wrap the conditional logic inside
AnimatePresence— do not conditionally renderAnimatePresenceitself, or the exit animation is skipped. - Pass
initial={false}to skip enter animations for children that are present on first render.
Basic Animation
Animate a motion component from an initial state to an animate target, with an exit state for removal.
import { AnimatePresence, motion } from "motion/react"
import { useState } from "react"
export default function Box() {
const [show, setShow] = useState(true)
return (
<>
<button onClick={() => setShow((v) => !v)}>Toggle</button>
<AnimatePresence>
{show && (
<motion.div
initial={{ opacity: 0, scale: 0.8, y: 40 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.8, y: 40 }}
transition={{ duration: 0.4, ease: "easeOut" }}
style={{ width: 120, height: 120, background: "#0af", borderRadius: 12 }}
/>
)}
</AnimatePresence>
</>
)
}Notes
initialis the visual state before mount; passinitial={false}to render directly at theanimatestate without an enter animation.exitonly runs when the element is wrapped inAnimatePresence.- Prefer animating
transform(x,y,scale,rotate) andopacity— they run on the compositor and avoid React re-renders. transitionacceptsduration/easefor tweens ortype: "spring"withstiffness/dampingfor physics.
Gesture Animation
React to hover, tap, and drag interactions with the whileHover, whileTap, whileDrag, and drag props.
import { motion } from "motion/react"
import { useRef } from "react"
export default function GestureCard() {
const constraintsRef = useRef(null)
return (
<motion.div
ref={constraintsRef}
style={{ width: 320, height: 320, background: "#eee", borderRadius: 16 }}
>
<motion.div
// Hover + tap feedback
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9, rotate: 3 }}
// Drag within the parent bounds
drag
dragConstraints={constraintsRef}
dragElastic={0.2}
whileDrag={{ cursor: "grabbing" }}
onTap={() => console.log("tapped")}
transition={{ type: "spring", stiffness: 400, damping: 25 }}
style={{ width: 100, height: 100, background: "#0af", borderRadius: 12 }}
/>
</motion.div>
)
}Notes
whileHover/whileTap/whileDraganimate only for the duration of the gesture, then return toanimate.whileTapis keyboard-accessible automatically:Entertriggers it, and release firesonTap.dragConstraintsaccepts a ref (bounds = that element) or an explicit{ top, left, right, bottom }pixel box;dragElastic(0–1) controls overshoot beyond the bounds.- For touch devices, set the
touch-actionCSS rule on the draggable axis so the page does not scroll while dragging.
Layout Animation
Animate size/position changes automatically with the layout prop, and animate list reordering/removal together with AnimatePresence.
import { AnimatePresence, motion } from "motion/react"
import { useState } from "react"
export default function TodoList() {
const [items, setItems] = useState([
{ id: 1, text: "Design" },
{ id: 2, text: "Build" },
{ id: 3, text: "Ship" },
])
const remove = (id: number) =>
setItems((prev) => prev.filter((i) => i.id !== id))
return (
<ul style={{ listStyle: "none", padding: 0 }}>
<AnimatePresence>
{items.map((item) => (
<motion.li
key={item.id}
layout
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
transition={{ type: "spring", stiffness: 500, damping: 40 }}
onClick={() => remove(item.id)}
style={{ background: "#0af", margin: 8, padding: 12, borderRadius: 8 }}
>
{item.text}
</motion.li>
))}
</AnimatePresence>
</ul>
)
}Notes
layoutmakes the element smoothly animate any layout change (size + position) caused by re-render; uselayout="position"to animate only position and avoid stretching of aspect-changing content.- Layout animations run via CSS
transform, so the surrounding elements reflow without paint-triggering width/height changes. - Combining
layoutwithAnimatePresencelets remaining items slide into place as a sibling is removed — setmode="popLayout"to pull exiting items out of the layout flow during their exit. - Set
border-radiusandbox-shadowthrough thestyleprop so they are scale-corrected during the transition.
samples
| Name | Description | Path |
|---|---|---|
| Basic Animation | Animate a motion component from initial to animate, with an exit state for removal | basic-animation.md |
| Scroll Animation | Drive animations from scroll progress with useScroll, optionally remapping via useTransform | scroll-animation.md |
| Gesture Animation | React to hover, tap, and drag interactions with whileHover, whileTap, whileDrag, and drag | gesture-animation.md |
| Layout Animation | Animate size/position changes with the layout prop, combined with AnimatePresence for lists | layout-animation.md |
| AnimatePresence | Animate components as they mount and unmount by wrapping keyed children in AnimatePresence | animate-presence.md |
Scroll Animation
Drive animations from scroll progress with useScroll, optionally remapping the value via useTransform.
import { motion, useScroll, useTransform } from "motion/react"
import { useRef } from "react"
// 1. Page reading-progress bar (fixed to the top of the viewport)
export function ProgressBar() {
const { scrollYProgress } = useScroll()
return (
<motion.div
style={{
scaleX: scrollYProgress,
originX: 0,
position: "fixed",
top: 0,
left: 0,
right: 0,
height: 4,
background: "#0af",
}}
/>
)
}
// 2. Element-linked reveal: animate as a section passes through the viewport
export function Reveal() {
const ref = useRef(null)
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "center center"],
})
const opacity = useTransform(scrollYProgress, [0, 1], [0, 1])
const y = useTransform(scrollYProgress, [0, 1], [80, 0])
return (
<section ref={ref} style={{ minHeight: "100vh" }}>
<motion.div style={{ opacity, y }}>Revealed on scroll</motion.div>
</section>
)
}Notes
useScroll()with no args tracks the window; passtarget(a ref) to track an element's progress through its scroll container.offsetdefines the progress boundaries:["start end", "center center"]maps progress 0 when the element's start meets the viewport end, to 1 when its center meets the viewport center.- The returned
scrollYProgressis a motion value (0–1) — bind it directly tostyleor remap it withuseTransform/useSpring. - Binding scroll values to
transform,opacity,clipPath, orfilterkeeps the animation GPU-accelerated.
Install
Motion パッケージのインストールと import 方法。
React 向けインストール
npm install motionyarn add motionpnpm add motionReact 18.2 以上が前提。Vite は追加設定不要。
React の import
import { motion } from "motion/react"Next.js App Router では "use client" ディレクティブを付けるか、後述の motion/react-client を import する。
クライアント JS を減らす React import
import * as motion from "motion/react-client""use client" を付けずに利用でき、Server Component 構成でクライアント側 JS を削減できる。
CDN 経由の React import
import motion from "https://cdn.jsdelivr.net/npm/motion@latest/react/+esm"ビルドツールなしの環境向け。
vanilla JS 向けインストール
npm install motionReact と同じ motion パッケージを使用する。
vanilla JS の import
import { animate, scroll } from "motion"animate などの命令的 API を直接 import する。
TypeScript 環境での使用
npm install motionmotion パッケージをインストールするだけで TypeScript からそのまま利用できる。@types/motion 等の追加インストールは不要。
要確認: 型定義がパッケージに同梱されている旨は公式インストールページ (https://motion.dev/docs/react-installation) には明記されていない。追加の型パッケージが不要であることは利用上の前提として記載しているが、公式記載の確認を推奨する。
scripts
| Name | Description | Path |
|---|---|---|
| Install | Motion パッケージのインストールと import 方法 | install.md |