How the build works
@svebcomponents/build provides the pipeline around a Svelte custom element
package. It compiles Svelte for the browser, runs
auto-options, emits TypeScript declarations,
and describes every element it built.
This page covers how it decides what to build. For the practical side — which
fields to put in package.json before you publish — see
Publishing your package.
Component entrypoints
Section titled “Component entrypoints”Each custom element is its own entrypoint, and its PascalCase source filename
matches the JavaScript output basename declared in package.json. The
component declares its own tag:
<svelte:options customElement="favorite-number" />Consumers import the entrypoint for the side effect of defining
<favorite-number>. Svelte’s generated customElements.define() call is made
idempotent at build time, so evaluating the component — or a bundle containing
it — more than once never throws.
Zero-config builds
Section titled “Zero-config builds”The CLI infers entrypoints from the exports field:
{ "exports": { ".": { "types": "./dist/client/FavoriteNumber.d.ts", "svelte": "./dist/client-svelte/FavoriteNumber.js", "default": "./dist/client/FavoriteNumber.js" } }}The inference follows these rules:
- The export’s
defaultorimportcondition must start with./dist/client/. - The output path is mapped from
dist/clienttosrc. - A same-basename
.sveltesource is a svebcomponent. - A same-basename
.tsor.jssource is an ordinary module and receives a plain tsdown build, without the custom-element, hydration or SSR pipeline. - Exactly one matching source must exist. Ambiguous basenames fail with an error instead of silently choosing one.
- Subpath patterns (
"./*") are not inferred. Give each entry its own export key, or use manual configuration.
Here ./dist/client/FavoriteNumber.js resolves to src/FavoriteNumber.svelte.
Rule 4 is what lets one package ship components and ordinary helpers without
running the helpers through custom-element compilation.
A svelte condition adds a second, lighter build, and a matching /ssr export
adds a server renderer. Both are covered in
Publishing your package.
Server preparation
Section titled “Server preparation”An SSR-enabled component can acquire its own data on the server by adding an
adjacent <entry>.ssr.ts (or .js) module next to the component. It is
compiled only into the server output, so server-only dependencies never enter
the browser bundle, and values it sets are serialized for the client to adopt.
See the
SsrPrepare signature and
what makes a component asynchronous.
Element types & manifest
Section titled “Element types & manifest”Every build describes the elements your package declares, in two forms:
custom-elements.json— a custom elements manifest (schema 2.1.0). Editors read it for HTML completions, and the wider custom elements tooling ecosystem consumes it. It reaches consumers only ifpackage.jsondeclares it — see Ship the manifest.- TypeScript declarations at the path the entry’s
typescondition points at. Direct.svelteentries receive a complete declaration for their custom element constructor as well as their tag, props and events.
Nothing extra is needed for the TypeScript half:
// defines <simple-component> and types itimport "my-components";
const el = document.querySelector("simple-component");el?.count; // number | undefinedWhat ends up in both is decided by what you wrote in the component:
props become attributes, $host() events become a typed event map, <slot>
elements and consumed CSS custom properties become documented members. See
Authoring components for each of them, and
typing elements in React & Vue for the four
exported types and how to register them with a host framework.
Static tags only
Section titled “Static tags only”Both forms are produced by reading your .svelte sources at build time, so an
element is only describable when its tag is a literal in <svelte:options> and
the component is a direct entry.
Two consequences:
- A dynamically interpolated tag, or the bare
customElementboolean shorthand, cannot be read. auto-options warns and leaves<svelte:options>untouched, which also means no inferred props and no hydration wrapper. - A component reached only through a script entry is not described. If
src/index.tsimportsButton.svelteand re-exports it, the build treatssrc/index.tsas an ordinary module: no manifest declaration, no element types. Make the component the entry instead.
A tag that is not knowable at build time cannot be typed, cannot be put in
HTMLElementTagNameMap, and cannot be self-registered by the SSR renderer.
Beyond the inference rules
Section titled “Beyond the inference rules”When export inference cannot express your layout, a
svebcomponents.config.ts file replaces it
and exposes every build option directly.
For installation, the generated outputs and the full pipeline, see the
@svebcomponents/build reference.