Importing components

<import-element> loads an HTML file and defines every <custom-element> in it.

<import-element from="/components/todo-app.html"></import-element>

<todo-app></todo-app>

from is resolved against the page, like href. Elements used before their definition arrives are upgraded when it arrives. Until then, realm.css keeps them hidden.

Behaviour

Behaviour What REALM does
Fetching Each URL is fetched once. Every <import-element> with the same URL, and importDefinitions(url), share that request
Bundles A file can hold any number of <custom-element> definitions. All of them are defined
Duplicates Importing the same component twice is harmless. A different definition for a name that already exists keeps the first and warns, naming both files
Errors A failed request fires error on the <import-element> and logs a warning. The rest of the page keeps working
Removal An <import-element> removed before its file arrives stays quiet: it fires no events
Throttling At most six files are fetched at once per origin. The rest wait in a queue
Moving Moving or reinserting an <import-element> does not import again

Bundles

Group components that are used together in one file. REALM UI ships every component in one ui.html:

<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/ui.html"></import-element>

Smaller bundles load faster, but one file per component makes more requests. A file per feature area is a good default.

Lazy loading

loading="lazy" waits until one of the tags listed in for appears in the page (or in any component's markup) before fetching:

<import-element from="/components/charts.html" loading="lazy" for="sales-chart usage-chart"></import-element>

Without for, a lazy import cannot know what to wait for, so REALM warns.

Events

<import-element> fires load when every definition in the file is defined and error when the file cannot be loaded:

<import-element
  from="/components/map.html"
  onload="console.info('map ready', event.detail.url)"
  onerror="console.warn('map failed', event.detail.url, event.detail.status)"
></import-element>
Event event.detail
load { url }
error { url, status } (status is 0 for network errors)

Imports inside component scripts

A component script can import JavaScript modules. Use <script type="module"> for clarity, and put the imports first:

<custom-element name="cart-badge">
  <script type="module">
    import { cart } from "../state/cart.js";
    import { formatMoney } from "../lib/format.js";

    const badge = useState({
      get total() {
        return formatMoney(cart.total);
      },
    });
  </script>

  <span><slot name="#badge.total"></slot></span>
</custom-element>

Relative paths (./, ../) resolve against the component file, not the page, so a component folder can be moved as a whole. Imported names are available to markup and handlers like any top-level name.

Stylesheets

A definition can link a stylesheet. Its URL also resolves against the component file, and it applies inside the component only:

<custom-element name="fancy-table">
  <link rel="stylesheet" href="./fancy-table.css">
  <table>…</table>
</custom-element>

From JavaScript

importDefinitions(url) does the same as <import-element> and returns a promise:

import { importDefinitions } from "@realmorg/realm";

await importDefinitions("/components/editor.html");
document.body.append(document.createElement("rich-editor"));

Trust

A component file is code, like a <script src>. Importing a component from another origin runs that origin's code in your page. Only import from sources you trust, and pin versions on CDNs.

Edit this page on GitHub · View as Markdown