Skip to content

Resolution, caching and errors

  • src resolves like a module specifier: relative to the importing document (or, inside a module, the importing module), or absolute. Bare specifiers go to hostResolve; in the browser that is import.meta.resolve, so the page’s own <script type="importmap"> applies. Import maps only map URLs; they never parse HTML. As in JavaScript, ui.html without ./ is a bare specifier.
  • A document’s <html-import-settings base> replaces the document (or module) URL as the base for its relative specifiers.
  • Modules are cached by kind (HTML or JavaScript) and resolved URL as promises, so repeated and concurrent imports share one fetch and parse. Failed loads are evicted and can be retried; HTMLModules.unload(src) evicts one on purpose (registered tags stay registered, and a JavaScript module stays in the browser’s module map).
  • Circular dependencies between HTML modules are rejected with the cycle in the message, whether the modules load one after another or concurrently.

Nothing fails silently. In markup, a failure is an error event (bubbling, composed, with detail.error) on the element concerned, and a rejected el.ready; a failing <html-binding> fires on the binding and bubbles through its import. In JavaScript it is a throw or a rejection. With errors="throw" it also reaches reportError() (the console and window.onerror). Mistakes in a module’s source are SyntaxErrors naming the module, identical whether the module is loaded or compiled. Some common ones:

Situation Error
Module fetch fails Error: Failed to fetch HTML module …: 404
Invalid module (no template/style/JSON, duplicate or non-kebab name, two templates, bad shadow) SyntaxError naming the module
A tag the delimiter makes invalid (as="ui" delimiter="." + card) SyntaxError: … <ui.card> is not a valid custom element name (it has no hyphen)
Circular dependency Error: Circular HTML module dependency: a -> b -> a
Tag already bound to a different component Error: Cannot bind <ui--card>: it is already defined by "card" from … (conflict="reuse" keeps the existing definition instead)
Missing export SyntaxError: The requested module '…' does not provide an export named '…'
A malformed binding in a template ({{ a + b }}), or an on*, style or srcdoc binding, at registration SyntaxError: …: Invalid binding "{{ a + b }}": a binding is the name of a host attribute, {{attribute-name}}. There are no expressions, filters or calls; write \{{ for a literal "{{"
form-control without form-associated SyntaxError: <html-export name="<n>">: form-control="<selector>" needs form-associated: the component must take part in forms for its control's value to be the form value in <url>
An unknown registry value SyntaxError: Invalid registry="<value>" on <html-import> in <url>: use "global" or "scoped"
<html-import-settings> after an <html-import> SyntaxError: <html-import-settings> must come before any <html-import> …

Every error, its exact message and where it is reported: Errors.