Why a tiny demo ships 71 KB of JavaScript
Most pages are assembled from many pieces, yet the default approach is to send all of the application’s JavaScript up front. For a small utility page that sorts three numbers, that initial transfer should be nearly nothing. Looking at the Networkpanel before any optimization shows 71.2 KB of JavaScript for exactly that demo. In src/index.js the entire lodash package is imported even though only one of its methods, sortBy, is ever called.
Trim the bundle: import less
There are several ways to shrink this payload:
- Write a small custom sort.
- Use native
Array.prototype.sort(). - Import only
lodash.sortByrather than the entire library. - Fetch the sorting code only when the user clicks the button.
The first two options are the most practical for a real app, but the last two are better for demonstrating general techniques. Start by swapping the package.json dependency from the full lodash package to lodash.sortby:
Then in src/index.js, import that specific module and adjust the sort call to use it directly.
Reloading and checking the Network panel again shows the bundle dropped by more than 4× with just that one change. There is still room to improve, though.
Split work by route and by action
webpack bundles all JavaScript modules into static files the browser can read. A single bundle can be split into two chunks:
- One holding the code for the initial route.
- A second chunk containing the sorting code.
Dynamic import() creates that secondary chunk and lazy-loads it only when needed. For this app, the sort module should load when the button is pressed.
Remove the top-level import for the sort method from src/index.js:
Then move the import into the button’s event listener:
import() is part of a TC39 proposal for dynamic module import, and webpack already follows its syntax. The call returns a promise that, once resolved, provides the selected module in a separate chunk. In this case module.default references lodash’s default export, which is then passed to a sortInput method inside a chained .then(). A .catch() at the end of the chain handles any rejection.
Finally, define sortInput at the end of the file as a function that returns another function. The inner function accepts the imported lodash.sortBy method, sorts the three input values, and updates the DOM.
Verify the split in the Network panel
After the changes, reload the app. The Network panel now shows only a small initial bundle on load. Once the button is pressed, the secondary chunk containing the sorting code is fetched and executed, and the numbers still sort correctly.
Considerations before applying code splitting
User feedback for lazy regions
If an action triggers a large module download, users on slower connections may think the page stalled. A loading indicator is worth adding whenever the split chunk is significant.
Vendor bundles vs. lazy chunks
Lazy loading third-party code is not always the right move. Dependencies that rarely change are usually better placed in a separate, cacheable vendor bundle using webpack’s SplitChunksPlugin.
Framework-level abstractions
Frameworks built on webpack typically provide higher-level lazy-loading APIs that are safer than manually scattering dynamic imports. Angular, React Router, and Vue Router each ship their own approach. Use what the framework recommends once you understand the underlying dynamic import mechanism.
Resource hints
Where critical modules exist, browser hints like <link rel="preload"> and <link rel="prefetch"> can pull them in earlier. webpack supports these via magic comments inside import statements.
Code is not the only lazy-loading candidate
Below-the-fold images can dominate page weight too. Native image lazy loading or a library such as Lazysizes can speed up rendering the same way code splitting does for scripts.



