Lit Integration

Lit bindings for @quantajs/core: reactive controllers that connect QuantaJS stores to web components. Works with Lit 2 and 3.

Installation

npm install @quantajs/lit @quantajs/core
# or
pnpm add @quantajs/lit @quantajs/core
# or
yarn add @quantajs/lit @quantajs/core

Usage

// stores/todos.ts
import { defineStore } from '@quantajs/core';

export const useTodoStore = defineStore('todos', {
    state: () => ({ items: [] as { text: string; done: boolean }[] }),
    getters: {
        remaining: (s) => s.items.filter((t) => !t.done).length,
    },
    actions: {
        add(text: string) {
            this.items.push({ text, done: false });
        },
    },
});
// todo-app.ts
import { LitElement, html } from 'lit';
import {
    QuantaActionsController,
    QuantaController,
    QuantaValueController,
} from '@quantajs/lit';
import { useTodoStore } from './stores/todos';

export class TodoApp extends LitElement {
    // Updates this element only when `remaining` changes.
    private remaining = new QuantaValueController(
        this,
        useTodoStore,
        (s) => s.remaining,
    );

    // Updates this element on any change to the store.
    private todos = new QuantaController(this, useTodoStore);

    // The store, without subscribing: for calling actions.
    private actions = new QuantaActionsController(this, useTodoStore);

    render() {
        return html`
            <p>${this.remaining.value} left</p>
            <button @click=${() => this.actions.store.add('New todo')}>
                Add
            </button>
            <ul>
                ${this.todos.store.items.map((t) => html`<li>${t.text}</li>`)}
            </ul>
        `;
    }
}
customElements.define('todo-app', TodoApp);

No setup is needed in a client-only app: stores resolve against the default container.

Controllers

ControllerUpdates the element onUse for
QuantaValueController(host, definition, selector, opts?)What the selector readsMost elements; read .value
QuantaController(host, definition, opts?)Any change to the storeElements that read most of a store; read .store
QuantaActionsController(host, definition, opts?)NothingElements that only call actions; read .store
QuantaLocalController(host, definition)Any change to the storeA store owned by one element, disposed when it is removed

Create them as class fields, or in the constructor. Subscriptions start when the element connects and end when it disconnects. A local store survives the element being moved in the DOM.

A selector that builds a new object on every run should pass shallow, so an unchanged projection does not update:

import { LitElement } from 'lit';
import { shallow, QuantaValueController } from '@quantajs/lit';
import { useTodoStore } from './stores/todos';

export class TodoSummary extends LitElement {
    private summary = new QuantaValueController(
        this,
        useTodoStore,
        (s) => ({ remaining: s.remaining, total: s.items.length }),
        { equalityFn: shallow },
    );
}

Containers

provideQuantaContainer(this) in an element's constructor gives that element's subtree its own container, including elements in its shadow root. It follows the web components context protocol, so @lit/context works too: quantaContainerContext is the context key.

import { LitElement } from 'lit';
import { provideQuantaContainer } from '@quantajs/lit';

export class AppRoot extends LitElement {
    constructor() {
        super();
        // Created here and disposed when the element is removed.
        provideQuantaContainer(this);
    }
}

Pass { container } to any controller to use a specific container instead. Before an element connects, and during server rendering, controllers resolve against that container or the default one.

DevTools

npm install -D @quantajs/devtools
import { enableDevTools } from '@quantajs/lit';

if (import.meta.env.DEV) {
    enableDevTools({ redact: ['token'] });
    import('@quantajs/devtools').then(({ mountDevTools }) => mountDevTools());
}

See DevTools for what the panel shows.

Learn More

Spot something that needs improving?

Edit page