Embedding Svelte in other apps, shipping web components and migrating legacy code all depend on the low-level component API.
In Svelte 5 a component is a plain function, not a class. You drive it with functions from svelte:
mount(Component, { target, props })creates and inserts it, returning its exports;hydratedoes the same over server-rendered HTML.unmount(instance, { outro: true })removes it, optionally playing outro transitions (returns a promise).- Effects created by
mountdon't run synchronously; callflushSync()when you need the DOM updated immediately, for example in tests. - To change props from outside, pass a
$stateobject aspropsand mutate it; the component sees the changes. - Components can expose functions or values with
export functionin the script; they appear on the objectmountreturns.
Custom elements: with <svelte:options customElement="my-counter" /> and the customElement: true compiler option, a component compiles to a web component usable from any framework or plain HTML. Props become attributes and properties (configure types and reflection with the props option), styles are encapsulated in shadow DOM, and $host() gives access to the host element, e.g. to dispatch DOM events. Caveats: the inner component is created a tick after the element is connected, passed-in content uses native <slot> elements rather than snippets, and context does not cross element boundaries.
Legacy interop: Svelte 5 still compiles Svelte 4 syntax (export let, $:, on:click, slots) per component in legacy mode, so you can migrate gradually; npx sv migrate svelte-5 automates most of it. Runes mode is enabled per file as soon as you use a rune.
Example
// embed.svelte.js - mounting Svelte inside a non-Svelte page or a test
import { mount, unmount, flushSync } from 'svelte';
import Counter from './Counter.svelte';
const props = $state({ start: 5 }); // reactive props object
const counter = mount(Counter, { target: document.querySelector('#widget'), props });
flushSync(); // DOM is now rendered
console.log(document.querySelector('#widget').textContent); // e.g. "count: 5"
props.start = 10; // component reacts to prop change
counter.reset?.(); // call a function the component exported
await unmount(counter, { outro: true }); // play outros, then remove
// Counter.svelte compiled as <my-counter> (compilerOptions.customElement = true):
// <svelte:options customElement={{ tag: 'my-counter', props: { start: { type: 'Number' } } }} />
// <script>
// let { start = 0 } = $props();
// let count = $state(start);
// export function reset() { count = start; }
// const host = $host();
// const inc = () => { count++; host.dispatchEvent(new CustomEvent('change', { detail: count })); };
// </script>
// <button onclick={inc}>count: {count}</button>Remember
- Components are functions:
mount,hydrate,unmount,flushSyncdrive them - Pass a
$stateobject as props to update a mounted component from outside customElementcompiles to web components;$host()reaches the host element- Svelte 4 syntax still compiles per component, so migration can be gradual
Watch out for
- Using
new Component({ target })from Svelte 4; in Svelte 5 usemount().