Bundle Size: When Runtime Dependencies Defeat Tree Shaking

Tree Shaking is static, but your app is dynamic. It can fail when a component imports a large dependency for an optional, runtime-configured feature. A UI library's provider might pull in an entire form library, even if you don't use it.
Why it exists
Developers strive for minimal JavaScript bundles to improve web performance. Tools like Tree Shaking were created to automatically remove unused code during the build process. However, these static tools have limitations, creating the need for developers to understand why they sometimes fail and how to diagnose the resulting bundle size bloat.
The mental model
Think of Tree Shaking as a librarian who, before opening for the day, discards all books not referenced in the main catalog. But if one book has a note saying, "See the entire 'Advanced Physics' section if the reader asks for it," the librarian must keep that whole section on the shelf, just in case. The runtime condition ("if the reader asks") defeats the static analysis (the catalog check).
How it works
A bundler's Tree Shaking is a static compilation process. It analyzes import and export statements to determine which code is unused. The problem arises with runtime conditions. For instance, a UI library's <ConfigProvider> might import a form validation library to handle an optional prop. Even if the code using that import is inside a conditional block like if (props.useForms), the bundler can't know the value of props.useForms at build time. To be safe, it must include the imported form library in the final bundle.
When to use it
You should perform bundle size analysis whenever you notice an unexpected increase in your application's size, especially after adding or updating a dependency. Use a tool like webpack-bundle-analyzer to create a visual map of your bundle. This makes it easy to spot large, unexpected libraries that have been pulled in transitively. This is particularly common with large, multi-functional components or global providers.
When not to use it
As an architectural pattern, avoid creating components that couple many optional features through a single interface with runtime flags. This design is hostile to static analysis. A better approach is to decompose dependencies. Instead of a single provider, offer more specific providers, like a FormConfigProvider. This makes dependency usage explicit, allowing developers to only import (and pay the bundle size cost for) the features they actually use.
One canonical example
An application uses the Ant Design UI library for buttons and modals but not for any forms. After building the app, the developer is surprised to find rc-field-form, a form library, in their production bundle. The cause is the top-level <ConfigProvider> component. By default, it imports the form library to be ready to handle an optional form configuration prop, even if one is never passed. This defeats Tree Shaking and bloats the bundle for every user of the application.
Interview question
Why does Tree Shaking often fail to remove large, optional dependencies pulled in by runtime-configured features?
- a.The imported dependency uses CommonJS modules, which are less amenable to tree shaking.
- b.Tree Shaking is a static process and cannot evaluate code paths determined by runtime conditions or props.Correct
- c.The bundler's configuration is not optimized to aggressively remove dead code.
- d.The component's package.json file is missing the "sideEffects": false declaration.
Why? this is the answer
The card explains that Tree Shaking is a static process, meaning it operates at build time and cannot predict dynamic runtime conditions or the values of props. Therefore, it must include code paths that *might* be used, even if they are optional. While missing "sideEffects: false" can prevent tree shaking, the specific problem described is the conflict between static analysis and dynamic runtime decisions.
Just read this? Test yourself on what you have been reading.
Read the original → ant.design
- #bundle size
- #tree shaking
- #performance
- #webpack
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.
See open roles