References

Beginner-friendly references for web development, with live, editable examples.

The HTML nomodule attribute

Attribute All modern browsers
In short

Current browsers skip any classic script marked with the HTML nomodule attribute. Paired with <script type="module">, it gives browsers without module support a fallback, since they ignore the module as an unknown type and run the nomodule script instead.

Overview

The HTML nomodule attribute stops a classic script from running in current browsers, all of which support module scripts. It exists to mark fallback code for browsers that can't run modules.

It is half of a pair, such as <script type="module" src="app.js"> next to <script nomodule defer src="app-legacy.js">. A modern browser runs only the module. A browser without module support skips the module as an unknown script type and runs the fallback.

Under the HTML standard, a browser that skips the script doesn't download it either. The check for nomodule comes before the fetch step, so an external nomodule file is never requested and fires no load or error event. Inline scripts with nomodule are skipped the same way.

Safari 10.1 on macOS and 10.3 on iOS run both the module and the fallback, because they support module scripts but not nomodule, which arrived in version 11. The other engines added nomodule along with modules.

Syntax

<script type="module" src="app.js"></script>
<script nomodule defer src="app-legacy.js"></script>

Values

Value
Browsers that support nomodule skip the classic script.

Example

Live example
<p>This code holds a nomodule fallback and a module script.</p>
<div role="status"><ul id="log"></ul></div>
<p>In a current browser, only the module adds a line. Delete nomodule in the editor and the fallback adds one too.</p>
<script nomodule>
const fallbackItem = document.createElement('li');
fallbackItem.textContent = 'The nomodule fallback ran.';
document.getElementById('log').append(fallbackItem);
</script>
<script type="module">
const moduleItem = document.createElement('li');
moduleItem.textContent = 'The module script ran.';
document.getElementById('log').append(moduleItem);
</script>

Best practices

  • Pair each nomodule script with a module script that loads the same features. On its own, a nomodule script runs only in old browsers, and modern ones get none of its code.
  • Add defer to the nomodule script. The fallback then runs after parsing, the same point at which module scripts run.
  • Build the fallback without import or export statements. In a classic script, an import declaration throws a SyntaxError.
  • Serve a cross-origin module with CORS headers. Module scripts need CORS for cross-origin fetches, and if the module fails, modern browsers have no fallback to run, since they skip the nomodule copy.

Frequently asked questions

Why would a script tag have nomodule?
The nomodule attribute marks a fallback script that current browsers skip. Browsers without module support ignore the attribute and run the script instead.
Why doesn't my nomodule script run in Chrome?
Chrome supports nomodule, so it skips any classic script marked with it, by design. Remove the attribute from code that every browser should run.
Can a module script carry nomodule?
No, validator.nu treats nomodule on a type=module script as an error. Browsers ignore it there, since nomodule only applies to classic scripts, and the module still runs.
Is nomodule still needed?
A nomodule fallback is needed only if you still support browsers without module scripts, such as Internet Explorer. Chrome since 61, Edge since 16, Firefox since 60 and Safari since 11 all skip it.
Will a browser skip a nomodule script created in JavaScript?
Yes, current browsers also skip a classic script you insert with its noModule property set to true. That property reflects the nomodule attribute, so setting it adds the attribute.