Edit Page

#Reduce bundle size

Learn what makes up Motion's bundle size, and how to reduce it.

As of version 2.9, the full bundle size for Framer Motion is 27.6kb. Thanks to code-splitting, it's likely that this isn't the actual weight you'll be adding to your bundle size.

For example, if you are only importing AnimatePresence and usePresence to implement your own exit animations with CSS, you'd only add 1.4kb to your bundle.

import { AnimatePresence, usePresence } from "framer-motion" // 1.4kb

However, the main import is the motion component and, by default, when you use it you add around 23kb to your bundle.

import { motion } from "framer-motion" // 23kb

Framer Motion's simple, props-driven API is the reason for this extra size. Unused features aren't code-split, as it isn't currently possible to detect which features you're using at build time.

However, Motion offers APIs that allow you to create loading strategies so that you only ever import what you need. In 2.9, employing these strategies makes it possible to reduce the motion import size down to 12.5kb, and future versions will be able to reduce this even further.

#How to reduce bundle size

Instead of importing motion, import the slim m component. You can do this without changing your code by importing it as motion.

import { m as motion } from "framer-motion"

By default, m has no knowledge of features like exit animations or drag. They can be loaded manually via the MotionConfig component. This lets you define only the features you use, and provide them to m components via React context, ensuring unused motion features are not bundled into your code.

import {
  MotionConfig,
  DragFeature,
  ExitFeature,
} from "framer-motion"

export function App({ children }) {
  return (
    <MotionConfig features={[DragFeature, ExitFeature]}>
      {children}
    </MotionConfig>
  )
}

#Available features

The features you can import from framer-motion are:

  • AnimationFeature: For animate prop support. This includes variants and animation controls.
  • ExitFeature: For exit prop support.
  • DragFeature: For drag prop support.
  • GesturesFeature: For all gesture callbacks including whileHover and whileTap.
  • AnimateLayoutFeature: For layout and layoutId prop support.

#Dynamic feature loading (experimental)

By using m and MotionConfig you can reduce bundle size by only importing the features you need. But these features are being added to your initial bundle size, even if you don't need their functionality straight away.

Motion supports the progressive enhancement of m components by adding new items to the features prop even after it's mounted.

When and how you add new features is up to you.

One way you can implement this is with the dynamic import function and a bundler that supports it (like Webpack).

Make a file with an array of features as its default export.

// motion-features.js
import { AnimationFeature } from "framer-motion"

export default [AnimationFeature]

Then, in your component, you can use import(), useState and useEffect to import this file after the component has made an initial render.

// index.js
function App() {
  const [features, setFeatures] = useState([])

  useEffect(() => {
    import("./motion-features").then(res => {
      setFeatures(res.default)
    })
  }, [])

  return (
    <MotionConfig features={features}>
      <Component />
    </MotionConfig>
  )
}

In this example, we're only importing the AnimationFeature. Any m components that should perform an animation when they mount will do so when AnimationFeature loads.

import { m as motion } from "framer-motion"

export function Component() {
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
    />
  )
}

You can even mix static and dynamic imports to load some features with a higher priority than others.

<MotionConfig features={[AnimationFeature, ...features]}>