Skip to content
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ const Greeting = ({ name = 'World' }) => (
<p>Hello, {name}!</p>
);

register(Greeting, 'x-greeting', ['name'], { shadow: true, mode: 'open', adoptedStyleSheets: [] });
// ^ ^ ^ ^ ^ ^
// | HTML tag name | use shadow-dom | use adoptedStyleSheets
// Component definition Observed attributes Encapsulation mode for the shadow DOM tree
register(Greeting, 'x-greeting', ['name'], { shadow: true, mode: 'open', adoptedStyleSheets: [], serializable: true });
// ^ ^ ^ ^ ^ ^ ^
// | HTML tag name | use shadow-dom | use adoptedStyleSheets |
// Component definition Observed attributes Encapsulation mode for the shadow DOM tree shadow root may be serialized
```

> _**\* Note:** as per the [Custom Elements specification](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name), the tag name must contain a hyphen._
Expand Down Expand Up @@ -81,7 +81,7 @@ register(FullName, 'full-name');

### Passing slots as props

The `register()` function also accepts an optional fourth parameter, an options bag. At present, it allows you to opt-in to using shadow DOM for your custom element by setting the `shadow` property to `true`, and if so, you can also specify the encapsulation mode with `mode`, which can be either `'open'` or `'closed'`.
The `register()` function also accepts an optional fourth parameter, an options bag. At present, it allows you to opt-in to using shadow DOM for your custom element by setting the `shadow` property to `true`, and if so, you can also specify the encapsulation mode with `mode`, which can be either `'open'` or `'closed'`. Additionally, the shadow DOM may be serialized by setting `serializable` to `true`, which is useful for server-side rendering scenarios.

When using shadow DOM, you can make use of named `<slot>` elements in your component to forward the custom element's children into specific places in the shadow tree.

Expand All @@ -105,6 +105,22 @@ register(TextSelection, 'text-selection', [], { shadow: true });
</text-section>
```

### `serializable` option

If the `serializable` option is set, the shadow root may be serialized.

```js
register(MyComponent, 'my-element', [], {
shadow: true,
serializable: true
});

const el = document.querySelector('my-element');
const html = el.getHTML({ serializableShadowRoots: true });

console.log(el.shadowRoot.serializable);
```

### Static Properties

We support a number of static properties on your component that map to special behaviors of the custom element. These can be set on components like so:
Expand Down
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type Options =
shadow: true;
mode?: 'open' | 'closed';
adoptedStyleSheets?: CSSStyleSheet[];
serializable?: boolean;
};

/**
Expand Down
5 changes: 4 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ export default function register(Component, tagName, propNames, options) {
inst._vdomComponent = Component;

if (options && options.shadow) {
inst._root = inst.attachShadow({ mode: options.mode || 'open' });
inst._root = inst.attachShadow({
mode: options.mode || 'open',
serializable: options.serializable ?? false,
});

if (options.adoptedStyleSheets) {
inst._root.adoptedStyleSheets = options.adoptedStyleSheets;
Expand Down
16 changes: 16 additions & 0 deletions test/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -421,4 +421,20 @@ describe('web components', () => {
'<h1>Light DOM Children</h1><div><slot><p>Child 1</p><p>Child 2</p></slot></div>'
);
});

it('supports the `serializable` option', async () => {
function SerializableComponent() {
return <div className="serializable-child">Serializable Shadow DOM</div>;
}

registerElement(SerializableComponent, 'x-serializable', [], {
shadow: true,
serializable: true,
});

root.innerHTML = `<x-serializable></x-serializable>`;

const el = document.querySelector('x-serializable');
assert.isTrue(el.shadowRoot.serializable);
});
});