My Journey to 3x Faster Builds: Trimming Barrel File Imports

I maintain a small frontend application (4K LOC) which uses Vite as the compiler. The production build, using npm run build, was taking 26 secs on Github Actions. It seemed awfully slow for such a small application. I decided to investigate why.

I decided to check build time of another project (100K LOC) which uses Vite. I’s build time is also 26 secs. Something is definitely wrong and I want to know why.

We use material-ui in this project. When we started this project, I double checked that top-level import from the material-ui package did not cause bundle size to go up.

import { Button } from "@mui/material";

I wanted to double check the modules that were included in the output generated by Vite. I used the vite-bundle-visualizer and everything seems to be fine. There are no excess modules than what I was roughly using in the material-ui block.

My first guess was that it might be still material-ui package that’s causing the issue. I have read this excellent blog post by Marvin Hagemeister. I removed the top-level import and imported the components directly.

import Button from "@mui/material/Button";

The build time did not change much. I noticed Vite prints the number of modules it transformed and it seemed unusually high.

# small application

vite v4.5.0 building for production...
transforming...
11798 modules transformed.

When I looked at my other application, it printed the following:

# large application

vite v4.5.0 building for production...
transforming...
2894 modules transformed.

We are consuming a lot of modules but barely using any of it and I don’t know what is causing it. My next step was to remove all the routes in the application and check how many modules are being compiled.

vite v4.5.0 building for production...
transforming...
390 modules transformed.

I added one route at a time to find the culprit and I found the problem. It’s the top-level import from mui icons package.

import {
  CheckCircle as CheckCircleIcon,
  Search as SearchIcon,
} from "@mui/icons-material";

I have replaced it with direct icon component imports.

import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import SearchIcon from "@mui/icons-material/Search";

Boom! We are fast again!

vite v4.5.0 building for production...
transforming...
1188 modules transformed.

The build time was down to 8 seconds. Hopefully, once Vite team releases rolldown, it will be ever faster.

Conclusion

Barrel files are terrible for performance whether it’s runtime performance or bundling performance. Now go and kill them from your codebase. Being vigilant and caring about performance will always yield results.