Ecopages0.2.0

Radiant

@ecopages/radiant is an ultra-minimalist library for building reactive Web Components. It is designed to play perfectly with Ecopages' dependency management and SSR capabilities.

Introduction

Radiant uses standard CUSTOM Elements with a thin layer of decorators to handle reactivity and DOM queries. Use it when you need interactivity without the overhead of a full framework.

Component Example

import { customElement, reactiveProp, onEvent, query } from '@ecopages/radiant';
 
@customElement('radiant-counter')
export class RadiantCounter extends HTMLElement {
    @reactiveProp() 
    value = 0;
 
    @query('span') 
    display!: HTMLSpanElement;
 
    @onEvent('click', 'button.increment')
    inc() {
        this.value++;
    }
 
    render() {
        this.display.textContent = this.value.toString();
    }
}

Decorators

@customElement(tag: string)

Registers the class as a custom element with the given tag name.

@reactiveProp()

Marks a property as reactive. When the property changes, it automatically triggers a re-render or a specific update logic.

@onEvent(type: string, selector?: string)

Declaratively attaches an event listener. If a selector is provided, it uses event delegation.

@query(selector: string)

A shorthand for this.querySelector(selector). Returns the element every time it is accessed.

Using with Ecopages

Radiant hosts in JSX apps use Ecopages JSX (ecopagesJsxPlugin() enables Radiant SSR by default). See Creating Components for the full eco.component() pattern.

  1. Put element registration in a *.script.ts file (call customElements.define or use @customElement).
  2. Wrap the host in eco.component() and declare the script with ssr: true so Ecopages evaluates it on the server before SSR. Use import type from the script module when you only need props—no separate value import in the wrapper file.
  3. Import the wrapper from pages; Ecopages discovers local eco.component() imports automatically.

counter.script.ts:

export type CounterProps = { count?: number };
 
export class CounterElement extends HTMLElement {
	static observedAttributes = ['count'];
 
	connectedCallback() {
		this.textContent = String(this.getAttribute('count') ?? 0);
	}
 
	attributeChangedCallback() {
		this.textContent = String(this.getAttribute('count') ?? 0);
	}
}
 
if (!customElements.get('radiant-counter')) {
	customElements.define('radiant-counter', CounterElement);
}

counter.tsx:

import { eco } from '@ecopages/core';
import type { JsxRenderable } from '@ecopages/jsx';
import type { CounterProps } from './counter.script';
 
export const Counter = eco.component<CounterProps, JsxRenderable>({
	dependencies: {
		scripts: [{ src: './counter.script.ts', ssr: true }],
	},
	render: ({ count = 0 }) => <radiant-counter count={count} />,
});

Add lazy on the script entry when browser delivery should wait for idle, interaction, or visibility. lazy does not skip server registration when ssr: true is set.