Version v2.0.8 Size 9.4 KB gzip Dependencies Zero dependencies
Why Ore?
Ore keeps custom elements functional and signal-driven while giving you direct control over templates, lifecycle hooks, host bindings, and form-associated behavior.
ts
// Before — vanilla custom element boilerplate
class MyCounter extends HTMLElement {
#count = 0;
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.#render();
}
#render() {
this.shadowRoot!.innerHTML = `<button>${this.#count}</button>`;
this.shadowRoot!.querySelector('button')!.onclick = () => {
this.#count++;
this.#render();
};
}
}
customElements.define('my-counter', MyCounter);
// After — Ore
import { signal } from '@vielzeug/ripple';
import { define, html } from '@vielzeug/ore';
define('my-counter', {
setup() {
const count = signal(0);
return html`<button @click=${() => count.value++}>${count}</button>`;
},
});| Feature | Ore | Lit | Stencil |
|---|---|---|---|
| Bundle size | 9.4 KB | ~12 kB | ~60 kB+ toolchain |
| Signal-first runtime | |||
| Functional component setup | Partial | ||
| Typed prop helpers | Partial | ||
| Host binding helpers | Partial | Partial | |
| Form-associated helpers | Manual | Partial | |
| Zero dependencies |
Use Ore when you want typed, signal-driven custom elements with minimal runtime overhead and no framework lock-in.
Consider Lit when you need a mature ecosystem with wide community adoption and don't need signal-based reactivity.
Installation
sh
pnpm add @vielzeug/ore @vielzeug/ripplesh
npm install @vielzeug/ore @vielzeug/ripplesh
yarn add @vielzeug/ore @vielzeug/rippleQuick Start
ts
import { computed, signal } from '@vielzeug/ripple';
import { bind, css, define, html, onMounted, prop } from '@vielzeug/ore';
define('my-counter', {
props: {
label: prop.string('Count'),
step: prop.number(1),
},
styles: [
css`
:host {
display: inline-grid;
gap: 0.5rem;
}
`,
],
setup(props) {
const count = signal(0);
const doubled = computed(() => count.value * 2);
bind({ class: { 'is-positive': () => count.value > 0 } });
onMounted(() => console.log('mounted'));
return html`
<button @click=${() => (count.value += props.step.value)}>${props.label}: ${count}</button>
<p>Doubled: ${doubled}</p>
`;
},
});Features
- Signal-first runtime with
signal,computed,watch,batchfrom@vielzeug/ripple— import them directly - Functional component authoring via
define(tag, { props, setup, styles, formAssociated }) - Props via
prop.*helpers (prop.string,prop.number,prop.bool,prop.oneOf,prop.json,prop.data) or rawPropDefobjects setup(props)takes only props and returns anHTMLResultdirectly:return html\...``- Lifecycle hooks —
onMounted,onCleanup,onEvent,onElement,watchEffect— plain functions imported from@vielzeug/ore, called directly fromsetup()or any composable it calls - Directives:
each(keyed reactive list rendering),classMap,styleMap,when,live,unsafeHtml - Host bindings via
bind({ attr, class, style, on })— pass{ target: el }to bind any off-host element - Reactive ARIA sync via
bind({ aria }, { target })— appliesaria-*attributes reactively to any element, auto-cleanup on disconnect - Context via
provide(key, value)/inject(key); typed emit/slots viauseEmit<Emits>()/useSlots<SlotNames>() - Form-associated
useField()and observer helpers are root exports - Testing utilities (
@vielzeug/ore/testing) —mount,renderHook,flush,cleanup - Generic testing utilities (scoped queries, named event dispatchers, and async waits) are exported by
@vielzeug/assay - Debug utilities (
@vielzeug/ore/testing) —debugFlush()for diagnosing update timing
Package Entry Points
| Import | Purpose |
|---|---|
@vielzeug/ore | All browser runtime APIs: components, directives, useField, and observers |
@vielzeug/ore/testing | Ore-specific mounting, lifecycle flushing, hooks, cleanup, and form internals |
@vielzeug/assay | Generic DOM events, scoped queries, and async waiting |