npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@svebcomponents/ssr

v0.3.3

Published

Downloads

1,325

Readme

Server-side rendering support for Svelte-built custom elements.

Browsers know how to instantiate custom elements, but server renderers usually only see an unknown HTML tag. @svebcomponents/ssr bridges that gap by letting a custom element package provide an ElementRenderer, then letting a Vite app use that renderer when it sees the custom element in a Svelte template.

The design is modeled after Lit's server-side rendering system: custom elements are rendered by ElementRenderer classes, and the server uses declarative shadow DOM to serialize the rendered shadow root.

This package is currently in beta and ready for real-world evaluation and early production adoption. Its runtime API and generated output may still change before 1.0; breaking changes are documented in release notes and migration guides. Attribute values/names and tag names are validated and escaped through Svelte's own SSR serializer, and this is covered by XSS regression tests, but the package has not had an independent security audit. Shadow-root content rendered via {@html} relies on the renderer's escaping, so applications that handle untrusted content should assess that boundary carefully.

What It Provides

@svebcomponents/ssr has three pieces:

  • @svebcomponents/ssr/tsdown: a tsdown config helper used by @svebcomponents/build to generate a server-renderable build and an ssr.js renderer entrypoint.
  • @svebcomponents/ssr/vite: a Vite pre-transform that wraps custom element tags in Svelte templates with a runtime wrapper component.
  • @svebcomponents/ssr: runtime utilities for installing server DOM shims, registering renderers, and rendering Svelte custom elements through Lit's SSR ElementRenderer API.

Useful background and related upstream limitations:

Package-author Flow

Most component packages should use @svebcomponents/build rather than importing the SSR build helper directly.

When SSR is enabled, the build produces a server output directory containing an ssr.js entrypoint. That entrypoint exports an ElementRenderer subclass for the custom element.

Expose it from your component package:

{
  "exports": {
    ".": {
      "import": "./dist/client/index.js"
    },
    "./ssr": {
      "import": "./dist/server/ssr.js"
    }
  }
}

The browser entrypoint defines the custom element. The SSR entrypoint provides the renderer an app can register on the server.

When using @svebcomponents/build, an adjacent server module such as src/index.ssr.ts is discovered automatically. Its default SsrPrepare export runs after host attributes and properties have been applied but before the component renders. Values written with setProperty are serialized into hydratable output for reuse in the browser.

If the package's Svelte config enables Svelte async rendering, the generated ./ssr renderer can yield async Lit RenderResult chunks. Async-capable host integrations should use the async Vite wrapper below.

App-author Flow

Install the Vite plugin in the consuming app:

import { svelte } from "@sveltejs/vite-plugin-svelte";
import { defineConfig } from "vite";
import svebcomponentsSsr from "@svebcomponents/ssr/vite";

export default defineConfig({
  plugins: [svebcomponentsSsr(), svelte()],
});

This automatically adds @svebcomponents/ssr to Vite's ssr.noExternal (it ships raw .svelte files under some export conditions, which Node's SSR externalization can't load directly). If your own component package also needs ssr.noExternal (e.g. an externalSvelte build sharing the host's Svelte runtime), list it via the plugin's noExternal option:

svebcomponentsSsr({ noExternal: ["my-component-package"] });

Load the component's renderer once before rendering templates that use the custom element:

import "my-component-package/ssr";

The generated renderer reads its own tag name from the component's <svelte:options customElement> declaration at build time and registers itself with ElementRendererRegistry on import. The DOM shim installs first regardless of import order or bundler chunking.

If a component's tag name couldn't be determined at build time (e.g. it's computed dynamically), the generated renderer falls back to requiring manual registration instead:

import { ElementRendererRegistry } from "@svebcomponents/ssr";
import MyComponentRenderer from "my-component-package/ssr";

ElementRendererRegistry.set("my-component", MyComponentRenderer);

Async SSR requires the host Svelte compiler to opt into Svelte's experimental async mode:

export default defineConfig({
  plugins: [
    svebcomponentsSsr({ async: true }),
    svelte({
      compilerOptions: {
        experimental: {
          async: true,
        },
      },
    }),
  ],
});

The async wrapper can consume both sync and async renderers. The sync wrapper can only consume renderers whose shadow output is fully synchronous.

The app can then render Svelte markup containing the custom element:

<my-component title="Hello" count={5}></my-component>

On the server, the Vite plugin rewrites that tag to CustomElementWrapper. The wrapper looks up the custom element constructor, finds the registered renderer, passes attributes and properties into it, and emits declarative shadow DOM.

On the client, the wrapper renders the original custom element tag so the browser can hydrate/upgrade it normally.

Runtime Exports

ElementRendererRegistry

A global registry that maps custom element constructors to Lit ElementRenderer constructors.

ElementRendererRegistry.set("my-component", MyComponentRenderer);

You can register by tag name or by constructor. Lookups walk the element prototype chain, so a renderer registered for a base element class can also serve subclasses.

The registry is designed for the Svelte-generated renderers produced by this package. Registered renderers are instantiated with the resolved tag name, so stock Lit ElementRenderer classes now receive their tagName, but the Svelte wrapper still passes a minimal RenderInfo when rendering; full compatibility with arbitrary Lit renderers is therefore not guaranteed.

SvelteCustomElementRenderer

A base renderer for Svelte custom elements.

It creates the client custom element class, applies incoming attributes/properties, and renders the server Svelte component with svelte/server. Generated SSR entrypoints extend this class.

Its optional SsrPrepare hook receives a read-only property snapshot and a setProperty callback. Synchronous hooks preserve synchronous rendering; promise-returning hooks require an async-capable host integration.

installShim

Importing @svebcomponents/ssr installs @lit-labs/ssr-dom-shim globals:

  • Element
  • HTMLElement
  • customElements

Those shims allow custom element modules to be imported in server environments.

Vite Transform

The Vite plugin scans .svelte files before they are compiled.

Any regular element whose tag name contains a dash is treated as a custom element:

<my-component count={5}></my-component>

It is rewritten to the wrapper component:

<CustomElementWrapper _tagName="my-component" count={5}></CustomElementWrapper>

The plugin also rewrites plain slot attributes inside custom elements to spread attributes. This avoids Svelte's component-slot transform from removing custom-element slot attributes during SSR.

Current Limitations

  • This package is in beta, so its runtime API and generated output may change before 1.0.
  • Attribute values/names and tag names are validated/escaped via Svelte's SSR serializer and covered by XSS regression tests, but the generated HTML and shadow DOM output have not had an independent security audit.
  • Custom element tags are detected by the presence of a dash in the tag name.
  • The consuming app must import the browser custom element module and register the matching SSR renderer.
  • The Vite plugin currently transforms Svelte files and injects a Svelte wrapper component.
  • Async SSR currently requires Svelte's experimental async compiler mode in the consuming Svelte app.