nextui icon indicating copy to clipboard operation
nextui copied to clipboard

[Feature Request] Async framer-motion loading

Open luzat opened this issue 1 year ago • 4 comments

Is your feature request related to a problem? Please describe.

Just adding a NextUI button increases bundle size a lot. framer-motion contributes 212 KB (uncompressed) in a production build here. It would be nice if a that could be reduced or loaded asynchronously.

A second problem is, that Framer Motion is also be loaded if animations are disabled with disableAnimation={true}.

Describe the solution you'd like

According to the framer Motion guide it's possible to load features asynchronously using a dynamic import. For many use cases quick initial loading would be preferable over having animations loaded right away.

#2929 already did most of the work required from what I can tell. In a test, I did replace the domAnimation in the ripple effect by a dynamic port and Vite did split the 212 KB framer-motion chunk into 59 KB to be loaded initially and a large 150 KB (or 45 KB with Brotli) chunk that can be loaded asynchronously. I feel that this is a huge amount. Also, if the dynamic import is never invoked when disableAnimation={true}, this code would never have to be downloaded.

I do not know if any other components would not work with that pattern or if there are cases when you might want to load the features synchronously. There could be at least two ways to go about this:

  • Allow users to choose between a dynamic or static import: possibly by importing and using a separate package which reexports domAnimation as a dynamic/static import and pass that to NextUI?)
  • Allow users to at least await the dynamic import: either a setting or just an await import('@nextui/dom-animation') or similar

Even if code splitting won't work in all cases/components, this might be a helpful optimization for some projects.

Describe alternatives you've considered

  • usage of a smaller library
  • dynamically importing all of NextUI; problematic and impractical

Screenshots or Videos

No response

luzat avatar Jun 27 '24 12:06 luzat

Thanks for the info. We're also investigating this issue as well.

wingkwong avatar Jun 27 '24 13:06 wingkwong

@luzat can you also share what tool you used and how you recorded the size?

wingkwong avatar Jun 28 '24 14:06 wingkwong

@wingkwong I set up a basic Remix v2 project with Vite and mostly default settings. I manually added NextUI according to the documentation with single components (only Button and its animation dependencies). I added a single button to the _index route.

I checked the bundles that were generated with NODE_ENV=production npx vite-bundle-visualizer. The _index route included 212 KB from framer-motion. After patching the ripple effect to dynamically import domAnimation the _index route shrank by 150 KB and a separate chunk of that size, which contained a large part of framer-motion, was created instead. The _index route still dynamically loaded that chunk, but did not need that code for the initial render.

I did not test with disabled animations, but in that case the import should have been skipped, given that the animation is included conditionally, and 45 to 150 KB saved.

luzat avatar Jun 28 '24 15:06 luzat

I seem to be getting this error: Warning: React does not recognize the disableAnimation prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase disableanimation instead. If you accidentally passed it from a parent component, remove it from the DOM element. The reason is caused by the Avatar component 截屏2024-07-01 16 52 45

xiaoluoer avatar Jul 01 '24 08:07 xiaoluoer

@xiaoluoer this is another issue which I've fixed already. See here. The fix will be available in the next bug fix release.

wingkwong avatar Jul 01 '24 10:07 wingkwong