Skip to content

[FEATURE] Reducing bundle size #254

@mattgperry

Description

@mattgperry

Currently Framer Motion is around 24kb gzipped and minified as a whole. With tree-shaking the motion component alone is about 22kb.

This isn't unprecedented, with Pose being around 25kb and GSAP being in the low 30s. But a low bundle size is quite (either the American or British "quite") important to me, and I've had this thought in my head for a while that it's daft that the animation part of an animation library has to load synchronously.

I've done some experiments and I think it'd be possible to crank the core library down to 10kb. The rest could either be loaded synchronously with the main bundle, but as optional bits (for instance someone who only wants to use animations). Or using import() the rest could be loaded in asynchronously.

For ease of use I'd like the main entry point to remain the same, with something like motion/lazy as a safe second entry point for people who want to crank down the bundle size.

Potential savings

Functionality: ~5kb

Functionality is loaded in via renderless components so we don't run, for instance, draggable code on components that aren't draggable.

These are already abstracted and loaded into a single function. It could be that we allow this to be configured and loaded in via the currently private MotionPluginContext API. So only the functionality used in the site is loaded at all.

A further step could be that users could dynamically import this configuration, re-rendering MotionPluginContext and its subscribers when functionality has downloaded. This could be used to defer the loading of dynamic functionality until after the initial render has finished.

Popmotion animations: ~4.5kb

At this point all animations have been abstracted behind a single function that returns a promise. As all composition also works via Promises it might work quite neatly that calls to startAnimation occuring before Popmotion has loaded in simply wait for it to load before running.

Element names: ~0.7kb

Currently motion components are created by iterating over two lists of tag names. Pose used to use a Proxy to do this without maintaining lists but I had to replace it because Googlebot would crash on Pose-powered pages. Proxy is now supported by Googlebot, which means we can support it too.

This would also theoretically reduce memory usage and start times, and allow us to support arbitrary web components out of the box.

Value conversion: ~2kb

Motion performs a step of preprocessing on animation definitions to make the unanimatable animatable.

Specifically, it reads CSS variable definitions from the DOM and makes them numbers/colors, and it detects incompatible from/tos like "100vh" -> "100px" and converts those to pixel values by measuring the DOM. This function is only called when we start an animation. This wouldn't be as neat to abstract as startAnimation as it doesn't currently live behind a Promise. But ValueAnimationControls.animate does - and maybe we can do something there to defer until this functionality is present.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfeatureNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions