@nisli/core
v0.46.0
Published
A reactive web component framework. Signals, templates, dependency injection — no build step, no virtual DOM, no dependencies.
Downloads
290
Maintainers
Readme
nisli
A reactive web component framework. Signals, templates, dependency injection — no build step, no virtual DOM, no dependencies.
Install
npm install @nisli/coreQuick Start
import { signal, component, html } from '@nisli/core';
const Counter = component('x-counter', () => {
const count = signal(0);
return html`
<button @click=${() => count.set(count() + 1)}>
Count: ${count}
</button>
`;
});Features
- Signals — Fine-grained reactivity with
signal,computed,effect - Components — Web Components with a composition-style setup function
- Templates — Tagged template literals with automatic signal binding
- Dependency Injection —
injectany class as a singleton,provideoverrides for testing - Queries — Declarative async data loading with caching and auto-refetch
- Event Emitters — Typed event bus with auto-disposal in component context
- Lifecycle —
onMount,onCleanup,useHostEvent - Refs — Direct element access via
ref() - Control Flow —
when()for toggles,each()for keyed list rendering
API
// Reactivity
signal(value) // Reactive signal
computed(() => expr) // Derived signal (lazy, cached)
effect(() => { ... }) // Side effect that tracks dependencies
// Components
component('tag-name', (props, host) => html`...`)
component<Props>('tag-name', { props: [...] }, (props, host) => html`...`)
// Templates — signals are implicit, no .value needed
html`<div>${count}</div>`
html`<button @click=${handler}>Go</button>`
html`<div class:active=${isActive}>...</div>`
// Control flow
when(condition, () => html`...`)
each(items, item => item.id, (item) => html`...`)
// Dependency injection — class IS the token
inject(MyService) // Auto-creates singleton
provide(MyService, () => mock) // Override (testing)
// Queries
const { data, loading, error } = query(
() => ['tasks', id.value], // Cache key (tracked)
() => api.getTasks(id.value), // Fetcher
)
// Lifecycle
onMount(() => { ... })
onCleanup(() => { ... })
useHostEvent('click', handler)
// Refs
const el = ref<HTMLDivElement>()
html`<div ${el}>...</div>`
// Events
class Nav extends Emitter<{ select: { id: string } }> {}
inject(Nav).emit('select', { id })
inject(Nav).on('select', ({ id }) => { ... })Size
~2,600 lines of TypeScript. Zero dependencies.
Inspiration
nisli stands on the shoulders of giants:
- React — Component model, declarative UI
- Solid — Signals, fine-grained reactivity, no virtual DOM
- Lit — Web Components, tagged template literals
- Angular — Dependency injection, typed tokens
- Vue — Composition-style setup functions, reactive system design
License
MIT
