Finding late-discovered critical assets
Some resources are only fetched after the browser has already parsed and executed significant chunks of JavaScript. That was the case in the sample application used for this exercise: main.css is not declared in the HTML document's <link> tags. Instead, a separate JavaScript file, fetch-css.js, attaches that link element to the DOM only after the window.onLoad event fires. Similarly, the K2D.woff2 web font specified inside main.css only begins downloading once the CSS file itself has finished loading.
A Lighthouse performance audit on the live page flags this exact problem under a failed audit for a late-fetched resource. The critical request chain—the order in which the browser prioritizes and fetches resources—reveals the issue:
├─┬ / (initial HTML file)
└── fetch-css.js
└── main.css
└── K2D.woff2
Because the CSS file sits on the third level of that chain, Lighthouse identifies it as a late-discovered resource.
Preload hints for resources needed now
For assets that are critical to the initial render but are discovered late, a link preload tag tells the browser to start fetching them sooner. Add the preload declaration to the <head> of the document:
<head>
<!-- ... -->
<link rel="preload" href="main.css" as="style">
</head>
The as attribute tells the browser what type of resource is being fetched; for stylesheets, the value is as="style".
After reloading the application and inspecting the Network panel, note that the CSS file is now requested before the JavaScript that originally fetched it has even finished parsing. The preload hint triggers a preemptive fetch for a resource the page is assumed to need immediately.
Preload can backfire if applied carelessly. In this project, details.css is another stylesheet at the root level, but it's only used on a separate /details route. Adding a preload hint for that file causes an unnecessary request:
<head>
<!-- ... -->
<link rel="preload" href="main.css" as="style">
<link rel="preload" href="details.css" as="style">
</head>
As expected, the Network tab shows a request for details.css even though the current page never uses it. Chrome also logs a warning in the Console panel when a preloaded resource is not consumed within a few seconds of page load. Use that warning as a signal to remove hints for assets that aren't needed right away.
For a reference to all resource types and the correct as attribute values, see the MDN article on preloading content.
Prefetching for future navigation
Prefetch is a separate browser hint for assets that are only required on a different navigation route. These requests run at a lower priority than critical resources for the current page.
In the sample app, clicking the main image navigates to a details/ route whose styles all live in details.css. Add a prefetch link to index.html:
<head>
<!-- ... -->
<link rel="prefetch" href="details.css">
</head>
With DevTools open and the Disable cache option unchecked, reload the page. The Network panel shows a very low priority request for details.css issued after all other files have finished loading.
Navigating to the details page by clicking the image still triggers a request for details.css from details.html—but the resource is served from the browser's disk cache. By exploiting idle browser time, prefetch gets the asset cached early, so the subsequent navigation is much faster. A similar effect can be seen for any network request that would otherwise block the destination page.
webpack integration
Webpack 4.6.0 and later support preloading and prefetching for dynamically imported chunks. To demonstrate, consider a simple app that calls a Lodash method after a user submits a form. The relevant code in src/index.js looks like this:
form.addEventListener("submit", e => {
e.preventDefault()
import('lodash.sortby')
.then(module => module.default)
.then(sortInput())
.catch(err => { alert(err) });
});
Code splitting already reduces the initial payload. Adding a prefetch for the dynamic import ensures the chunk is available at browser idle time, so there's no fetch delay when the user actually presses the button:
form.addEventListener("submit", e => {
e.preventDefault()
import(/* webpackPrefetch: true */ 'lodash.sortby')
.then(module => module.default)
.then(sortInput())
.catch(err => { alert(err) });
});
After bundling, webpack injects a prefetch tag into the document head, visible in the Elements panel. The Network panel confirms the chunk is fetched at low priority after all other resources.
The same webpack mechanism supports the webpackPreload comment parameter for cases where preloading a chunk makes more sense:
import(/* webpackPreload: true */ 'module')
When to use each hint
Both preload and prefetch should be applied selectively; using them for the wrong resources can degrade performance by creating requests that are never used.
- Use preload for resources that are critical to the current page but only get discovered late.
- Use prefetch for resources a user will likely need on a future route or action.
Support for both hints is not universal across browsers, so not every user will see the same improvement. Consult compatibility data for preload and prefetch to understand the reach of these optimizations in your audience.



