What is svebcomponents?
Svelte already allows compiling to custom elements today: set
customElement: true and you have a working web component.
That is enough for one component. A library is a different job. You write prop metadata by hand for every attribute, assemble a build pipeline SvelteKit does not provide, ship no types to your consumers, and give up server rendering entirely — because a Svelte custom element wipes its shadow root the moment it upgrades.
svebcomponents is the toolchain for that second job.
What you get over customElement: true
Section titled “What you get over customElement: true”Svelte customElement: true | With svebcomponents | |
|---|---|---|
| Attribute types, reflection | hand-written <svelte:options customElement={...}> | inferred from your $props() types |
| Packaging | assemble your own pipeline | package.json exports are the config |
| Types for consumers | none | .d.ts plus a custom elements manifest |
| Server rendering | none | declarative shadow DOM via Lit’s ElementRenderer |
| Hydration | shadow root wiped, component re-mounted | server DOM adopted in place |
| Evaluated twice | customElements.define throws | idempotent |
Attributes come from your types
Section titled “Attributes come from your types”Svelte needs prop metadata to turn an HTML attribute string into a typed prop.
Auto-options reads the types you already wrote
and generates it, so value: number is all you write to make value="42"
arrive as 42.
Your package.json is the build config
Section titled “Your package.json is the build config”SvelteKit packages Svelte components, not custom elements.
The build infers entrypoints from your exports
field, so declaring where the output goes is the same act as configuring the
build.
Consumers get types and a manifest
Section titled “Consumers get types and a manifest”Every build emits TypeScript declarations and a
custom elements manifest,
so editors autocomplete your tags and querySelector returns a typed element:
import "my-components";
const el = document.querySelector("simple-component");el?.count; // number | undefinedServer rendering that survives hydration
Section titled “Server rendering that survives hydration”This is the part plain Svelte custom elements cannot do at all. Svelte’s generated element attaches a shadow root on upgrade, which per spec clears any declarative shadow root the server delivered, and then mounts from scratch.
svebcomponents renders the shadow root on the server as declarative shadow DOM and adopts it in place in the browser: the nodes survive, styles are reused, and the component is reactive afterwards. It is on by default for every SSR-enabled component.
Is this for you?
Section titled “Is this for you?”svebcomponents is a good fit if you write Svelte and want to ship components that non-Svelte applications can use — a design system consumed by several frameworks, embeddable widgets, or anything that outlives one app’s stack.
If your consumers are all Svelte applications, plain Svelte components are simpler and faster; you do not need custom elements. If you have no Svelte in your stack at all, Lit is the more direct choice — svebcomponents’ value is that it lets you keep writing Svelte.