You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is an overhaul of custom elements in Svelte. Instead of compiling to a custom element class, the Svelte component class is mostly preserved as-is. Instead a wrapper is introduced which wraps a Svelte component constructor and returns a HTML element constructor. This has a couple of advantages:
- component can be used both as a custom element as well as a regular component. This allows creating one wrapper custom element and using regular Svelte components inside. Fixes#3594, fixes#3128, fixes#4274, fixes#5486, fixes#3422, fixes#2969, helps with sveltejs/kit#4502
- all components are compiled with injected styles (inlined through Javascript), fixes#4274
- the wrapper instantiates the component in `connectedCallback` and disconnects it in `disconnectedCallback` (but only after one tick, because this could be a element move). Mount/destroy works as expected inside, fixes#5989, fixes#8191
- the wrapper forwards `addEventListener` calls to `component.$on`, which allows to listen to custom events, fixes#3119, closes#4142
- some things are hard to auto-configure, like attribute hyphen preferences or whether or not setting a property should reflect back to the attribute. This is why `<svelte:options customElement={..}>` can also take an object to modify such aspects. This option allows to specify whether setting a prop should be reflected back to the attribute (default `false`), what to use when converting the property to the attribute value and vice versa (through `type`, default `String`, or when `export let prop = false` then `Boolean`), and what the corresponding attribute for the property is (`attribute`, default lowercased prop name). These options are heavily inspired by lit: https://lit.dev/docs/components/properties. Closes#7638, fixes#5705
- adds a `shadowdom` option to control whether or not encapsulate the custom element. Closes#4330, closes#1748
Breaking changes:
- Wrapped Svelte component now stays as a regular Svelte component (invokeing it like before with `new Component({ target: ..})` won't create a custom element). Its custom element constructor is now a static property named `element` on the class (`Component.element`) and should be regularly invoked through setting it in the html.
- The timing of mount/destroy/update is different. Mount/destroy/updating a prop all happen after a tick, so `shadowRoot.innerHTML` won't immediately reflect the change (Lit does this too). If you rely on it, you need to await a promise
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1
Original file line number
Diff line number
Diff line change
@@ -8,6 +8,7 @@
8
8
***breaking** Stricter types for `createEventDispatcher` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224))
9
9
***breaking** Stricter types for `Action` and `ActionReturn` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224))
10
10
***breaking** Stricter types for `onMount` - now throws a type error when returning a function asynchronously to catch potential mistakes around callback functions (see PR for migration instructions) ([#8136](https://github.com/sveltejs/svelte/pull/8136))
11
+
***breaking** Overhaul and drastically improve creating custom elements with Svelte (see PR for list of changes and migration instructions) ([#8457](https://github.com/sveltejs/svelte/pull/8457))
11
12
***breaking** Deprecate `SvelteComponentTyped`, use `SvelteComponent` instead ([#8512](https://github.com/sveltejs/svelte/pull/8512))
Copy file name to clipboardExpand all lines: site/content/docs/03-template-syntax.md
+2-2
Original file line number
Diff line number
Diff line change
@@ -1825,10 +1825,10 @@ The `<svelte:options>` element provides a place to specify per-component compile
1825
1825
*`accessors={true}` — adds getters and setters for the component's props
1826
1826
*`accessors={false}` — the default
1827
1827
*`namespace="..."` — the namespace where this component will be used, most commonly "svg"; use the "foreign" namespace to opt out of case-insensitive attribute names and HTML-specific warnings
1828
-
*`tag="..."` — the name to use when compiling this component as a custom element
1828
+
*`customElement="..."` — the name to use when compiling this component as a custom element
Copy file name to clipboardExpand all lines: site/content/docs/04-run-time.md
+32-5
Original file line number
Diff line number
Diff line change
@@ -1118,7 +1118,7 @@ app.count += 1;
1118
1118
Svelte components can also be compiled to custom elements (aka web components) using the `customElement: true` compiler option. You should specify a tag name for the component using the `<svelte:options>`[element](/docs#template-syntax-svelte-options).
1119
1119
1120
1120
```sv
1121
-
<svelte:options tag="my-element" />
1121
+
<svelte:options customElement="my-element" />
1122
1122
1123
1123
<script>
1124
1124
export let name = 'world';
@@ -1130,12 +1130,12 @@ Svelte components can also be compiled to custom elements (aka web components) u
1130
1130
1131
1131
---
1132
1132
1133
-
Alternatively, use `tag={null}`to indicate that the consumer of the custom element should name it.
1133
+
You can leave out the tag name for any of your inner components which you don't want to expose and use them like regular Svelte components. Consumers of the component can still name it afterwards if needed, using the static `element` property which contains the custom element constructor and which is available when the `customElement` compiler option is `true`.
When constructing a custom element, you can tailor several aspects by defining `customElement` as an object within `<svelte:options>`. This object comprises a mandatory `tag` property for the custom element's name, an optional `shadow` property that can be set to `"none"` to forgo shadow root creation, and a `props` option, which offers the following settings:
1172
+
1173
+
-`attribute: string`: To update a custom element's prop, you have two alternatives: either set the property on the custom element's reference as illustrated above or use an HTML attribute. For the latter, the default attribute name is the lowercase property name. Modify this by assigning `attribute: "<desired name>"`.
1174
+
-`reflect: boolean`: By default, updated prop values do not reflect back to the DOM. To enable this behavior, set `reflect: true`.
1175
+
-`type: 'String' | 'Boolean' | 'Number' | 'Array' | 'Object'`: While converting an attribute value to a prop value and reflecting it back, the prop value is assumed to be a `String` by default. This may not always be accurate. For instance, for a number type, define it using `type: "Number"`
Custom elements can be a useful way to package components for consumption in a non-Svelte app, as they will work with vanilla HTML and JavaScript as well as [most frameworks](https://custom-elements-everywhere.com/). There are, however, some important differences to be aware of:
1170
1196
1171
-
* Styles are *encapsulated*, rather than merely *scoped*. This means that any non-component styles (such as you might have in a `global.css` file) will not apply to the custom element, including styles with the `:global(...)` modifier
1197
+
* Styles are *encapsulated*, rather than merely *scoped* (unless you set `shadow: "none"`). This means that any non-component styles (such as you might have in a `global.css` file) will not apply to the custom element, including styles with the `:global(...)` modifier
1172
1198
* Instead of being extracted out as a separate .css file, styles are inlined into the component as a JavaScript string
1173
1199
* Custom elements are not generally suitable for server-side rendering, as the shadow DOM is invisible until JavaScript loads
1174
1200
* In Svelte, slotted content renders *lazily*. In the DOM, it renders *eagerly*. In other words, it will always be created even if the component's `<slot>` element is inside an `{#if ...}` block. Similarly, including a `<slot>` in an `{#each ...}` block will not cause the slotted content to be rendered multiple times
1175
-
* The `let:` directive has no effect
1201
+
* The `let:` directive has no effect, because custom elements do not have a way to pass data to the parent component that fills the slot
1176
1202
* Polyfills are required to support older browsers
1177
1203
1204
+
When a custom element written with Svelte is created or updated, the shadow dom will reflect the value in the next tick, not immediately. This way updates can be batched, and DOM moves which temporarily (but synchronously) detach the element from the DOM don't lead to unmounting the inner component.
Copy file name to clipboardExpand all lines: src/compiler/compile/compiler_warnings.ts
+4-4
Original file line number
Diff line number
Diff line change
@@ -6,9 +6,9 @@ import { ARIAPropertyDefinition } from 'aria-query';
6
6
* @internal
7
7
*/
8
8
exportdefault{
9
-
custom_element_no_tag: {
10
-
code: 'custom-element-no-tag',
11
-
message: 'No custom element \'tag\' option was specified. To automatically register a custom element, specify a name with a hyphen in it, e.g. <svelte:options tag="my-thing"/>. To hide this warning, use <svelte:options tag={null}/>'
9
+
tag_option_deprecated: {
10
+
code: 'tag-option-deprecated',
11
+
message: "'tag' option is deprecated — use 'customElement' instead"
0 commit comments