refine
refine copied to clipboard
[FEAT] Reduce bundle size in Next.js (with Material UI)
Is your feature request related to a problem? Please describe.
The bundle size of my Refine app built with Next.js and Material UI is very big and the app is very slow:
┌ /_app 0 B 551 kB
├ λ /[[...refine]] 249 B 551 kB
└ ○ /404 184 B 551 kB
+ First Load JS shared by all 551 kB
├ chunks/framework-305dbdef56d67aa9.js 45.4 kB
├ chunks/main-a8eaa706e962a5d6.js 27.9 kB
├ chunks/pages/_app-1e8eb2bf899090b0.js 476 kB
└ chunks/webpack-44567bfec3a37904.js 1.05 kB
I think there is room for improvement.
Describe alternatives you've considered
No response
Additional context
No response
Describe the thing to improve
1.Out of curiosity, I removed the resource pages from the _app.tsx
file to see the results. It significantly reduced the bundle size:
┌ /_app 0 B 292 kB
├ λ /[[...refine]] 249 B 292 kB
└ ○ /404 184 B 292 kB
+ First Load JS shared by all 292 kB
├ chunks/framework-305dbdef56d67aa9.js 45.4 kB
├ chunks/main-a8eaa706e962a5d6.js 27.9 kB
├ chunks/pages/_app-432b4edb6ea69894.js 217 kB
└ chunks/webpack-44567bfec3a37904.js 1.05 kB
The fact that I have to import all resource pages in the _app.tsx
file to pass them to <Refine>
has a significant effect on the bundle size and speed of my app. I wish there was a way to at least lazy load them.
- I have to import Material UI components using named imports:
import { Button } from "@pankod/refine-mui";
But the Material UI docs suggests to import the components using default imports to improve bundle size in dev mode:
import Button from "@pankod/refine-mui/Button";
This is not possible with @pankod/refine-mui
because it only re-exports mui components with named export.
- Next.js recently introduced a new compiler option,
modularizeImports
that automatically converts named imports to default imports:
// before:
import { Delete } "@mui/icons-material"
// after:
import Delete "@mui/icons-material/Delete"
To enable it I just need to add this to my next.config.js
:
module.exports = {
modularizeImports: {
"@mui/icons-material": {
transform: "@mui/icons-material/{{member}}",
},
},
};
This is very handy for packages like @mui/icons-material
. It significantly reduces the compile time in dev mode. It doesn't effect the production build because @mui/icons-material
is side-effect free (at least this is what I've been told, I don't have deep knowledge about how bundles work).
It also could be used with @mui/material
but not your version of mui (@pankod/refine-mui/
) so 1) we can't reduce compile time in dev mode with this option 2) it potentially effects the production build because @pankod/refine-mui
is not side-effect free(?)
Again, I'm not very familiar with this topic. These are just my raw thoughts.
I'm willing to do anything I can do to help you on this because I'm currently blocked by this.
I made some progress by dynamically importing resource pages:
import dynamic from "next/dynamic";
const ArticleList = dynamic(() =>
import("../components/pages/articles/list").then((mod) => mod.ArticleList)
);
I think it's worth being documented. Also I get a Typescript error where I use the dynamically imported component that says:
1. Type 'ComponentType<IResourceComponentsProps<GetListResponse<Article>, { [key: string]: any; }, ILogData>>' is not assignable to type 'FunctionComponent<IResourceComponentsProps<any, any, ILogData>> | undefined'.
Type 'ComponentClass<IResourceComponentsProps<GetListResponse<Article>, { [key: string]: any; }, ILogData>, any>' is not assignable to type 'FunctionComponent<IResourceComponentsProps<any, any, ILogData>>'.
Type 'ComponentClass<IResourceComponentsProps<GetListResponse<Article>, { [key: string]: any; }, ILogData>, any>' provides no match for the signature '(props: IResourceComponentsProps<any, any, ILogData>, context?: any): ReactElement<any, any> | null'. [2322]
I would probably make a PR to fix those but I'm pretty buzy right now.
Hey @ahhshm, sorry for the issue and thank you for sharing your progress here.
About the type incompatibility, that's an easy fix, don't know why but we've defined the action property types of the resources with React.FunctionalComponent
rather than React.ComponentType
😅 We can deploy a fix for it very soon to resolve that.
Can you also share the bundle size change you get by using next/dynamic
? 🙏
For the bundle size issue, I'll investigate this one to make refine packages comply with the bundlers to reduce the file size, goal is to make them all side effect free and a better way to pass resource components so we don't include them in everywhere of the app 🚀
Hey @aliemir
We can deploy a fix for it very soon to resolve that.
That's great. Thank you.
Can you also share the bundle size change you get by using next/dynamic?
This is the bundle size after using next/dynamic
Route (pages) Size First Load JS
┌ /_app 0 B 426 kB
├ λ /[[...refine]] 249 B 426 kB
└ ○ /404 184 B 426 kB
+ First Load JS shared by all 426 kB
├ chunks/framework-305dbdef56d67aa9.js 45.4 kB
├ chunks/main-a8eaa706e962a5d6.js 27.9 kB
├ chunks/pages/_app-251183cf1f82c0cf.js 351 kB
└ chunks/webpack-fdea33a90021617c.js 2.22 kB
It's about 130kb smaller and the app feels slightly faster. Still, it's very big compared to how relatively small my app is.
For the bundle size issue, I'll investigate this one to make refine packages comply with the bundlers to reduce the file size, goal is to make them all side effect free and a better way to pass resource components so we don't include them in everywhere of the app
Thank you. Looking forward to it.
Hey @ahhshm , We've released the ComponentType fix (@pankod/[email protected])
Hey @ahhshm and everyone interested in this issue. We're aware of the situation and will try to do some changes to reduce the bundle sizes in all of the packages and release but the major change will happen with the refine@4 since the main changes are going to be breaking ones and all the features/changes we want to do for v4 is not ready yet (including the bundle size related ones).
We'll try to share the roadmap and our target for the v4 and its release in the following days 🙏
Hey @ahhshm , With refine v4, we removed re-exports and updated the routing structure. Now your refine apps have a much smaller bundle size! https://refine.dev/blog/refine-v4-announcement/#reduced-bundle-size