Declarative Web Components built on the temples template
engine. Inherit from TemplesComponent instead of HTMLElement, and the template, state, and
event wiring become declarative.
bun add @temples/components@temples/engine is installed automatically as a dependency.
Declare a component with TemplesComponent.define(). The class carries the state and the handler
methods. The define call carries the template, the attributes, and the event bindings:
import { TemplesComponent } from "@temples/components";
import template from "./flipping-card.html" with { type: "text" };
import "./flipping-card.css";
export class FlippingCard extends TemplesComponent {
constructor() {
super({ flipped: false });
}
flip() {
this.state.flipped = true; // reactive: mutation re-renders
}
onUpdated(event) {
// event.detail carries the payload
}
}
TemplesComponent.define("flipping-card", FlippingCard, {
template,
attributes: { title: "string", flipped: "boolean" },
events: {
"click .flip-btn": "flip",
"shopping-item:updated": "onUpdated",
},
});-
Each name in the
attributesmap is an observed attribute. It flows intothis.state, coerced by its declared type ("string" | "boolean" | "number" | "json"). -
this.stateis a deep reactive proxy: any mutation triggers a re-render of the component's bindings. -
Attributes on the tag are the single source of truth. The optional
globalStoreoption seeds attributes that the tag does not set. -
In TypeScript, the class declares its complete state shape with the
TemplesComponent<T>parameter. The compiler types everystateaccess, and the initialsuper({ ... })literal is checked against the shape:interface CardState { title: string; flipped: boolean; } export class FlippingCard extends TemplesComponent<CardState> { constructor() { super({ title: "", flipped: false }); } }
- The
eventsoption maps bindings to handler method names. Handlers run withthisbound to the component instance and receive the event. - A binding with a space (
"click .flip-btn") is a delegated DOM event inside the component. - A binding without a space (
"shopping-item:updated") is an inter-component message delivered over a shared bus. Send one withthis.emit("updated", detail)— it is delivered as"flipping-card:updated"to any subscribed component.
Components use the Light DOM. Scope the rules in the component stylesheet by the component's tag name:
flipping-card {
display: inline-block;
}Full guides and API reference: https://zipang.github.io/temples/
MIT