Web Components
Reef pairs very nicely with native Web Components.
Reactive Web Components
You can use Reef’s signal()
and component()
methods to reactively update the content inside a Web Component.
Whenever the signal
object’s data is updated, the Web Component content will automatically get updated.
<todo-list></todo-list>
// Create a reactive signal
let data = signal({
heading: 'My Todos',
todos: ['Swim', 'Climb', 'Jump', 'Play'],
emoji: '👋🎉'
});
// Create a native Web Component
customElements.define('todo-list', class extends HTMLElement {
// Use Reef to reactively render UI into the Web Component
constructor () {
super();
component(this, this.template);
}
// Define the template
template () {
let {heading, todos, emoji} = data;
return `
<h1>${heading} ${emoji}</h1>
<ul>
${todos.map(function (todo) {
return `<li key="${todo}">${todo}</li>`;
}).join('')}
</ul>`;
}
});
Scoping data
Web Components make it easy to scope data to the component.
You can use the signal()
method to attach reactive data to the component instead of a global object.
<count-up></count-up>
customElements.define('count-up', class extends HTMLElement {
// Instantiate the component
constructor () {
super();
this.count = signal(0);
this.events = {countUp: () => this.count.value++};
component(this, this.template, {events: this.events});
}
// The UI template
template = () => {
return `<button onclick="countUp()">Clicked ${this.count.value} times</button>`;
}
});
To reduce the amount of diffing that occurs with multiple components, you can use the crypto.randomUUID()
method to create a unique ID and signal
namespace for each component.
customElements.define('count-up', class extends HTMLElement {
// Instantiate the component
constructor () {
super();
this.uuid = crypto.randomUUID();
this.count = signal(0, this.uuid);
this.events = {countUp: () => this.count.value++};
component(this, this.template, {
events: this.events,
signals: [this.uuid]
});
}
// The UI template
template = () => {
return `<button onclick="countUp()">Clicked ${this.count.value} times</button>`;
}
});
In Templates
You can include native web components inside the HTML template strings that get rendered by Reef.
Because web components control their own internal content, Reef will modify element attributes, but will not diff content within them.
// Create a reactive store
let data = store({
heading: 'My Counter',
emoji: '👋🎉'
});
// Create a template
function template () {
let {heading, emoji} = data;
return `
<h1>${heading} ${emoji}</h1>
<count-up></count-up>`;
}
// Reef will NOT diff the content of the count-up element
data.heading = 'Count it';
This also means you can safely nest Web Components inside other Web Components.