npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@uistate/view

v1.0.0

Published

State-driven view: DOM structure as first-class state. DOMless resolve + surgical project.

Readme

@uistate/view

State-driven view: DOM structure as first-class state.

The UI is not a function of state. The UI is state.

The Idea

Traditional frameworks treat the virtual DOM as an ephemeral, opaque computation:

render(state) -> vDOM -> reconcile -> real DOM

You can't inspect it, subscribe to it, or test it.

@uistate/view inverts this. The view specification lives in the EventState store as a normalized tree of nodes, each independently subscribable via dot-paths. Data and view coexist in the same store, tested with the same assertPath.

state -> view tree (persistent, observable, first-class state) -> projector -> real DOM

Architecture

Two phases, inspired by the normalized state pattern from Ch15 (Pro State Management in JS):

Phase 1: resolve.js; Pure, DOMless (runs in Node)

  • normalize(nestedTree); Flattens a nested view specification into a map of nodes by stable ID. Each node has tag, childIds, parentId, and all attributes/bindings. This is the Ch15 pattern applied to the view layer.
  • flatten(tree, store, prefix); Normalizes and writes into an EventState store via setMany (atomic).
  • resolveTree(nodes, rootId, getData); Evaluates expressions, expands forEach, resolves text interpolation. Pure function.
  • serialize(resolvedTree); Converts a resolved tree to an HTML string (SSR/snapshots).

Phase 2: project.js; DOM Projector (browser only)

  • mount(store, prefix, container, handlers): Reads normalized nodes from the store, creates DOM elements, subscribes to each node's path for surgical updates. Follows the Ch15 createNodeElement(nodeId) pattern.

Install

npm install @uistate/view

Peer dependency: @uistate/core >= 5.0.0

Usage

Define state + view in one store

import { createEventState } from '@uistate/core';
import { flatten } from '@uistate/view/resolve';

const store = createEventState({
  todos: [
    { id: 1, text: 'Learn SDD', done: true },
    { id: 2, text: 'Build @uistate/view', done: false }
  ],
  inputText: ''
});

flatten({
  tag: 'div',
  class: 'todo-app',
  children: [
    { tag: 'h2', text: 'Todo ({todos.length})' },
    {
      tag: 'ul',
      forEach: 'todos',
      as: 'todo',
      template: {
        tag: 'li',
        classIf: { done: 'todo.done' },
        children: [
          { tag: 'span', text: 'todo.text' },
          { tag: 'button', text: 'x', onClick: 'deleteTodo(todo.id)' }
        ]
      }
    },
    {
      tag: 'div',
      class: 'stats',
      children: [
        { tag: 'span', text: 'Total: {todos.length}' },
        { tag: 'span', text: 'Done: {todos.filter(t => t.done).length}' }
      ]
    }
  ]
}, store, 'view');

After flatten, the store contains:

view.rootId        -> 'v0'
view.nodes.v0      -> { id: 'v0', tag: 'div', class: 'todo-app', childIds: ['v1','v2','v3'], ... }
view.nodes.v1      -> { id: 'v1', tag: 'h2', text: 'Todo ({todos.length})', ... }
view.nodes.v2      -> { id: 'v2', tag: 'ul', forEach: 'todos', template: {...}, ... }
...

Each node is independently subscribable:

store.subscribe('view.nodes.v1', (node) => { /* only h2 changed */ });
store.subscribe('view.nodes.*', (detail) => { /* any node changed */ });

DOMless testing (Node, no browser)

import { resolveTree } from '@uistate/view/resolve';

const nodes = store.get('view.nodes');
const rootId = store.get('view.rootId');
const resolved = resolveTree(nodes, rootId, (path) => store.get(path));

// Assert on the resolved tree — no DOM needed
assert(resolved.children[0].text === 'Todo (2)');
assert(resolved.children[1].children.length === 2);
assert(resolved.children[1].children[0].children[0].text === 'Learn SDD');

SSR (serialize to HTML)

import { serialize } from '@uistate/view/resolve';

const html = serialize(resolved);
// <div class="todo-app">
//   <h2>Todo (2)</h2>
//   <ul>
//     <li><span>Learn SDD</span>...</li>
//     ...
//   </ul>
//   ...
// </div>

Browser projection

import { mount } from '@uistate/view/project';

const cleanup = mount(store, 'view', document.getElementById('app'), {
  addTodo: () => { /* ... */ },
  deleteTodo: (id) => { /* ... */ },
  toggleTodo: (id) => { /* ... */ }
});

// cleanup() removes all subscriptions and DOM

View Specification

Node properties

| Property | Type | Description | |----------|------|-------------| | tag | string | HTML tag name | | class | string | CSS class(es) | | classIf | object | Conditional classes: { done: 'todo.done' } | | text | string | Text content, supports {expression} interpolation and data references | | type | string | Input type | | placeholder | string | Input placeholder | | bind | string | Two-way binding to a store path | | onClick | string | Handler name: 'addTodo' or 'deleteTodo(todo.id)' | | onEnter | string | Handler on Enter key | | children | array | Child node specifications | | forEach | string | Store path to iterate | | as | string | Loop variable name (default: 'item') | | template | object | Template spec for each item |

Expressions

Text supports {expression} interpolation:

  • Path lookup: {todos.length} -> array length
  • Filter count: {todos.filter(t => t.done).length} -> filtered count
  • Data reference: todo.text (in forEach context) -> item property

How it works (normalized state)

The nested view tree you author:

{ tag: 'div', children: [{ tag: 'h1', text: 'Hello' }, { tag: 'p', text: 'World' }] }

Gets normalized into a flat map (Ch15 pattern):

{
  'v0': { id: 'v0', tag: 'div', childIds: ['v1', 'v2'], parentId: null },
  'v1': { id: 'v1', tag: 'h1', text: 'Hello', childIds: [], parentId: 'v0' },
  'v2': { id: 'v2', tag: 'p',  text: 'World', childIds: [], parentId: 'v0' }
}

Each node is an independent entry in EventState. Updating v1.text fires only view.nodes.v1 subscribers, not the parent, not siblings. This is O(1) reactivity, exactly like the tree app in Ch15.

Testing

Two-layer testing architecture:

self-test.js: Zero-dependency self-test (73 assertions). Runs automatically on npm install via postinstall. Tests the pure resolve module: normalize, resolveNode, resolveTree, serialize, flatten, getByPath, interpolate. Includes the full SDD todo app as the final integration test.

node self-test.js

tests/view.test.js: Integration tests via @uistate/event-test (14 tests). Tests flatten into EventState, store-driven resolve, surgical updates, node independence, SSR, and the full SDD workflow.

npm test

| Suite | Assertions | Dependencies | |-------|-----------|-------------| | self-test.js | 73 | @uistate/core only | | tests/view.test.js | 14 | @uistate/event-test, @uistate/core |

Comparison

| Concern | React | @uistate/renderer | @uistate/view | |---------|-------|-------------------|---------------| | View representation | Ephemeral vDOM | HTML attributes | First-class state | | Subscribable? | No | Per-binding | Per-node | | DOMless testing? | No (needs jsdom) | Partial (pure helpers) | Complete | | SSR? | Framework-specific | No | serialize() | | Reactivity | Component-level | Per-attribute | Per-node (O(1)) | | Source of truth | Code (JSX) | DOM (HTML) | State (JSON in store) |

License

MIT