π§© Core Concepts
UIElement is a minimalist library that builds on the power of Web Components, extending them with lightweight, reactive state management.
Anatomy of a UIElement Web Component
Extending Web Components
UIElement extends the native HTMLElement class to add state management and reactivity to Web Components. You can create your own custom elements by extending UIElement.
class MyComponent extends UIElement {
/* component definition */
}
Defining Custom Elements
You must define the custom element's tag name in kebab-case using the define() method to make it available for use in HTML.
MyComponent.define('my-component');
Make sure to use valid custom element names and avoid defining the same element more than once.
Using the Custom Element
Once defined, you can use the custom element in HTML just like any other tag.
<my-component>Content goes here</my-component>
Web Components Lifecycle
Each UIElement component goes through a lifecycle. Hereβs how the core lifecycle callbacks work:
constructor()
Called when the element is created. Avoid accessing attributes or DOM elements here; use connectedCallback() instead.
connectedCallback()
Called when the component is attached to the DOM. This is where you can initialize state, add event listeners, and set up reactive effects.
disconnectedCallback()
Called when the component is removed from the DOM. Clean up any external event listeners here.
adoptedCallback()
Called when the component is moved to a new document. Rarely needed unless you're dealing with advanced scenarios like drag-and-drop.
attributeChangedCallback()
Called when an observed attribute is changed. UIElement handles this automatically, so usually, there's no need to override it.
Signals & State Management in UIElement
Signals are the fundamental units of reactive state in UIElement. You can create and update signals using this.set().
this.set('count', 0); // Create a signal
this.set('isEven', () => this.get('count') % 2 === 0); // Create a derived signal
Signals automatically trigger updates to elements in the DOM that are bound to them.
Auto-Effects & Reactive Bindings
Auto-effects like setText() automatically update the DOM when signals change. Use the .map() method to bind signals to HTML elements.
this.first('.count').map(setText('count'));
The content of the .count element will update whenever the count signal changes.
Attributes & State Synchronization
Observed attributes in UIElement are automatically converted into reactive signals. You can declare them using the observedAttributes static property.
static observedAttributes = ['count'];
To parse and map attributes to specific state types, use the attributeMapping property.
static attributeMapping = {
count: 'integer',
};
You can also use custom parsing functions for more complex attribute handling.
attributeMapping = {
date: (value) => new Date(value),
};
Handling Events & State Changes
Use .map() and on() to attach event listeners to elements. These event handlers can trigger state changes and update the DOM.
this.first('#increment').map(on('click', () => this.set('count', (prev) => prev + 1)));
User interactions like clicks or form inputs can trigger signal updates, which are reflected immediately in the DOM.
Efficient & Fine-Grained Updates
UIElement efficiently updates only the parts of the DOM affected by signal changes, avoiding full re-renders. This ensures optimal performance and precise updates.
Unlike virtual DOM approaches, updates are fine-grained, affecting only the relevant elements in the view.
Outlook: State Sharing & Context
While most components manage their own state, sometimes you need to share state between components. UIElement allows this through pass() for parent-child state sharing and context providers for more advanced scenarios.
These advanced features will be covered in detail in later sections on Best Practices & Patterns and Advanced Topics.