preinitModule
Downloads an ESM module and evaluates it immediately rather than waiting for an import.
preinitModule(href, options)- Added in React 19.0.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$href | string | The URL of the module you want to download and execute. |
$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.
preinitModule fetches an ES module and evaluates it as soon as it has downloaded, without waiting for an import() to ask for it. Its top-level code runs, its own imports are resolved, and any side effects it performs happen right there.
When to reach for it
- A module whose whole purpose is a side effect: registering a custom element, installing a polyfill, starting an error reporter or analytics client.
- Work you want finished before a later interaction, where merely having the bytes cached would still leave evaluation on the critical path.
What trips people up
This executes code. A component that mounts, calls it, and unmounts a moment later has still run the module, and there is no way to undo that. If the user might never reach the feature, prefer preloadModule and let the real import() decide.
Evaluation order is not something you control: it runs whenever the download finishes, which may be before or after other modules. Do not use it to sequence initialization — a module that must run after another should import it.
as must be the literal string "script", the URL must be a real ES module, and it must be the bundler's emitted hashed URL rather than a source path. Duplicate calls for the same href collapse into one, and on the server only calls made during render are honoured.
Example
import { useState } from "react";
import { preinitModule } from "react-dom";
// Emitted by the bundler; the module registers <sc-chart> on evaluation.
const CHART_ELEMENT = "/assets/chart-element-6b0f.js";
function ChartPanel({ series }) {
// Runs the module now so the custom element is defined before markup uses it.
preinitModule(CHART_ELEMENT, { as: "script", crossOrigin: "anonymous" });
return (
<div>
<sc-chart data={JSON.stringify(series)} />
<p>Points plotted: {series.length}</p>
</div>
);
}
export default function Report() {
const [series] = useState([
{ x: 1, y: 12 },
{ x: 2, y: 18 },
{ x: 3, y: 9 },
]);
return (
<section>
<h1>Traffic report</h1>
<ChartPanel series={series} />
</section>
);
}When to use it
- Register a custom element definition before markup that uses the tag is rendered.
- Start an error-reporting client early in the first render of the app shell.
- Install a polyfill module on browsers that need it, decided at render time.
- Evaluate a heavy formatting module while the user is still typing, before its first use.
Frequently asked questions
What is the difference between this and preloadModule?
preloadModule only downloads the module into the browser's module map; this one also evaluates it, running its top-level code and side effects immediately. Choose the download-only hint when the module might never be needed.
Can I rely on the order two modules are evaluated in?
No. Each module runs when its own download finishes, which may be in any order. If one module must run after another, express that with a real import inside the module rather than with two eager hints.
Does unmounting the component undo the module?
No. Once the module downloads it is evaluated, and there is no way to revert its side effects. Only call it at a point where you are certain the module should run.
More examples
Starting an error reporter in the app shell
The reporter is useful only once its top-level code has run, which is exactly the difference between this API and a download hint. Because the call sits in the shell that renders on every page, the deduplication rule means it is issued once no matter how often the shell re-renders. The integrity hash makes the browser reject a tampered copy rather than executing it.
import { preinitModule } from "react-dom";
const REPORTER = "/assets/error-reporter-4d19.js";
export default function AppShell() {
// The module installs window.onerror handlers when it evaluates,
// so it must run, not merely be cached.
preinitModule(REPORTER, {
as: "script",
integrity: "sha384-4hCkH0FhMJ0oNaVUwPqk0aH8lqOaK1qz9SVAvV0RTV0O2p3vN1o0EmZ0oJUcKz3P",
});
return (
<div>
<h1>Console</h1>
<p>Errors thrown after this point are reported.</p>
</div>
);
}Loading a polyfill only where it is needed
The feature test runs at render time, so browsers with native popover support never download the polyfill at all, while the rest fetch and evaluate it before the menu is interacted with. The guard also keeps the call out of server rendering, where HTMLElement does not exist. Nothing here waits for the polyfill: evaluation happens when the download completes, so the first click may still land slightly before it.
import { preinitModule } from "react-dom";
const POPOVER_POLYFILL = "/assets/popover-polyfill-9c2e.js";
function supportsPopover() {
// On the server there is no HTMLElement and nothing to polyfill, so treat
// that case as "supported" and let the client decide after hydration.
return (
typeof HTMLElement === "undefined" || "popover" in HTMLElement.prototype
);
}
export default function Menu() {
if (!supportsPopover()) {
preinitModule(POPOVER_POLYFILL, { as: "script" });
}
return (
<div>
<button popovertarget="actions">Actions</button>
<div id="actions" popover="auto">
<p>Rename</p>
<p>Delete</p>
</div>
</div>
);
}
Discussion