Getting started
Install
Section titled “Install”npm install @johnhenry/html-modules<!-- In <head>, before any <script type="module"> or modulepreload. --><script type="importmap"> { "imports": { "@johnhenry/html-modules/browser": "https://cdn.jsdelivr.net/npm/@johnhenry/[email protected]/src/browser.js", "@johnhenry/html-modules/runtime": "https://cdn.jsdelivr.net/npm/@johnhenry/[email protected]/src/runtime.js" } }</script><script type="module"> import * as htmlModulesBrowser from "@johnhenry/html-modules/browser"; import * as htmlModulesRuntime from "@johnhenry/html-modules/runtime";</script>Provenance. @johnhenry/html-modules is a new package: it was developed locally as web-module-graph and
renamed before it was ever published, so 0.0.0 is the first version under any name. The unscoped
html-modules on npm is an unrelated package by another author; install the scoped name. (Pre-1.0, ^0.0.0 matches
only 0.0.0: pin exactly until a deliberate 0.1.0.)
- Browsers: any browser with custom elements and shadow DOM. Constructable stylesheets are used where available;
elsewhere component styles fall back to a
<style>per shadow root. Nothing needs a bundler. - Node >= 26 (
engines) for the compiler, the CLI, the tests and the Node examples. The browser code itself has no Node requirement.
Load the bootstrap once per page. Served from node_modules:
<script type="module" src="/node_modules/@johnhenry/html-modules/src/browser.js"></script>or through an import map, which is also what lets compiled modules (which import
@johnhenry/html-modules/runtime) share the page’s runtime:
<script type="importmap"> { "imports": { "@johnhenry/html-modules/browser": "/node_modules/@johnhenry/html-modules/src/browser.js", "@johnhenry/html-modules/runtime": "/node_modules/@johnhenry/html-modules/src/runtime.js" } }</script><script type="module">import '@johnhenry/html-modules/browser';</script>browser.js imports ./runtime.js, so both entries above resolve to one copy of the runtime. Keep it that way
(see Honest limitations).
Quick start
Section titled “Quick start”1. Author an HTML module. Any .html file; each <html-export> is a public export, everything else is
private.
<html-export name="card"> <style>:host { display: block; border: 1px solid #ddd; border-radius: 10px; padding: 1rem; }</style> <template> <article> <header part="title"><slot name="title"></slot></header> <slot></slot> </article> </template></html-export>
<html-export name="button" delegates-focus> <template><button part="button" type="button"><slot></slot></button></template></html-export>
<html-export name="theme"><style>:root { --brand: #5b4bd6; }</style></html-export>
<html-export name="meta"><script type="application/json">{ "version": "1.2.0" }</script></html-export>2. Import it with <html-import as>. Every component export is registered as <as>--<export>.
<script type="module" src="/node_modules/@johnhenry/html-modules/src/browser.js"></script><html-import src="./ui.html" as="ui"></html-import>3. Use the tags. Anywhere, before or after the import: elements upgrade in place when the module arrives.
<ui--card> <b slot="title">Hello</b> <ui--button>Click me</ui--button></ui--card><style>ui--card:not(:defined) { visibility: hidden; }</style>4. Pick only what you need, choose tag names, adopt the stylesheet, read the data:
<html-import src="./ui.html" as="ui"> <html-binding export="card"></html-binding> <!-- <ui--card> only --> <html-binding export="button" element="brand-button"></html-binding> <!-- a tag you choose --> <html-binding export="theme" adopt></html-binding> <!-- adopted into the document --> <html-binding export="meta"></html-binding> <!-- el.bindings.meta --></html-import>5. Or from JavaScript, sharing the same cache:
import { HTMLModules } from '@johnhenry/html-modules/browser';
const ui = await HTMLModules.load('./ui.html'); // { button, card, components, meta, theme }: registers nothingui.card.define('profile-card'); // one definition, any number of tagsawait HTMLModules.import('./ui.html', { as: 'admin' }); // = <html-import src="./ui.html" as="admin">Runnable, self-verifying versions of all of this are in examples/ (npm run examples), and
a browser demo site with live checks on every page is at /examples/ when you serve the package root
(python3 -m http.server).
Where next
Section titled “Where next”- Make templates dynamic with
{{attribute}}andprops, or build form controls: Data binding and forms. - Edit modules with live updates:
npx html-module dev ./site(Dev server, hot reload and Vite). - Types ship for every entry point (TypeScript).