A script with type=module loads a JavaScript module; a nomodule script can provide a separate classic fallback for older engines.
HTML module scripts: load one module without ordering every classic script
Use it for a real task
A receipt dashboard needs a small enhancement after the document is parsed. The module path carries the current implementation. The fallback path is an actual separate build, not the same file renamed: a browser that does not support modules may run it instead. Essential receipt content is already present in HTML, so either script can fail without blanking the page.
<main>
<h1>Receipt queue</h1>
<p><a href="/receipts/R-47">Open receipt R-47</a></p>
</main>
<script type="module" src="/assets/receipt-queue.mjs"></script>
<script nomodule src="/assets/receipt-queue-legacy.js"></script>What the markup guarantees
Both asset paths must be deployed if both branches are advertised. Modules have their own loading and scope rules; do not assume a global variable from a classic script is available as an imported binding.
Cost and limits
Two builds increase maintenance and asset storage. If the supported browser set needs only modules, omit the legacy branch rather than shipping a fallback nobody uses.
Common Mistakes
- Do not mark a module file as a classic nomodule fallback.
- Do not rely on scripts for the page's primary text.
- Do not assume module and classic execution order is interchangeable.
Connected lessons
- HTML script loading: choose ordered defer or independent async work
- HTML noscript: keep the essential task available without enhancement
- JavaScript Tutorial: Getting Started
Related: HTML import maps: resolve a module name to a controlled local file.
Continue with HTML modulepreload: fetch a known module before its import is discovered.
