# @vandeurenglenn/lite

> See [LLMS.md](./LLMS.md) for an AI-oriented guide with the public API, efficient usage patterns, and implementation-aware caveats.

Latest version **1.3.0** (published 2026-07-25) · MIT license · 0 weekly downloads

## Install

```sh
npm install @vandeurenglenn/lite
pnpm add @vandeurenglenn/lite
yarn add @vandeurenglenn/lite
bun add @vandeurenglenn/lite
```

Provides the command `lite`.

## Health

**Score 60/100 (C)** — status: active.

Positive: esm support; no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types.

## Facts

| | |
|---|---|
| Version | 1.3.0 |
| Published | 2026-07-25 |
| First published | 2024-02-02 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | ESM |
| Dependencies | 4 |
| Unpacked size | 90.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Maintainers | vandeurenglenn |

## Links

- npm: https://www.npmjs.com/package/@vandeurenglenn/lite
- npm.io page: https://npm.io/package/@vandeurenglenn/lite

## Dependencies (4)

- [ws](https://npm.io/package/ws.md) ^8.21.1
- [lit](https://npm.io/package/lit.md) ^3.3.3
- [lit-html](https://npm.io/package/lit-html.md) ^3.3.3
- [@vandeurenglenn/little-pubsub](https://npm.io/package/@vandeurenglenn/little-pubsub.md) ^1.5.2

## Recent versions

- 1.3.0 (latest) — 2026-07-25
- 1.2.4 — 2026-07-25
- 1.2.3 — 2026-06-02
- 1.2.2 — 2026-05-16
- 1.2.1 — 2026-05-16
- 1.2.0 — 2026-05-10
- 1.1.3 — 2026-04-11
- 1.1.2 — 2026-04-09
- 1.1.1 — 2026-04-08
- 1.1.0 — 2026-04-04
- 1.0.2 — 2026-03-29
- 1.0.1 — 2026-03-29
- 1.0.0 — 2026-01-07
- 0.3.1 — 2025-09-24
- 0.3.0 — 2025-09-18
- … 68 more at https://npm.io/package/@vandeurenglenn/lite/versions

## README

# lite

## AI guide

See [LLMS.md](./LLMS.md) for an AI-oriented guide with the public API, efficient usage patterns, and implementation-aware caveats.

## install

```sh
npm i @vandeurenglenn/lite
```

## why lite

Lite is useful when you want a tiny custom-element base with modern decorators, but without heavy framework overhead.

We are big fans of Lit. Lite exists for a specific rendering philosophy: show UI as soon as possible.
Instead of waiting for every property/data path to be fully ready, Lite is optimized for an "asap" first paint and then progressive updates as state arrives.

- Small mental model: reactive properties + a few focused decorators.
- Fast list rendering options: simple `map`, keyed `repeat`, and lazy viewport rendering via `@repeat`.
- Built-in component communication: `provides` / `consumes` channels for shared state.
- Works with native web components: easy to embed in existing apps without full framework lock-in.
- Lifecycle hooks where they matter: `willChange`, `onChange`, `firstRender`.

Use Lite when you want to ship custom elements quickly, keep dependencies light, and still have ergonomic patterns for real-world UI state and rendering.

## usage

```js
import { LiteElement, property, repeat, html, customElement } from '@vandeurenglenn/lite'

@customElement('some-element')
class SomeElement extends LiteElement {
  @property({ type: Array })
  accessor items = ['hello', 'world']

  render() {
    return html`
      <ul>
        ${repeat(this.items, (item) => html`<li>${item}</li>`)}
      </ul>
    `
  }
}
```

### one big example (all core features)

```js
import {
  LiteElement,
  html,
  css,
  customElement,
  darkMode,
  property,
  repeat,
  map,
  query,
  queryAll,
  assignedElements
} from '@vandeurenglenn/lite'

// Register a custom element and attach dark mode state to this instance.
@customElement('kitchen-sink-el')
@darkMode(true)
class KitchenSinkEl extends LiteElement {
  // Standard reactive property that also reflects as an attribute.
  @property({ type: Boolean, reflect: true })
  accessor open = false

  // Provide data to other components through pubsub.
  @property({ type: Array, provides: 'items-channel' })
  accessor items = ['alpha', 'beta', 'gamma']

  // Consume shared state provided by another component.
  @property({ type: Boolean, consumes: 'drawer-state' })
  accessor drawerOpen = false

  // Big list source used by lazy viewport rendering.
  @property({ type: Array })
  accessor largeItems = Array.from({ length: 200 }, (_, i) => ({ id: i, label: `row-${i}` }))

  // Decorator mode: lazy render rows when they enter the viewport.
  @repeat('largeItems', (item) => html`<li>${item.label}</li>`, (item) => item.id)
  accessor visibleRows

  // Query a single node from shadow root after render.
  @query('#title') accessor titleEl

  // Query all matching nodes from shadow root after render.
  @queryAll('.chip') accessor chips

  // Read assigned slot elements.
  @assignedElements('actions') accessor actionButtons

  static styles = css`
    :host {
      display: block;
      padding: 12px;
    }
  `

  // Optional hook: mutate incoming values before they are stored.
  willChange(propertyKey, value) {
    if (propertyKey === 'items' && Array.isArray(value)) return value.filter(Boolean)
    return value
  }

  // Optional hook: react after values changed and render was requested.
  onChange(propertyKey, value) {
    if (propertyKey === 'open') console.log('open changed:', value)
  }

  // Optional hook: run once after first paint.
  firstRender() {
    console.log('first render complete', this.titleEl)
  }

  render() {
    return html`
      <h2 id="title">Kitchen Sink</h2>

      <button @click=${() => (this.open = !this.open)}>toggle open</button>

      <h3>repeat directive</h3>
      <ul>
        ${repeat(this.items, (item, i) => html`<li class="chip">${i}: ${item}</li>`)}
      </ul>

      <h3>map directive</h3>
      <div>
        ${map(this.items, (item) => html`<span style="margin-right:8px">${item}</span>`) }
      </div>

      <h3>repeat decorator (lazy viewport rows)</h3>
      <ul>${this.visibleRows}</ul>

      <slot name="actions"></slot>
    `
  }
}
```

### repeat directive

Use repeat in templates for lists.

```js
repeat(items, (item) => html`<li>${item}</li>`)
repeat(items, (item) => item.id, (item) => html`<li>${item.label}</li>`)
repeat(items, (item) => html`<li>${item}</li>`, { lazy: true })
repeat(items, (item) => item.id, (item) => html`<li>${item.label}</li>`, { lazy: true })
```

Use the keyed form when list items can be re-ordered, inserted, or removed and you want DOM identity to survive those changes.
Use `{ lazy: true }` when you want rows rendered only after they intersect the viewport.

```js
render() {
  return html`
    <ul>
      ${repeat(
        this.todos,
        (todo) => todo.id,
        (todo) => html`<todo-item .todo=${todo}></todo-item>`
      )}
    </ul>
  `
}
```

### repeatBy (keyed repeat)

There is no separate `repeatBy` export. `repeatBy` is the keyed form of `repeat`:

```js
repeat(items, keyFn, template)
```

Use the keyed form when list items can be re-ordered, inserted, or removed and you want stable identity.

```js
repeat(
  todos,
  (todo) => todo.id,
  (todo) => html`<todo-item .todo=${todo}></todo-item>`
)
```

If you only need plain value mapping, use `map(...)` instead. Using `repeat(items, keyFn, template)` with non-template return values still works for compatibility, but it is deprecated.

### map directive

Use `map` for simple array mapping in templates.

```js
import { map, html } from '@vandeurenglenn/lite'

map(items, (item, i) => html`<li data-index=${i}>${item}</li>`)
```

### store

Use `createStore` for simple shared app state outside component instances.

```js
import { createStore } from '@vandeurenglenn/lite'

const store = createStore({ count: 0, filter: 'all' })

const unsubscribe = store.subscribe((state) => {
  console.log('store changed', state)
})

store.set({ count: 1 })
store.set({ filter: 'done' })

console.log(store.get()) // { count: 1, filter: 'done' }

unsubscribe()
```

Notes:

- `set(updates)` shallow-merges into the current state.
- `get()` returns the latest state snapshot.
- `subscribe(callback)` returns an unsubscribe function.

### repeat decorator (lazy viewport rendering)

Use repeat as a decorator to lazily render list rows as placeholders enter the viewport.

```js
import { LiteElement, property, repeat, html, customElement } from '@vandeurenglenn/lite'

@customElement('big-list')
class BigList extends LiteElement {
  @property({ type: Array })
  accessor items = Array.from({ length: 1000 }, (_, i) => ({ id: i, label: `item-${i}` }))

  @repeat('items', (item) => html`<li>${item.label}</li>`, (item) => item.id)
  accessor visibleRows

  render() {
    return html`<ul>${this.visibleRows}</ul>`
  }
}
```

### provides/consumes

Basic data binding using pubsub.

#### consumes

```js
import { LiteElement, property, html, customElement } from '@vandeurenglenn/lite'

@customElement('consumer-el')
class ConsumerEl extends LiteElement {
  @property({ consumes: true, type: Array })
  accessor items

  @property({ consumes: 'someunique-id', type: Boolean })
  accessor drawerOpen

  render() {
    return html`${this.items.map((item) => html`${item}`)}`
  }
}
```

#### provides

```js
import { LiteElement, property, customElement } from '@vandeurenglenn/lite'

@customElement('provider-el')
class ProviderEl extends LiteElement {
  @property({ provides: true })
  accessor items = ['hello', 'world']

  @property({ provides: 'someunique-id', type: Boolean })
  accessor drawerOpen = false
}
```

#### query

```js
import { LiteElement, customElement, query, html } from '@vandeurenglenn/lite'

@customElement('query-el')
class QueryEl extends LiteElement {
  @query('my-item') accessor item

  render() {
    return html`<my-item></my-item>`
  }
}
```

#### queryAll

```js
import { LiteElement, customElement, queryAll, html } from '@vandeurenglenn/lite'

@customElement('query-all-el')
class QueryAllEl extends LiteElement {
  @queryAll('my-item') accessor items

  render() {
    return html`<my-item></my-item><my-item></my-item>`
  }
}
```

#### darkMode

```js
import { LiteElement, customElement, darkMode } from '@vandeurenglenn/lite'

@customElement('app-shell')
class AppShell extends LiteElement {
  @darkMode(true)
}
```

#### onChange

```js
import { LiteElement, property, customElement } from '@vandeurenglenn/lite'

@customElement('on-change-el')
class OnChangeEl extends LiteElement {
  @property({ type: Boolean })
  accessor drawerOpen = false

  onChange(propertyKey, value) {}
}
```

#### willChange

```js
import { LiteElement, property, customElement } from '@vandeurenglenn/lite'

@customElement('will-change-el')
class WillChangeEl extends LiteElement {
  @property({ type: Boolean })
  accessor drawerOpen = false

  willChange(propertyKey, value) {
    return value
  }
}
```

#### firstRender

```js
import { LiteElement, property, customElement } from '@vandeurenglenn/lite'

@customElement('first-render-el')
class FirstRenderEl extends LiteElement {
  @property({ type: Boolean })
  accessor drawerOpen = false

  firstRender() {}
}
```

---
_Source: https://npm.io/package/@vandeurenglenn/lite · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
