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
Signalobject to the directive. -
Effect Hook: Utilizes Preact's
effectto 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)