T
all articles

Boosting Framer Motion Performance & Accessibility 🚅💨

image of the author - Łukasz Fiuk

Łukasz Fiuk

5 minutes read ⏳

Back in the good ol' days, we had to choose between fast websites or nicely animated websites. Well, those days are over, and we can have it all now 😎. And to make life even sweeter, we can also optimize accessibility of our animations with single prop 🌈

Quick note: Framer motion has great guides on that topic, so you might want to check those out instead 💀

Step 1: Replace motion tags 🐥

Framer Motion is full of features, and to be fair you're most likely not using some of them, but there's high chance that you are loading it all with motion tags 🤷‍♂️. Let's fix that by replacing all motion (33KB zipped) tags with m (7KB zipped) tag 🔥. Why m tag is so much lighter? Because it's not loading any features by default - you have to specify what, and how to load. More about that in next step. ⏭️

💾 >_ tsx
import { motion } from "framer-motion"
import { m } from "framer-motion"

//  Before
<motion.button>
   I'm just a button
<motion.button>

// After
<m.button>
   I'm super cool button ✨
<m.button>

Pro tip: use vscode "search" Shift ⌘ F functionality to find and replace all motion instances.

Step 2: Lazy Load needed features 🦋

Replacing all your motion tags with m will break your animations as we are missing essential features. To fix that, we must first re-export the necessary Framer Motion features, as shown below:

💾 >_ tsx
// Used for lazy-loading Framer motion features.
// Docs: https://www.framer.com/motion/guide-reduce-bundle-size/#lazy-loading

import { domAnimation } from "framer-motion";
export default domAnimation;

There are currently two feature packages you can load:

  • domAnimation: This provides support for animations, variants, exit animations, and tap/hover/focus gestures. (+15kb)
  • domMax: This provides support for all of the above, plus pan/drag gestures and layout animations. (+25kb)

Now let's put it all together. Open your top level component, eg. _app.tsx load features and wrap your app with <LazyMotion/>.

💾 >_ tsx
import { LazyMotion } from "framer-motion"

// Make sure to return the specific export containing the feature bundle.
const loadFeatures = () =>
  import("./features.js").then(res => res.default)

// This animation will run when loadFeatures resolves.
export const  App = () => {
  return (
    <LazyMotion features={loadFeatures} strict>
            <Component {...pageProps} /> 
    </LazyMotion>
  )
}

Strict prop will throw errors ⚠️ if you, or anyone in your team will try lo load motion tags anywhere in your app. That's good because from now on you'd like to use lighter and optimized m tag.

Step 3: Optimizing Accessibility ♿

Since your app is already blazingly fast, we might spend a minute to optimize accessibility. 🌈 Adding <MotionConfig/> with reducedMotion="user" prop, will disable all animations except opacity and background-color for users who prefers reduced animations.

💾 >_ tsx
import { LazyMotion, MotionConfig } from "framer-motion";

export const App = () => {
  return (
    <MotionConfig reducedMotion="user">
      <LazyMotion features={loadFeatures} strict>
        <Component {...pageProps} />
      </LazyMotion>
    </MotionConfig>
  );
};

Wrapping Up 🍬

That's is folks - your animations are now fast, inclusive and sweet. 🍩 Happy Hecking! ✨

0

Comments 💬

Building for Accessibility and Performance: The Story of Heckerspace 🌚🌝