/svelte-retag

Light DOM custom element wrapper for Svelte 3 and 4 with slots, context and Vite HMR support

Primary LanguageJavaScriptMIT LicenseMIT

svelte-retag

Unit Tests

A web component wrapper for Svelte 3 and 4. Embeds your Svelte app or components inside custom elements using the light DOM or shadow DOM. Automatically forwards all slots and attributes to your Svelte app.

Demo: https://svelte-retag.vercel.app/

Core Features

  • 🌟 Light DOM: Allows you to render your Svelte components as custom elements in the light DOM as usual.
  • 🎰 Slots & Nesting: Supports default and named slots in the light DOM (with nesting).
  • 🧭 Context: Use setContext() and getContext() and just compose your components as custom elements as you normally would (see live tab demo). Supports nesting.
  • Vite HMR: Unlike Svelte, these custom elements are also compatible with Vite's HMR. It avoids the infamous Failed to execute 'define' on 'CustomElementRegistry' errors.
  • 🏃‍♂️ IIFE/UMD: Supports building to iife and umd for eager rendering to the light DOM, as soon as the parser encounters the custom element. Reduces CLS (Cumulative Layout Shift), making it interactive more quickly without waiting for defer'd scripts (such as modules).
  • Composability: svelte-retag gives you the flexibility to use your component as you normally would within Svelte and as a custom element outside of Svelte (supporting both <ExampleComponent /> and <example-component>). For details on how to use with PHP, Python, Ruby, etc., see Backend Integration below.
  • 💼 Portability: Enables the freedom to utilize your Svelte components anywhere custom elements are supported, regardless of the stack (great for upgrading legacy applications).

Why?

Svelte already allows you to compile your components to custom elements. However, it's missing a few extra features:

How do I use it?

Installation & Quick Start

npm install svelte-retag

Check out the Hello World demo to see it in action yourself and for instructions on how to get started from scratch.

If you're running a non-JavaScript backend such as PHP, Python, Ruby, etc. and would still like to use Vite (but cannot rely solely on Vite for local development), see Vite's Backend Integration documentation. This will guide you on how to run both your specific backend and Vite's development server simultaneously.

Svelte vs. SvelteKit

Note that if you already have an existing backend, it is recommended that you just install svelte and not @sveltejs/kit since the extra metaframework features of SvelteKit (such as routing) may not be necessary. SvelteKit is now installed by default in the official documentation, so the extra complexity may be confusing when you are already running a backend and just using svelte-retag to add web components into an existing site.

Demo Code

Add the following to your main entrypoint. If you are using Vite, this would likely be src/main.js.

import svelteRetag from 'svelte-retag';
import HelloWorld from './HelloWorld.svelte';

svelteRetag({
	component: HelloWorld,
	tagname: 'hello-world',

	// Optional:
	attributes: true, // Forward all attributes to your component, or set to explicit list of attributes, e.g. ['greetperson'] or leave empty
	shadow: false, // Use the light DOM
	href: '/your/stylesheet.css', // Only necessary if shadow is true
});

And in the HelloWorld.svelte Svelte component:

<script>
	export let greetPerson = 'World';
</script>

<h1>Hello {greetPerson}!</h1>

Now anywhere you use the <hello-world> tag, you'll get a Svelte component. Note that you must set your tag name to anything containing a dash.

To align with Svelte 4, attributes are automatically converted to lowercase (following the Lit-style naming convention). So, greetPerson on your component would be automatically made available as greetperson on your custom element.

<hello-world greetperson="Cris"></hello-world>

For more info on getting started, take a look at the Hello World demo.

Options 🛠

Option Default Description
component (required) The constructor for your Svelte component (from import)
tagname (required) The custom element tag name to use (must contain a dash)
attributes [] Array (legacy): Explicit list of attributes to reactively forward to your component. Attributes must be the lowercase version of your Svelte component props (similar to Lit).

Boolean (recommended): If set to true, will automatically forward all attributes to your component props. If false, will not forward anything.

Note: In v2, this option will be removed and all attributes will be forwarded by default (for consistency with Svelte 4's custom elements, see #36).
shadow false Optional. Indicates if this component should use shadow DOM.

Note: Only basic support for shadow DOM is currently provided. See #6.
ignoreCommonAttribWarnings false Optional. Suppresses warnings in development mode about common attributes (such as id, class, style and data-*) if they don't already exist on the component. Set to an array to customize the list of ignored attributes.
href '' Optional. URL to your stylesheet. Allows you to ensure your styles are included in the shadow DOM. This option is only useful when shadow is set to true.

Note: For portability, svelte-retag's API is fully backward compatible with svelte-tag@^1.0.0.

For experimental options, click here.

Experimental Options 👨‍🔬

Warning: These features are not production ready and are purely experimental.

Option Default Description
hydratable false Optional. Compatible with Light DOM rendering only.

If enabled, allows for SSR/SSG of custom elements managed by svelte-retag by including extra markup so that they can be initialized client-side from pre-rendered HTML (a.k.a. "hydrated"). See hydration demo here.

Do not enable this for regular use on the front-end. Enable this only during SSR/SSG to allow for proper initialization and only if you plan on re-rendering a second time (e.g. first in SSG/SSR and then finally in-browser).

Why does this exist?

The initial use case for this is to address rendering bugs in Percy which renders the components 2 times (first in a local headless Chrome browser, then a second time in the cloud).

Change Log

v1

Changes since forking from svelte-tag (upstream):

  • Migrate to Vitest for unit testing (see crisward/svelte-tag#14)
  • Update logo
  • Fix nested slot support (#5)
  • Better support for slots during early execution of IIFE compiled packages, i.e. use MutationObserver to watch for light DOM slots during initial parsing (see #7)
  • Support Lit-style lowercase props (see #9)
  • Svelte 4 support (tested)
  • Support context (see #10, PR at #18)
  • Add demos to vercel site (see #11)
  • Add step-by-step instructions and provided a simple MVP example (#24)
  • Automatically forward all attributes to component (i.e. attributes: true) (#34)
  • Add better TypeScript support (#33)
  • Add ability to suppress warnings for common attributes (such as id, class, style and data-*) if they don't already exist on the component (#54)

v2

See the milestones page for changes planned in upcoming versions. Please be aware that until the version is officially released, the features slated for a particular version are subject to change!

Support & Contribution

Features: The API for this package is intentionally minimal and features that are outside of the scope of the core features listed above are not likely to be considered. However, for stability (and due to time constraints), new features will still be considered if they are small or will have little or no impact on existing functionality.

To report bugs or improvements, please open an issue and explain in as much detail as possible what the bug is and how to reproduce it. Please make sure that you only submit an issue if you have verified that it requires a change to svelte-retag itself.

PR's: If you'd like to contribute, please feel free to open a PR, however: If possible, please attach it to an existing issue to ensure that discussion regarding your pull request isn't lost (in case it cannot be merged for whatever reason).

Attribution