Skip to content

Build

@svebcomponents/build provides the build pipeline around a Svelte custom element package. It compiles Svelte for the browser, runs @svebcomponents/auto-options, and emits TypeScript declarations alongside the JavaScript output.

Each custom element needs an entrypoint that re-exports the Svelte component:

import Component from "./Component.svelte";
export default Component;

The component itself declares its 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 it no-ops if the component (or a bundle containing it) is ever evaluated more than once.

For a tag that can’t be a literal in <svelte:options> (e.g. computed at build time), fall back to @svebcomponents/utils’s defineElement in the entrypoint instead — the same guarded registration, called by hand:

import { defineElement } from "@svebcomponents/utils";
import Component from "./Component.svelte";
export default Component;
defineElement(tagName, Component);

For the common case, the CLI infers component entrypoints from the exports field in package.json:

{
"exports": {
".": {
"types": "./dist/client/index.d.ts",
"svelte": "./dist/client-svelte/index.js",
"default": "./dist/client/index.js"
}
}
}

The inference follows three rules:

  1. The export’s default or import condition must contain dist/client.
  2. The output path is mapped from dist/client to src.
  3. If that exact source file does not exist, the CLI looks for the same path with a .ts extension.

In this example, ./dist/client/index.js resolves to src/index.ts.

The standalone default build includes the Svelte runtime so it can work in applications that do not use Svelte.

When the package is consumed by a Svelte application, bundling another copy of the runtime is often unnecessary. Adding a svelte condition asks the CLI to create a second build that leaves svelte and svelte/* imports external:

{
"exports": {
".": {
"types": "./dist/client/index.d.ts",
"svelte": "./dist/client-svelte/index.js",
"default": "./dist/client/index.js"
}
}
}

Svelte-aware tooling such as SvelteKit and @sveltejs/vite-plugin-svelte can select dist/client-svelte/index.js and share the host application’s Svelte runtime. Other consumers select the standalone default build.

Every matching export becomes a component entrypoint, so a package can expose more than one custom element:

{
"exports": {
".": {
"types": "./dist/client/index.d.ts",
"svelte": "./dist/client-svelte/index.js",
"default": "./dist/client/index.js"
},
"./button": {
"types": "./dist/client/button.d.ts",
"svelte": "./dist/client-svelte/button.js",
"default": "./dist/client/button.js"
}
}
}

SSR is enabled per component by adding an export with the same key followed by /ssr:

{
"exports": {
"./button": {
"types": "./dist/client/button.d.ts",
"svelte": "./dist/client-svelte/button.js",
"default": "./dist/client/button.js"
},
"./button/ssr": {
"types": "./dist/server/button-ssr.d.ts",
"svelte": "./dist/server-svelte/button-ssr.js",
"default": "./dist/server/button-ssr.js"
}
}
}

The browser export defines the custom element. The matching SSR export provides the renderer that a server application can register.

SSR is currently in beta. See SSR for its stability and security considerations.

Package exports are the recommended configuration for straightforward packages. When you need different source paths or output directories, create svebcomponents.config.ts:

import { defineConfig } from "@svebcomponents/build";
export default defineConfig({
entry: "src/index.ts",
outDir: "dist/client",
svelteOutDir: "dist/client-svelte",
ssr: true,
ssrOutDir: "dist/server",
ssrSvelteOutDir: "dist/server-svelte",
});

svelteOutDir enables the Svelte-aware browser build. ssrSvelteOutDir does the same for the optional server renderer.

For installation, every configuration option, and the exact generated outputs, see the @svebcomponents/build reference.