Skip to content

mountly

v0.1 · ~9 KB gzipped core entry

Framework components. Any page. On demand.

mountly turns React, Vue, Svelte, and TSRX components into lazy, HTML-addressable features. Drop them into CMS pages, marketing sites, legacy apps, partner embeds, or framework migrations without a host-app rewrite. Light DOM is the default; use shadow: true when you need hard style isolation.

~9KBcore entry (gzipped)
7activation triggers
4adapter packages
0SPA required
3frameworks, one page
0x · Runtime shape

Small core + explicit subpaths.

mountly keeps a small top-level core entry and exposes focused subpath imports (for example mountly/attach, mountly/elements,mountly/shadow, mountly/assets). In browser import-map hosts, map only the subpaths you use.

01 · Positioning

One primitive. Several high-value jobs.

You wrap a component as a mountable widget. mountly loads and activates it from plain HTML when the page signals intent. Marketing reuse, strangler-fig migration, island-style UI, and embeds run on this one runtime.

What you keep

  • Normal React, Vue, Svelte, or TSRX components
  • Existing CMS, static, legacy, or app host pages
  • Current routing, query-string state, and deployment model
  • Per-widget ownership by the component team

No new component authoring model. No full app rewrite.

What mountly adds

  • HTML tags like <signup-card>
  • Activation on idle, viewport, click, hover, media, or URL change
  • Shadow-DOM style isolation and automatic CSS loading
  • Module/data caching, teardown, and prop updates

A small runtime for component delivery, not an app platform.

02 · Use cases

Where the primitive pays off.

01marketingReal product components on campaign pagesReuse checkout, pricing, signup, and calculator components on fast campaign pages without rewriting the CMS host.
02migrationStrangler fig, one widget at a timeReplace legacy surfaces incrementally. Each widget replaces one section. The host never rewrites. When all widgets are done, the legacy is gone.
03multi-teamIndependent widgets, not independent appsTeam A ships a payments widget, Team B ships chat, Team C owns the host. Independent widget deploys via CDN + manifest — no merge conflicts, no Federation shared block. Not a micro-frontend control plane.
04cross-frameworkReact + Vue + Svelte on one pageThree teams, three frameworks, one page. Each widget is a bare specifier in the import map. Teams converge on the next architecture at their own pace.
05embedsPartner and CMS dropsExpose a stable custom tag and let non-app teams place interactive UI without owning framework code or managing a build step.
06performanceIntent-paid JavaScriptKeep the page shell light. Load heavy UI when users scroll, click, hover, or match a route. ~9KB core, widgets on demand.
03 · Triggers

Six ways to say “now”.

A trigger is the signal that moves a feature from idle to preload to mount. Pick the one that matches user intent, or compose your own with a plugin.

01on intenthoverPreload on mouseenter, mount on click. The default for discoverable controls.
02explicitclickMount on click without preloading. Use when intent is committed, not exploratory.
03accessibilityfocusPreload on focus, mount on commit. Keyboard parity with hover.
04below the foldviewportMount when the element scrolls into view. Configurable threshold.
05predictiveidleUse requestIdleCallback to preload during quiet moments.
06routingurl-changeMount when the URL matches a pattern. Pairs with client-side navigation.
04 · Lifecycle

One state machine, five states. The same shape on every framework.

Every feature moves through the same sequence regardless of trigger or framework. You hook into any phase, abort in flight, and unmount without leaks.

01idleNothing loaded. Waiting for the trigger.
02preloadModule fetched, data fetch may begin in parallel.
03activateCommitment signal. Finalise data, prepare to mount.
04mountWidget rendered into the container (light DOM by default; shadow root with shadow: true).
05unmountRemoved from DOM. Caches retained for re-mount.
05 · The whole API

A widget. A feature. A trigger. Three lines you’ll write.

Components stay components. The adapter wraps them as widgets. You wire a feature in code, or declare it as an HTML tag.

signup-card.ts
import { createWidget } from "mountly-react";
import SignupCard from "./SignupCard.tsx";
import styles from "./SignupCard.css?inline";
// 1. Wrap a component as a framework-agnostic widget
export default createWidget(SignupCard, { styles });
page.ts
import { createOnDemandFeature } from "mountly";
// 2. Add the on-demand lifecycle around a widget
const signup = createOnDemandFeature({
moduleId: "signup-card",
loadModule: () => import("./signup-card.js"),
render: ({ mod, container, props }) => mod.mount(container, props),
});
// 3. Attach it to a DOM trigger — preload on hover, mount on click
signup.attach({
trigger: document.querySelector("#cta")!,
preloadOn: "hover",
activateOn: "click",
});

Styling: mountly loads CSS with the module and applies it before render, so no FOUC. Light DOM lets the host’s design system reach in; pass shadow: true for full isolation.

host.html
<signup-card trigger="viewport" props='{"plan":"pro"}'></signup-card>
<script type="module">
import { defineMountlyFeature } from "mountly";
defineMountlyFeature("/widgets/dist/index.js");
</script>
06 · How it compares

Composable with frameworks. Useful where framework boundaries end.

ApproachBest atTradeoffComplexity
Traditional SPAOne app owns every surfacePoor fit for CMS, legacy, or partner-hosted UILow
Framework code-splittingRoutes inside a single framework appDoes not standardize HTML drops or host-agnostic lifecycleMedium
Microfrontend orchestratorsIndependent apps with org-level ownershipOperationally heavy for component-sized features. Shared config blocks, runtime negotiation, build-time coupling.High
Monorepo + npm libsSame org, shared CI, lazy routes until clean breakMerge coordination; not for foreign hosts you cannot merge intoLow
Import-map widgets (mountly model)Team-scalable widgets with zero orchestratorShared React version agreement; you own CDN/manifest workflowLow
mountlyComponent features in any HTML pageNot a router, SSR framework, or control planeLow
07 · Packages

One runtime, four adapters, one optional design preset.

mountly · Apache-2.0 · pre-1.0github.com/jagreehal/mountly

see ./examples in the repo