Quick start
The fastest path is the CLI. It scaffolds a React widget pre-wired with the build pipeline, both bundle outputs, and a working host page.
1. Scaffold a widget
Section titled “1. Scaffold a widget”npx mountly init my-widgetcd my-widgetYou get:
src/Component.tsx— a React component (you can swap in Vue or Svelte later).- A widget factory using
mountly-react’screateWidget. - A
tsupbuild that produces bothdist/index.js(self-contained) anddist/peer.js(shared React). - An example host page.
2. Install and build
Section titled “2. Install and build”pnpm installpnpm buildTwo artifacts land in dist/:
index.js— self-contained bundle. Includes React. Drop into any page.peer.js— peer build. Excludes React. Use when the host already maps React via an import map.
See Distribution for which one to ship.
3. Mount it
Section titled “3. Mount it”<div id="mount"></div><script type="module"> import widget from './my-widget/dist/index.js'; widget.mount(document.getElementById('mount'));</script>The widget mounts inside the container in light DOM and the host’s CSS reaches in. If you need full style isolation, pass shadow: true to createWidget.
3b. Lowest-ceremony host (recommended for plain HTML)
Section titled “3b. Lowest-ceremony host (recommended for plain HTML)”If you’re on a static/CMS host, prefer declarative islands + one script tag:
<div data-mountly-island='{"schemaVersion":1,"id":"signup","moduleId":"signup-card","trigger":"idle","props":{"plan":"pro"}}'> <a href="/signup">Sign up (fallback)</a></div>
<script type="module" src="/packages/mountly/dist/host-entry.js" data-mountly-host data-mountly-loaders='{"signup-card":"/widgets/signup-card.js"}'></script>This is the default DX path: no host app code, one script tag, and fallback HTML still works when JS is unavailable.
4. Add the on-demand lifecycle
Section titled “4. Add the on-demand lifecycle”mount() is the bare minimum. To get on-intent loading, wrap the widget in a feature:
import { createOnDemandFeature } from 'mountly';
const signup = createOnDemandFeature({ moduleId: 'signup-card', moduleUrl: './my-widget/dist/index.js',});
signup.attach({ trigger: document.querySelector('#cta')!, preloadOn: 'hover', activateOn: 'click',});mountly synthesises the loader and a default render that threads moduleUrl through to the adapter, so the sibling dist/index.css is fetched and applied automatically. The widget code is fetched on first hover and mounted on click. Open DevTools → Network and watch.
Need bespoke loading or rendering? loadModule and render are still available and override the auto-derived defaults.
5. See the full demo
Section titled “5. See the full demo”The repo ships several runnable examples. The richest is the marketing site:
git clone https://github.com/jagreehal/mountlycd mountlypnpm install && pnpm -r buildcd examples/marketing-site && pnpm devOpen http://localhost:5176/examples/marketing-site/. You’ll see:
- A hero “Join the newsletter” button — preloads on hover, mounts on click.
- An inline embed — mounts on viewport entry.
- Zero framework JavaScript on the host before interaction.
The shortest copy-paste host lives at http://localhost:5175/examples/quickstart/host.html after running cd examples/plain-html && pnpm dev.
Where to next
Section titled “Where to next”- How it works — the architecture in one page.
- Triggers — the six ways to say “now”.
- React / Vue / Svelte — adapter-specific patterns.
- API:
createOnDemandFeature— the core primitive.