preloadModule
Tells the browser to download an ESM module ahead of time without evaluating it.
preloadModule(href, options)- Added in React 19.0.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$href | string | The URL of the module you want to download. |
$options | object | Options describing the module and how to fetch it. |
$options.as | 'script' | Required. Must be 'script'. |
$options.crossOriginoptional | 'anonymous' | 'use-credentials' | The CORS policy to use. |
$options.integrityoptional | string | A cryptographic hash of the module, used to verify its authenticity. |
$options.nonceoptional | string | A cryptographic nonce that allows the module under a strict Content Security Policy. |
Return value
voidNothing.
Changelog
- PHP 19.0.0Shipped in the stable react-dom release; previously available only in Canary builds.
preloadModule is the ES module counterpart of a plain download hint. React emits <link rel="modulepreload">, so the browser fetches the module — and, in most browsers, its static dependency graph — and parks it in the module map without evaluating anything.
When to reach for it
- A lazily loaded route or dialog you expect the user to open: fetch the chunk on hover, then let the eventual
import()resolve from cache. - Module chunks that only become discoverable after a first module has been parsed, which the browser's preload scanner cannot see.
What trips people up
as must be the literal string "script"; there is no separate module value, and the target really has to be an ES module. Pointing it at a classic script gives you a cached file the browser will re-request in a different mode when it is actually used.
Pass the emitted URL, not a source path. Bundlers rename and hash chunks, so a hint for ./routes/Settings.jsx matches nothing at runtime and the browser warns that the preload went unused. Get the real URL from your bundler's manifest.
Nothing here executes: module top-level code runs only when something imports it. Use preinitModule if you want the module evaluated on arrival. Repeated calls with the same href collapse into one, and on the server only calls made during render are honoured.
Example
import { useState, lazy, Suspense } from "react";
import { preloadModule } from "react-dom";
// The bundler emits this hashed URL; take it from the build manifest.
const SETTINGS_CHUNK = "/assets/SettingsDialog-8f21c4.js";
const SettingsDialog = lazy(() => import("./SettingsDialog.js"));
function OpenSettingsButton({ onOpen }) {
function warm() {
preloadModule(SETTINGS_CHUNK, { as: "script", crossOrigin: "anonymous" });
}
return (
<button onMouseEnter={warm} onFocus={warm} onClick={onOpen}>
Settings
</button>
);
}
export default function Toolbar() {
const [open, setOpen] = useState(false);
return (
<div>
<h1>Project</h1>
<OpenSettingsButton onOpen={() => setOpen(true)} />
{open && (
<Suspense fallback={<p>Loading settings…</p>}>
<SettingsDialog onClose={() => setOpen(false)} />
</Suspense>
)}
</div>
);
}When to use it
- Fetch a lazily imported settings dialog's chunk when the user hovers the button that opens it.
- Warm the chunk for the next step of a wizard while the current step is being filled in.
- Preload a module that is only imported after a first module has run, so the preload scanner never sees it.
- Fetch an editor module during idle time on a page where most users will eventually edit.
Frequently asked questions
Why must as be "script" for a module?
That is the only accepted value — React uses it to emit <link rel="modulepreload">, which already implies the module type. There is no separate "module" value to pass.
Does this evaluate the module's top-level code?
No. The module is downloaded and placed in the module map, but its code runs only when something imports it. Use preinitModule when you want it evaluated as soon as it arrives.
Why does the browser say my module preload was unused?
Almost always because the href does not match the URL the runtime later imports. Bundlers hash chunk filenames, so pass the emitted URL from the build manifest rather than a source path.
More examples
Preloading the next step of a flow
On each render only the immediately following chunk is hinted, so the browser never fetches the whole wizard up front. Because duplicate calls with the same href are collapsed, re-rendering the same step costs nothing. When Continue is clicked the dynamic import for that step resolves from the module map instead of hitting the network.
import { useState } from "react";
import { preloadModule } from "react-dom";
const STEP_CHUNKS = {
details: "/assets/step-details-1a2b.js",
payment: "/assets/step-payment-3c4d.js",
confirm: "/assets/step-confirm-5e6f.js",
};
const ORDER = ["details", "payment", "confirm"];
export default function Wizard() {
const [index, setIndex] = useState(0);
const next = ORDER[index + 1];
// While the user works on this step, fetch the one after it.
if (next) {
preloadModule(STEP_CHUNKS[next], { as: "script" });
}
return (
<div>
<h2>Step {index + 1}: {ORDER[index]}</h2>
<p>Next chunk queued: {next ?? "none"}</p>
<button disabled={!next} onClick={() => setIndex(index + 1)}>
Continue
</button>
</div>
);
}Emitting hints from a build manifest during server rendering
The hints are issued while a component renders, which is the only context server rendering honours, so React hoists the modulepreload tags into the streamed head. The browser downloads reports-b91f.js and charts-2ad0.js in parallel with the HTML, and the hydration import finds them already in the module map. Reading URLs from the manifest is what keeps the hints matching the hashed filenames the build actually emitted.
import { preloadModule } from "react-dom";
// Generated by the bundler: route id -> emitted chunk URLs.
const MANIFEST = {
"/reports": ["/assets/reports-b91f.js", "/assets/charts-2ad0.js"],
"/billing": ["/assets/billing-77ce.js"],
};
function RouteModules({ route }) {
for (const url of MANIFEST[route] ?? []) {
preloadModule(url, { as: "script" });
}
return null;
}
export default function Document({ route = "/reports" }) {
return (
<div>
<RouteModules route={route} />
<h1>Reports</h1>
<p>The chunks for this route are fetched alongside the HTML.</p>
</div>
);
}
Discussion