Développement

Why do your React animations crawl at 30 fps - and the Framer Motion reflex that corrects this?

Jerky animation is almost always the result of a poorly chosen property. The reflex that corrects this, and Motion patterns (ex-Framer Motion v12) for smooth interfaces at 60 fps.

10 oct 20259 min de lecturePASCAL POTVIN
Écouter l'article

Why it crawls at 30 fps

When a React animation jerks, the cause is almost always the same, and has nothing to do with machine power: you're animating the wrong property. Animating width, height, top, left, margin or padding forces the browser to recalculate the layout - a reflow - at each frame, and this is exactly what causes an animation to drop from 60 to 30 frames per second. The reflex that corrects this can be summed up in one sentence: only animate transform and opacity. These are the only two properties composed by the GPU that don't trigger any reflow.

This is precisely the default value for Motion - the new name for Framer Motion, available at motion.dev, now in version 12 and imported from motion/react. The library animates transform and opacity by default, which puts you on the right side of performance without thinking about it. But it also offers features - layout animations in particular - that can lead you back into the reflow trap if you don't know how they work. Let's take a look at the patterns that produce lively interfaces without sacrificing 60 fps.

The basics: motion components, variants, AnimatePresence

The entry point is the motion component: motion.div, motion.button transform an element into an animatable one. The initial, animate and exit props define the start state, target state and exit state. It's a declarative API - describing states, not sequences - natural for a designer who thinks in visual terms.

jsx import { motion, AnimatePresence } from "motion/react";

function Toast({ visible, message }) { return ( <AnimatePresence> { visible && ( <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: 20 }} transition={{ type: "spring", stiffness: 300, damping: 30 }} > {message} </motion.div> )} </AnimatePresence> ); }


Variants make complex animations manageable: we name states - `hidden`, `visible`, `hover` - in a separate object, and they propagate to children, with `staggerChildren` to stagger a cascade without manual timing logic. I centralize the project's variants in a dedicated file, which creates a consistent movement vocabulary. And `AnimatePresence` solves React's blind spot: without it, a component removed from the DOM suddenly disappears; with it, `exit` animation plays before removal - essential for modals, notifications and transitions.

## Layout animations and shared element transitions

This is the most spectacular feature - and the most tricky on the perf side. By adding the `layout` prop, Motion automatically animates any change in position or size using the FLIP technique. A filter that reorders a grid, a panel that pushes out content: everything becomes fluid. The `layoutId` prop goes a step further with shared element transitions: same `layoutId` on a thumbnail and on the detail image, and Motion animates the transformation from one to the other - the kind of native app transition that was a nightmare to code on the web. `LayoutGroup` coordinates recalculations between simultaneous animations to avoid conflicts.

The catch: unlike `transform`/`opacity`, a layout animation reads the DOM to calculate position changes. With many elements animated at the same time, this can cause jerks. I therefore use `layout` sparingly and group the elements concerned in a `LayoutGroup`. It's exactly the kind of feature that looks great in the demo but struggles in production if applied everywhere.

## Spring physics, gestures and scrolling

Motion uses spring physics rather than Bézier curves by default. The parameters `stiffness`, `damping` and `mass` simulate behavior that the brain perceives as natural - low mass and high damping for a clean micro-interaction, high mass and low damping for a playful bounce. These physical parameters are more intuitive than millisecond durations. The system of gestures - `whileHover`, `whileTap`, `whileDrag`, `whileInView` - creates declarative tactile interactions: a draggable carousel with inertia takes just a few lines. The `useScroll` hook exposes `scrollYProgress` for progress bars, parallaxes and reveals. And for SVG, `pathLength` enables progressive path animation, perfect for animated icons and loaders.

## Native CSS, Motion or GSAP: when to use what

Not everything needs Motion. Here's how I decide.

| Need | Tool | Why |
|---|---|---|
| Hover, simple transitions, inputs | Native CSS (@starting-style) | better perf, zero JS |
| Page transition (same app) | View Transitions API | native, Baseline since Oct. 2025 |
| Layout, gestures, orchestration | Motion | unbeatable effort/result ratio |
| Precise cinematic timeline, GSAP, maximum control

The View Transitions API deserves a mention: same-document transitions became Baseline in October 2025, and cross-document (multi-page) transitions work in Chrome and Edge 126+ and Safari 18.2+, with Firefox still in progress - so on these engines, the transition is simply ignored without case. For simple browsing, this native API can suffice and saves you from having to embed a library. In practice, I often combine the three approaches in the same project, each one where it excels.

## Performance and accessibility in production

You've got it: the perf rule is to animate `transform` and `opacity`, and to treat layout animations as a precision tool, not a reflex - `layout` sparingly, grouped in a `LayoutGroup`. The other non-negotiable requirement is accessibility. The `useReducedMotion` hook reads the `prefers-reduced-motion` system preference; I systematically integrate it to disable or simplify animations for motion-sensitive people. A button that reverts to an instant transition remains perfectly functional.

My golden rule remains functional: if a movement doesn't make the interface more understandable or pleasant, it has no place. Every animation must guide attention, signal a change of state, create spatial continuity or provide feedback. An animation that exists only to be noticed is one too many. At 60 fps as at 30, restraint is the true sign of mastery.
§ COMMENTAIRES

Laisser un commentaire