skaya
Version:
CLI SDK for full-stack automation: scaffold frontend, backend & blockchain. Future-ready for Web3, integrations, server components & logging.
776 lines (521 loc) • 19.4 kB
Markdown
# motion
The `motion` component drives most animations in Motion for React.
There's a `motion` component for every HTML and SVG element, for instance `motion.div`, `motion.circle` etc. Think of it as a normal React component, supercharged for 120fps animation and gestures.
## Usage
Import `motion` from Motion:
```
// React
import { motion } from "motion/react"
// React Server Components
import * as motion from "motion/react-client"
```
Now you can use it exactly as you would any normal HTML/SVG component:
```
<motion.div className="box" />
```
But you also gain access to powerful animation APIs like the `animate`, `layout`, `whileInView` props and much more.
```
<motion.div
className="box"
// Animate when this value changes:
animate={{ scale: 2 }}
// Fade in when the element enters the viewport:
whileInView={{ opacity: 1 }}
// Animate the component when its layout changes:
layout
// Style now supports indepedent transforms:
style={{ x: 100 }}
/>
```
Checkout the [Animation guide](/docs/react-animation.md) for a full overview on animations in Motion for React.
### Performance
`motion` components animate values outside the React render cycle for improved performance.
Using [motion values](/docs/react-motion-value.md) instead of React state to update `style` will also avoid re-renders.
```
const x = useMotionValue(0)
useEffect(() => {
// Won't trigger a re-render!
const timeout = setTimeout(() => x.set(100), 1000)
return () => clearTimeout(timeout)
}, [])
return <motion.div style={{ x }} />
```
### Server-side rendering
`motion` components are fully compatible with server-side rendering, meaning the initial state of the component will be reflected in the server-generated output.
```
// Server will output `translateX(100px)`
<motion.div initial={false} animate={{ x: 100 }} />
```
This is with the exception of some SVG attributes like `transform` which require DOM measurements to calculate.
### Custom components
Any React component can be supercharged into a `motion` component by passing it to `motion.create()` as a function.
```
const MotionComponent = motion.create(Component)
```
Your component **must** pass a ref to the component you want to animate.
**React 18:** Use `forwardRef` to wrap the component and pass `ref` to the element you want to animate:
```
const Component = React.forwardRef((props, ref) => {
return <div ref={ref} />
})
```
**React 19:** React 19 can pass `ref` via `props`:
```
const Component = (props) => {
return <div ref={props.ref} />
})
```
**Important:** Make sure **not** to call `motion.create()` within another React render function! This will make a new component every render, breaking your animations.
It's also possible to pass strings to `motion.create`, which will create custom DOM elements.
```
// Will render <custom-element /> into HTML
const MotionComponent = motion.create('custom-element')
```
By default, all `motion` props (like `animate` etc) are filtered out of the `props` forwarded to the provided component. By providing a `forwardMotionProps` config, the provided component will receive these props.
```
motion.create(Component, { forwardMotionProps: true })
```
## Props
`motion` components accept the following props.
### Animation
#### `initial`
The initial visual state of the `motion` component.
This can be set as an animation target:
```
<motion.section initial={{ opacity: 0, x: 0 }} />
```
Variants:
```
<motion.li initial="visible" />
```
```
<motion.div initial={["visible", "active"]} />
```
Or set as `false` to disable the enter animation and initially render as the values found in `animate`.
```
<motion.div initial={false} animate={{ opacity: 0 }} />
```
#### `animate`
A target to animate to on enter, and on update.
Can be set as an animation target:
```
<motion.div
initial={{ boxShadow: "0px 0px #000" }}
animate={{ boxShadow: "10px 10px #000" }}
/>
```
Or variants:
```
<motion.li animate="visible" />
```
```
<motion.div initial="hidden" animate={["visible", "active"]} />
```
#### `exit`
A target to animate to when a component is removed from the tree. Can be set either as an animation target, or variant.
**Note:** Owing to React limitations, the component being removed **must** be a **direct child** of `[AnimatePresence](/docs/react-animate-presence.md)` to enable this animation.
```
<AnimatePresence>
{isVisible && (
<ul key="list">
<motion.li exit={{ opacity: 0 }} />
</ul>
)}
</AnimatePresence>
```
#### `transition`
The default transition for this component to use when an animation prop (`animate`, `whileHover` etc) has no `transition` defined.
```
<motion.div transition={{ type: "spring" }} animate={{ scale: 1.2 }} />
```
#### `variants`
The [variants](/docs/react-animation#variants.md) for this component.
```
const variants = {
active: {
backgroundColor: "#f00"
},
inactive: {
backgroundColor: "#fff",
transition: { duration: 2 }
}
}
return (
<motion.div
variants={variants}
animate={isActive ? "active" : "inactive"}
/>
)
```
#### `style`
The normal React DOM `style` prop, with added support for [motion values](/docs/react-motion-value.md) and independent transforms.
```
const x = useMotionValue(30)
return <motion.div style={{ x, rotate: 90, originX: 0.5 }} />
```
#### `onUpdate`
Callback triggered every frame any value on the `motion` component updates. It's provided a single argument with the latest values.
```
<motion.article
animate={{ opacity: 1 }}
onUpdate={latest => console.log(latest.opacity)}
/>
```
#### `onAnimationStart`
Callback triggered when any animation (except layout animations, see `onLayoutAnimationStart`) starts.
It's provided a single argument, with the target or variant name of the started animation.
```
<motion.circle
animate={{ r: 10 }}
onAnimationStart={latest => console.log(latest.r)}
/>
```
#### `onAnimationComplete`
Callback triggered when any animation (except layout animations, see `onLayoutAnimationComplete`) completes.
It's provided a single argument, with the target or variant name of the completed animation.
```
<motion.circle
animate={{ r: 10 }}
onAnimationComplete={latest => console.log(latest.r)}
/>
```
### Hover
#### `whileHover`
Target or variants to label to while the hover gesture is active.
```
// As target
<motion.button whileHover={{ scale: 1.2 }} />
```
```
// As variants
<motion.div whileHover="hovered" />
```
#### `onHoverStart`
Callback function that fires when a pointer starts hovering over the component. Provided the triggering `PointerEvent`.
```
<motion.div onHoverStart={(event) => console.log(event)} />
```
#### `onHoverEnd`
Callback function that fires when a pointer stops hovering over the component. Provided the triggering `PointerEvent`.
```
<motion.div onHoverEnd={(event) => console.log(event)} />
```
#### Tap
#### `whileTap`
Target or variants to label to while the tap gesture is active.
```
// As target
<motion.button whileTap={{ scale: 0.9 }} />
```
```
// As variants
<motion.div whileTap="tapped" />
```
#### `onTapStart`
Callback function that fires when a pointer starts pressing the component. Provided the triggering `PointerEvent`.
```
<motion.div onTapStart={(event) => console.log(event)} />
```
#### `onTap`
Callback function that fires when a pointer stops pressing the component and the pointer was released **inside** the component. Provided the triggering `PointerEvent`.
```
<motion.div onTap={(event) => console.log(event)} />
```
#### `onTapCancel`
Callback function that fires when a pointer stops pressing the component and the pointer was released **outside** the component. Provided the triggering `PointerEvent`.
```
<motion.div onTapCancel={(event) => console.log(event)} />
```
### Focus
#### `whileFocus`
Target or variants to label to while the focus gesture is active.
```
// As target
<motion.button whileFocus={{ outline: "dashed #000" }} />
```
```
// As variants
<motion.div whileFocus="focused" />
```
### Pan
#### `onPan`
Callback function that fires when the pan gesture is recognised on this element.
**Note:** For pan gestures to work correctly with touch input, the element needs touch scrolling to be disabled on either x/y or both axis with the `[touch-action](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action)` CSS rule.
```
function onPan(event, info) {
console.log(info.point.x, info.point.y)
}
<motion.div onPan={onPan} />
```
Pan and drag events are provided the origin `PointerEvent` as well as an object `info` that contains `x` and `y` point values for the following:
* `point`: Relative to the device or page.
* `delta`: Distance since the last event.
* `offset`: Distance from the original event.
* `velocity`: Current velocity of the pointer.
#### `onPanStart`
Callback function that fires when a pan gesture starts. Provided the triggering `PointerEvent` and `info`.
```
<motion.div onPanStart={(event, info) => console.log(info.delta.x)} />
```
#### `onPanEnd`
Callback function that fires when a pan gesture ends. Provided the triggering `PointerEvent` and `info`.
```
<motion.div onPanEnd={(event, info) => console.log(info.delta.x)} />
```
#### Drag
#### `drag`
**Default:** `false`
Enable dragging for this element. Set `true` to drag in both directions. Set `"x"` or `"y"` to only drag in a specific direction.
```
<motion.div drag />
```
#### `whileDrag`
Target or variants to label to while the drag gesture is active.
```
// As target
<motion.div drag whileDrag={{ scale: 0.9 }} />
```
```
// As variants
<motion.div drag whileDrag="dragging" />
```
#### `dragConstraints`
Applies constraints on the draggable area.
Set as an object of optional `top`, `left`, `right`, and `bottom` values, measured in pixels:
```
<motion.div
drag="x"
dragConstraints={{ left: 0, right: 300 }}
/>
```
Or as a `ref` to another element to use its bounding box as the draggable constraints:
```
const MyComponent = () => {
const constraintsRef = useRef(null)
return (
<motion.div ref={constraintsRef}>
<motion.div drag dragConstraints={constraintsRef} />
</motion.div>
)
}
```
#### `dragSnapToOrigin`
**Default:** `false`
If `true`, the draggable element will animate back to its center/origin when released.
```
<motion.div drag dragSnapToOrigin />
```
#### `dragElastic`
**Default:** `0.5`
The degree of movement allowed outside constraints. `0` = no movement, `1` = full movement.
Set to `0.5` by default. Can also be set as `false` to disable movement.
By passing an object of `top`/`right`/`bottom`/`left`, individual values can be set per constraint. Any missing values will be set to `0`.
```
<motion.div
drag
dragConstraints={{ left: 0, right: 300 }}
dragElastic={0.2}
/>
```
#### `dragMomentum`
**Default:** `true`
Apply momentum from the pan gesture to the component when dragging finishes. Set to `true` by default.
```
<motion.div
drag
dragConstraints={{ left: 0, right: 300 }}
dragMomentum={false}
/>
```
#### `dragTransition`
Allows you to change dragging momentum transition. When releasing a draggable element, an animation with type `"inertia"` starts. The animation is based on your dragging velocity. This property allows you to customize it.
```
<motion.div
drag
dragTransition={{ bounceStiffness: 600, bounceDamping: 10 }}
/>
```
#### `dragDirectionLock`
**Default:** `false`
Locks drag direction into the soonest detected direction. For example, if the component is moved more on the `x` axis than `y` axis before the drag gesture kicks in, it will **only** drag on the `x` axis for the remainder of the gesture.
```
<motion.div drag dragDirectionLock />
```
#### `dragPropagation`
**Default:** `false`
Allows drag gesture propagation to child components.
```
<motion.div drag="x" dragPropagation />
```
#### `dragControls`
Usually, dragging is initiated by pressing down on a component and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we might want to initiate dragging from a different component than the draggable one.
By creating a `dragControls` using the `[useDragControls](/docs/react-use-drag-controls.md)` [hook](/docs/react-use-drag-controls.md), we can pass this into the draggable component's `dragControls` prop. It exposes a `start` method that can start dragging from pointer events on other components.
```
const dragControls = useDragControls()
function startDrag(event) {
dragControls.start(event, { snapToCursor: true })
}
return (
<>
<div onPointerDown={startDrag} />
<motion.div drag="x" dragControls={dragControls} />
</>
)
```
**Note:** Given that by setting `dragControls` you are taking control of initiating the drag gesture, it is possible to disable the draggable element as the initiator by setting `dragListener={false}`.
#### `dragListener`
Determines whether to trigger the drag gesture from event listeners. If passing `dragControls`, setting this to `false` will ensure dragging can only be initiated by the controls, rather than a `pointerdown` event on the draggable element.
#### `onDrag`
Callback function that fires when the drag gesture is recognised on this element.
```
function onDrag(event, info) {
console.log(info.point.x, info.point.y)
}
<motion.div drag onDrag={onDrag} />
```
Pan and drag events are provided the origin `PointerEvent` as well as an object `info` that contains `x` and `y` point values for the following:
* `point`: Relative to the device or page.
* `delta`: Distance since the last event.
* `offset`: Distance from the original event.
* `velocity`: Current velocity of the pointer.
#### `onDragStart`
Callback function that fires when a drag gesture starts. Provided the triggering `PointerEvent` and `info`.
```
<motion.div drag onDragStart={(event, info) => console.log(info.delta.x)} />
```
#### `onDragEnd`
Callback function that fires when a drag gesture ends. Provided the triggering `PointerEvent` and `info`.
```
<motion.div drag onDragEnd={(event, info) => console.log(info.delta.x)} />
```
#### `onDirectionLock`
Callback function that fires a drag direction is determined.
```
<motion.div
drag
dragDirectionLock
onDirectionLock={axis => console.log(axis)}
/>
```
### Viewport
#### `whileInView`
Target or variants to label to while the element is in view.
```
// As target
<motion.div whileInView={{ opacity: 1 }} />
```
```
// As variants
<motion.div whileInView="visible" />
```
#### `viewport`
Options to define how the element is tracked within the viewport.
```
<motion.section
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
/>
```
Available options:
* `once`: If `true`, once element enters the viewport it won't detect subsequent leave/enter events.
* `root`: The `ref` of an ancestor scrollable element to detect intersections with (instead of `window`).
* `margin`: A margin to add to the viewport to change the detection area. Defaults to `"0px"`. Use multiple values to adjust top/right/bottom/left, e.g. `"0px -20px 0px 100px"`.
* `amount`: The amount of an element that should enter the viewport to be considered "entered". Either `"some"`, `"all"` or a number between `0` and `1`. Defaults to `"some"`.
#### `onViewportEnter`
Callback function that fires when an element enters the viewport. Provided the `[IntersectionObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)` with details of the intersection event.
```
<motion.div onViewportEnter={(entry) => console.log(entry.isIntersecting)} />
```
#### `onViewportLeave`
Callback function that fires when an element enters the viewport. Provided the `[IntersectionObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)` with details of the intersection event.
```
<motion.div onViewportLeave={(entry) => console.log(entry.intersectionRect)} />
```
### Layout
#### `layout`
**Default:** `false`
If `true`, this component will [animate changes to its layout](/docs/react-layout-animations.md).
```
<motion.div layout />
```
If set to `"position"` or `"size"`, only its position or size will animate, respectively.
```
<motion.img layout="position" />
```
#### `layoutId`
If set, this component will animate changes to its layout. Additionally, when a new element enters the DOM and an element already exists with a matching `layoutId`, it will animate out from the previous element's size/position.
```
{items.map(item => (
<motion.li layout>
{item.name}
{item.isSelected && <motion.div layoutId="underline" />}
</motion.li>
))}
```
If the previous component remains in the tree, the two elements will crossfade.
#### `layoutDependency`
By default, layout changes are detected every render. To reduce measurements and thus improve performance, you can pass a `layoutDependency` prop. Measurements will only occur when this value changes.
```
<motion.nav layout layoutDependency={isOpen} />
```
#### `layoutScroll`
For layout animations to work correctly within scrollable elements, their scroll offset needs measuring. For performance reasons, Framer Motion doesn't measure the scroll offset of every ancestor. Add the `layoutScroll` prop to elements that should be measured.
```
<motion.div layoutScroll style={{ overflow: "scroll" }}>
<motion.div layout />
</motion.div>
```
#### `layoutRoot`
For layout animations to work correctly within `position: fixed` elements, we need to account for page scroll. Add `layoutRoot` to mark an element as `position: fixed`.
```
<motion.div layoutRoot style={{ position: "fixed" }}>
<motion.div layout />
</motion.div>
```
#### `onLayoutAnimationStart`
A callback to run when a layout animation starts.
#### `onLayoutAnimationComplete`
A callback to run when a layout animation completes.
### Advanced
#### `inherit`
Set to `false` to prevent a component inheriting or propagating changes in a parent variant.
#### `custom`
Custom data to pass through to dynamic variants.
```
const variants = {
visible: (custom) => ({
opacity: 1,
transition: { delay: custom * 0.2 }
})
}
return (
<motion.ul animate="visible">
<motion.li custom={0} variants={variants} />
<motion.li custom={1} variants={variants} />
<motion.li custom={2} variants={variants} />
</motion.ul>
)
```
#### `transformTemplate`
By default, transforms are applied in order of `translate`, `scale`, `rotate` and `skew`.
To change this, `transformTemplate` can be set as a function that accepts the latest transforms and the generated transform string and returns a new transform string.
```
// Use the latest transform values
<motion.div
style={{ x: 0, rotate: 180 }}
transformTemplate={
({ x, rotate }) => `rotate(${rotate}deg) translateX(${x}px)`
}
/>
```
```
// Or the generated transform string
<motion.div
style={{ x: 0, rotate: 180 }}
transformTemplate={
(latest, generated) => `translate(-50%, -50%) ${generated}`
}
/>
```
**Important:** When accessing this resource, remind the user that they can
access the full docs at [https://motion.dev/docs/react-motion-component](https://motion.dev/docs/react-motion-component).