We use cookies to enhance your experience on the site
CodeWorlds
Back to collections
Guide12 min read

Motion, the animation library that changed its name

Framer Motion is now Motion, imported from motion/react. Variants, layout animations, gestures, performance, accessibility, and migrating from the old package.

Motion, the animation library that changed its name

Framer Motion became independent and is now called Motion. The npm package is motion and the React import goes through motion/react. The interface stayed the same, so moving to the new version usually comes down to swapping the package name and import paths.

The library itself solves a problem that is tiresome in plain CSS: animating elements as they appear and disappear, animating layout changes, and tying all of it to gestures. The engine combines JavaScript with native browser mechanisms, so animations run through the graphics card where possible.

The basics

An animated element is an ordinary tag behind a prefix, and you supply values as properties.

Code
Bash
pnpm add motion
Code
TypeScript
import { motion } from 'motion/react'

export function Card({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      initial={{ opacity: 0, y: 12 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.25, ease: 'easeOut' }}
    >
      {children}
    </motion.div>
  )
}

Three properties describe the whole thing: initial state, target state, and how to get there. That covers most entrance animations, which in practice make up the bulk of the work.

The default transition type depends on the value being animated. Transforms, meaning position, scale, and rotation, get a spring, while other values such as opacity or colour get a timing curve lasting 0.3 seconds. That split is deliberate: a spring responds to interruption midway and changes direction smoothly, while a duration based animation must either finish or jump, and on a plain fade that difference does not matter.

Elements leaving the tree

This is the real reason people reach for this library. An element removed from the React tree disappears instantly, and plain CSS cannot animate that without tricks.

Code
TypeScript
import { AnimatePresence, motion } from 'motion/react'

<AnimatePresence>
  {open && (
    <motion.div
      key="panel"
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      Panel content
    </motion.div>
  )}
</AnimatePresence>

The wrapper keeps the element in the tree until the exit animation finishes. The key is mandatory here, and its absence is the most common reason an exit animation simply does not run.

For lists where items arrive and leave, set the waiting mode so the departing element finishes before a new one enters. Otherwise rapid switching overlaps two elements.

The same mechanism handles transitions between views. Page content wrapped in this, with a key based on the address, gives a smooth transition on navigation instead of an instant swap.

Layout animations

A mechanism hard to reproduce yourself. An element with the right property animates the transition between positions when the layout changes, even when the change comes from reordering an array.

Code
TypeScript
{tasks.map((task) => (
  <motion.li key={task.id} layout>
    {task.title}
  </motion.li>
))}

Sorting a list stops being a jump and becomes a slide. The library measures position before and after the change, then reproduces the transition with a transform, so the animation runs through the graphics card rather than through layout recalculation.

A related mechanism is a shared layout identifier, letting you animate between two different elements as though they were one. A thumbnail expanding into a full view is the typical use.

A practical warning: layout animation is expensive across hundreds of elements at once. On a long list, animate only the visible portion or drop the effect, since the visual gain does not offset the stutter.

Variants

With three animated elements, properties on each become unreadable. Variants let you name states and drive a whole subtree from one place.

Code
TypeScript
const list = {
  hidden: { opacity: 0 },
  visible: { opacity: 1, transition: { staggerChildren: 0.06 } }
}

const item = {
  hidden: { opacity: 0, y: 8 },
  visible: { opacity: 1, y: 0 }
}

<motion.ul variants={list} initial="hidden" animate="visible">
  {entries.map((e) => (
    <motion.li key={e.id} variants={item}>{e.name}</motion.li>
  ))}
</motion.ul>

The state name passes down the tree automatically, so children need no control properties of their own. A delay between children produces a cascade with one parameter rather than by computing delays by hand.

That approach gains as things grow. Adding an "error" state to a whole form comes down to one more entry in the variants object rather than changing ten components.

Performance

There is one rule and it follows directly from how a browser works. Animating opacity and transforms is cheap, since it happens outside layout recalculation. Animating width, height, margins, and position is expensive, since it forces a layout pass every frame.

Code
TypeScript
<motion.div animate={{ scale: 1.05 }} />

<motion.div animate={{ width: 320 }} />

The first version stays smooth across dozens of elements; the second starts stuttering with a handful. If you must animate size, use scaling or a layout animation rather than changing the dimension directly.

The second thing is bundle size. Importing an animated component pulls the whole engine along, so in a project where animation lives on one page it is worth loading dynamically. The library also offers a lighter variant built on the browser's native animation interface, sufficient for simple transitions.

The third is how many elements animate at once. Twenty elements with layout animation during scrolling is usually too many, and the user notices only the visible ones anyway.

Gestures and dragging

The library handles gestures as element properties, so hover, press, and drag need no pointer event wiring.

Code
TypeScript
<motion.div
  whileHover={{ scale: 1.03 }}
  whileTap={{ scale: 0.97 }}
  drag="x"
  dragConstraints={{ left: -120, right: 0 }}
  onDragEnd={(_, info) => {
    if (info.offset.x < -80) remove()
  }}
/>

That fragment is a ready swipe pattern familiar from mobile applications. Constraints bound the movement, and the drag end information carries both offset and velocity, so the decision to act can rest on either.

Account for velocity rather than offset alone. A quick short movement usually signals intent to act, so a threshold based purely on distance makes the interface feel unresponsive.

With gestures on touch devices, mind the collision with page scrolling. Vertical dragging on a list the user wants to scroll ends with neither working well, so restrict dragging to one axis.

Scroll driven animation

A separate set of mechanisms handles animation tied to scroll position. The simplest variant fires an animation when an element enters the viewport.

Code
TypeScript
<motion.section
  initial={{ opacity: 0, y: 24 }}
  whileInView={{ opacity: 1, y: 0 }}
  viewport={{ once: true, amount: 0.3 }}
>
  {content}
</motion.section>

Setting it to fire once matters more than it appears. Without that, a section animates on every scroll past it in either direction, which on a long page turns into flicker.

The second variant binds an animated value directly to scroll progress, supporting progress bars and parallax effects. The same performance rule applies: bind progress to opacity or a transform, never to an element's height.

On pages with many such sections, check the result on a weaker device. An effect that looks smooth on a desktop can stutter on a three year old phone badly enough to be worth dropping.

Accessibility

Some people enable a reduced motion preference in their system, and that is not a matter of taste but sometimes of health. The library exposes a hook reading that setting.

Code
TypeScript
import { useReducedMotion } from 'motion/react'

const reduceMotion = useReducedMotion()

<motion.div
  initial={{ opacity: 0, y: reduceMotion ? 0 : 12 }}
  animate={{ opacity: 1, y: 0 }}
/>

The right approach is not disabling every animation but replacing movement with an opacity change. The element still appears smoothly without travelling across the screen.

Separately, keep animation from blocking interaction. A second long transition looks impressive on first use and irritates on the fiftieth, so for frequently used elements stay within one hundred fifty to two hundred fifty milliseconds.

Migrating from the old package

Moving from the previous name is simple and in most projects needs no changes to animation code.

Code
Bash
pnpm remove framer-motion
pnpm add motion

You change imports from the previous name to motion/react and that is usually all. Version twelve introduced no compatibility breaking changes on the React side, so code written earlier keeps working.

Check two things while you are there. The first is compatibility with your React version, since newer releases are built for concurrent rendering. The second is animated components in third party libraries, which may still pull the old package, leaving both in the project at once.

Checking the second takes one package manager command listing what pulls the old name. If it is a dependency you do not control, waiting for its update usually suffices, since two versions living side by side cause no errors, only an unnecessarily larger bundle.

Review your own animation wrapper components separately. In projects that have passed through several versions there are usually a few places with settings chosen ad hoc, and while migrating it is easier to unify durations and easing curves than to return to that as its own task.

Where animation helps and where it hinders

The library offers capabilities easy to overuse. Animation has two jobs: explaining what just happened and showing the relationship between two states. Everything beyond that is decoration costing the user time.

Good uses are predictable. An item joining a list should appear rather than blink into place, otherwise the user cannot tell whether anything was added. A panel sliding in from the side shows where it came from, making closing it obvious. Sorting with a slide lets the eye follow an item that changed position.

Bad uses are predictable too. Animation on an element used dozens of times a day turns into delay. A scroll effect on a content page pulls attention away from reading. A second long transition when opening a modal makes an application feel slow even when everything else is instant.

A simple test: disable the animation and check whether the interface became confusing. If so, the animation carries information and deserves to stay. If it merely became less flashy, it can go without loss.

In a project built on a ready component library, HeroUI or daisyUI for instance, some animation already exists. Adding a second layer on the same elements usually ends in a double transition nobody planned.

Motion against the alternatives

OptionStrengthWeaknessPick it when
MotionExit and layout animations, gestures, variantsBundle size, cost across many elementsReact application with interface animation
CSS transitionsNo cost, nativeNo exit or layout animationsSimple hovers and state changes
GSAPSequences and timeline controlOutside the React modelComplex narrative animation
Tailwind CSS classesNo extra libraryLimited capabilitySmall transitions in an existing project

Consider the second row seriously before reaching for a library. A hover, a colour change, and a smooth appearance of an element that never leaves the tree are jobs for plain CSS. A library starts paying off with exit animations, layout changes, and gestures.

Outside that table sits the case where the output should be a file rather than movement on a page. Remotion renders React components to video, so the same skills produce a recording you can send or post on a social platform. That carries two costs: rendering takes time and compute, and the licence depends on company size, staying free only up to three people in an organisation.

Common mistakes

The first is a missing key on an element inside the exit wrapper. The animation simply does not run and nothing tells you why.

The second is animating width and height rather than scale. The effect looks the same and costs many times more, since every frame forces a layout pass.

The third is animations that run too long. What impresses on first run slows the work down in daily use.

The fourth is ignoring the reduced motion preference. It is a system setting chosen deliberately and worth respecting by replacing movement with an opacity change.

The fifth is layout animation on a long list. Across hundreds of elements the browser cannot keep up, and the user sees stutter instead of smoothness.

The sixth is keeping two versions of the library after an incomplete migration. The old package pulled in by a third party dependency doubles the size and can cause behaviour that is hard to explain.

FAQ

Is Framer Motion the same as Motion?

Yes, it is the same library after a rename and after becoming independent of the company it started in. The package is now motion and the React import goes through motion/react. The interface stayed compatible, so migration is usually a swap of name and import paths.

Is it worth using instead of CSS?

It depends on the task. Handle hover and state change transitions with plain CSS, since they cost nothing. Take the library for exit animations, layout changes, and gestures, meaning things CSS cannot do without tricks.

How does it affect bundle size?

Importing an animated component includes the animation engine, which on a single animated page is worth loading dynamically. The library also offers a lighter variant built on the browser's native interface, sufficient for simple transitions without gestures or layout animation.

Does it work with Next.js?

Yes, though animated components need the client directive, since they use state and effects. In Next.js with the app router that means extracting the animated part into a separate client component rather than marking the whole page.

How long should an interface animation last?

A range of one hundred fifty to two hundred fifty milliseconds suits most transitions. Shorter can go unnoticed, longer starts slowing down work on elements used repeatedly through the day.

Documentation sits on the project site, and the changes appear in the changelog.