Introduction
Lit already has a way to use Preact signals in LitElement
, the custom element class, but if you're using the standalone Lit (lit-html
), you might want similar functionality. This post describes a small directive that provides this for you.
I'm hoping to show you how easy it is to create reactive UIs using these two tiny, but exceptionally powerful, libraries.
Understanding the Basics
@preact/signals
@preact-signals
is a library by the Preact team that offers "a reactive primitive for managing application state". Signals are reactive state primitives that automatically update components when their values change.
lit-html
Lit is a simple library for building fast, lightweight web components. It uses lit-html
for templating, allowing developers to define and render HTML templates efficiently. These templates can also be used "standalone" - outside of a web component - which is how I'm using them here.
Directive Concept
A directive in Lit is a function that customises the behaviour of a template expression. It offers a way to encapsulate and reuse complex rendering logic within templates.
SignalDirective: Bridging Preact and Lit
The SignalDirective
class serves as a bridge between Preact Signals and Lit. It allows Lit templates to reactively update based on changes in Preact Signal values.
Code Analysis
Class Definition
class SignalDirective extends Directive {
#signal: Signal | undefined;
...
}
The SignalDirective
extends Directive
from Lit, and it maintains a private signal instance.
Render Method
render(signal) {
if (this.#signal !== signal) {
this.#signal = signal;
effect(() => this.render(signal));
}
return html`${signal.value}`;
}
-
Signal Binding: The method binds a
Signal
object to the directive. -
Effect Hook: Utilizes Preact's
effect
to trigger re-rendering upon signal change.
Here, we're first checking if we have already cached this signal. If not, we subscribe to its changes by using the @preact/signals
effect
hook
Exporting the Directive
export const litSignal = directive(SignalDirective);
The litSignal
function is exported, making it available for use in Lit templates.
Usage Example
Signal Initialisation
const tagsForCurrentNoteSignal = signal<Tag[]>([]);
A signal is created with an initial empty array of tags.
Computed Value
const tagNames = computed(() =>
tagsForCurrentNoteSignal.value.map((tag) => html`${tag.name}`),
);
A computed value is created that maps each tag to a Lit template result.
Template Utilization
export function template(): TemplateResult {
return html`<div>${litSignal(tagNames)}</div>`;
}
The litSignal
directive is used within a Lit template, allowing the div element to reactively display the transformed tag names.
Conclusion
The integration of Preact Signals with Lit through a custom directive presents an extremely simple way of defining reactive UIs. I hope that you found this useful.
Top comments (0)