Module preloading: filling the gap left by preload
Module-based code splits an application into smaller, cacheable pieces and lets you prioritize what the browser fetches first. But modules introduce a chicken-and-egg problem: the browser can't know a module's dependencies until that module has actually been fetched and parsed. That serialization leaves the connection idle while one file loads, then another, and so on down the dependency chain.
What preload gets right (and wrong)
<link rel="preload"> exists precisely to solve this kind of problem. Declaratively listing a resource in the HTML head lets the browser start a high-priority download immediately, rather than discovering the resource several roundtrips later. This works well for assets hidden deep inside CSS, like font files.
For modules, though, preload has two shortcomings.
First, credentials modes can cause mismatches. A <script type="module"> without a crossorigin attribute uses a credentials mode of omit—a mode that doesn't exist for <link rel="preload">. If the modes don't match, the resource gets fetched twice, wasting bandwidth and adding latency. You could align the crossorigin values across both tags, but you may not control the tags for modules that are dependencies of other modules.
Second, fetching isn't the whole job. After download, the browser must parse and compile the code. V8 handles modules differently from classic scripts, and preload gives the browser no signal that the file is a module. All it can do is cache the file; parsing and compilation wait until something actually loads the module.
modulepreload: a preload that understands modules
<link rel="modulepreload"> addresses both issues. Because the link type is specific to modules, the credentials mode defaults work without needing to coordinate crossorigin attributes across your module graph.
It also lets the browser parse and compile the module as soon as the fetch completes, instead of deferring that work until the module is about to execute.
Support is solid across current browsers: Chrome 66+, Edge 79+, Firefox 115+, and Safari 17+.
Handling the dependency tree
The modulepreload spec allows browsers to optionally fetch the entire dependency tree of a requested module, not just the module itself. Browsers aren't required to do this, so you can't rely on it everywhere.
The cross-browser safe approach is to declare the module alongside a flat list of all its dependencies. Browsers that do follow the dependency tree will deduplicate modules, so listing the full set explicitly won't cause double-fetching—it just ensures nothing is missed by browsers that don't recurse.
Is it worth it?
Preloading shines when deep dependency trees leave the browser idle during network roundtrips. Declaring the flat module list upfront keeps the connection busy and can meaningfully cut load time in that scenario.
That said, module loading performance is still evolving. Profile your application with Developer Tools before adding preloads, and in the meantime consider splitting your bundle into a few larger chunks. Ongoing module work in Chrome is steadily reducing the advantages that bundlers have held, but they're not obsolete yet.



