mirror of https://github.com/sveltejs/svelte
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
86 lines
4.5 KiB
86 lines
4.5 KiB
2 years ago
|
---
|
||
|
title: 'Custom elements API'
|
||
|
---
|
||
|
|
||
|
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/special-elements#svelte-options).
|
||
|
|
||
|
```svelte
|
||
|
<svelte:options tag="my-element" />
|
||
|
|
||
|
<script>
|
||
|
export let name = 'world';
|
||
|
</script>
|
||
|
|
||
|
<h1>Hello {name}!</h1>
|
||
|
<slot />
|
||
|
```
|
||
|
|
||
2 years ago
|
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`.
|
||
2 years ago
|
|
||
|
```js
|
||
2 years ago
|
// @noErrors
|
||
2 years ago
|
import MyElement from './MyElement.svelte';
|
||
|
|
||
|
customElements.define('my-element', MyElement);
|
||
|
```
|
||
|
|
||
|
Once a custom element has been defined, it can be used as a regular DOM element:
|
||
|
|
||
|
```js
|
||
|
document.body.innerHTML = `
|
||
|
<my-element>
|
||
|
<p>This is some slotted content</p>
|
||
|
</my-element>
|
||
|
`;
|
||
|
```
|
||
|
|
||
2 years ago
|
By default, custom elements are compiled with `accessors: true`, which means that any [props](/docs/basic-markup#attributes-and-props) are exposed as properties of the DOM element (as well as being readable/writable as attributes, where possible).
|
||
2 years ago
|
|
||
|
To prevent this, add `accessors={false}` to `<svelte:options>`.
|
||
|
|
||
|
```js
|
||
2 years ago
|
// @noErrors
|
||
2 years ago
|
const el = document.querySelector('my-element');
|
||
|
|
||
|
// get the current value of the 'name' prop
|
||
|
console.log(el.name);
|
||
|
|
||
|
// set a new value, updating the shadow DOM
|
||
|
el.name = 'everybody';
|
||
|
```
|
||
|
|
||
2 years ago
|
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:
|
||
|
|
||
|
- `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>"`.
|
||
|
- `reflect: boolean`: By default, updated prop values do not reflect back to the DOM. To enable this behavior, set `reflect: true`.
|
||
|
- `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"`
|
||
|
|
||
|
```svelte
|
||
|
<svelte:options
|
||
|
customElement={{
|
||
2 years ago
|
tag: 'custom-element',
|
||
|
shadow: 'none',
|
||
2 years ago
|
props: {
|
||
2 years ago
|
name: { reflect: true, type: 'Number', attribute: 'element-index' }
|
||
|
}
|
||
2 years ago
|
}}
|
||
|
/>
|
||
|
|
||
|
<script>
|
||
|
export let elementIndex;
|
||
|
</script>
|
||
|
|
||
|
...
|
||
|
```
|
||
|
|
||
2 years ago
|
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:
|
||
|
|
||
2 years ago
|
- 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
|
||
2 years ago
|
- Instead of being extracted out as a separate .css file, styles are inlined into the component as a JavaScript string
|
||
|
- Custom elements are not generally suitable for server-side rendering, as the shadow DOM is invisible until JavaScript loads
|
||
|
- 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
|
||
2 years ago
|
- 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
|
||
2 years ago
|
- Polyfills are required to support older browsers
|
||
2 years ago
|
|
||
2 years ago
|
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.
|