Skip to content

Svelte

mountly-svelte is the Svelte adapter. It supports both Svelte 4 (legacy class component API) and Svelte 5 (functional component API), and auto-detects which one your component uses.

Terminal window
pnpm add mountly mountly-svelte svelte

If you’re on Svelte 4, the adapter detects the legacy class API and uses new Component({ target, props }) internally. No extra setup.

signup-card.ts
import { createWidget } from "mountly-svelte";
import SignupCard from "./SignupCard.svelte";
export default createWidget(SignupCard);

The Vite library build emits dist/index.js plus dist/index.css (Svelte’s component styles, hashed). The adapter fetches the CSS automatically when the host wires up moduleUrl; see “On-demand lifecycle (zero-config)” below.

Svelte 5 components are plain functions. Pass mount and unmount from "svelte":

signup-card.ts
import { createWidget } from "mountly-svelte";
import { mount, unmount } from "svelte";
import SignupCard from "./SignupCard.svelte";
export default createWidget(SignupCard, { mount, unmount });

If you omit them, the adapter dynamically imports "svelte" itself. That works, but adds one promise hop on first mount.

import widget from "./signup-card.js";
widget.mount(document.querySelector("#cta-mount"), {
headline: "Try the API",
});

Each mount() unmounts any existing instance in the same container first.

import { createOnDemandFeature } from "mountly";
const signup = createOnDemandFeature({
moduleId: "signup-card",
moduleUrl: "/widgets/signup-card/dist/index.js",
});
signup.attach({ trigger: btn, preloadOn: "hover", activateOn: "click" });

mountly threads moduleUrl through to the adapter, which fetches dist/index.css (sibling) and applies it before render. No FOUC, no manual loader. Default light DOM lets the host’s design system reach in; pass shadow: true for hard isolation.

Need to override? loadModule / render still work and trump the auto-derived defaults.

The Svelte adapter falls back to remount on feature.update(), which preserves the container but creates a new component instance — local state inside the component resets. If you need state to survive a prop change, lift it to a store or pass it back in via props.

Same risk as React/Vue. Use the peer build pattern for multi-widget pages — externalise svelte and provide it once via the host’s import map.

Svelte’s component-scoped styles already isolate via Svelte’s hash. With shadow: true they get an additional hard boundary. Global styles (:global(...)) inside a Svelte component land inside the shadow tree when shadow: true (so they don’t leak to the document); in default light DOM they reach the document, which is usually what you want.

The Vite + @sveltejs/vite-plugin-svelte library build emits a sibling dist/index.css for any component that has <style>. The adapter picks it up automatically when the host passes moduleUrl:

import { createWidget } from "mountly-svelte";
import Component from "./Component.svelte";
export default createWidget(Component);

Compile Tailwind into the same dist/index.css (via your build’s CSS entry) and the same zero-config flow applies. If your Tailwind output lives at a non-default path, point the host at it explicitly:

createOnDemandFeature({
moduleId: "card",
moduleUrl: "/widgets/card/dist/index.js",
// No assetOptions needed; adapter derives /widgets/card/dist/index.css.
});

If you’d rather inline the CSS string at build time (e.g. small components, no shared Tailwind layer), that path still works:

import { createWidget } from "mountly-svelte";
import Component from "./Component.svelte";
import styles from "./styles.css?inline";
export default createWidget(Component, { styles });