@everystate/view
v1.3.0
Published
EveryState View: DOM structure as first-class state. DOMless resolve + surgical project.
Maintainers
Readme
@everystate/view v1.3.0
DOM structure as first-class state. DOMless resolve + surgical project.
Treat your entire UI as state. Normalize view specifications into a flat map, store them in EveryState, and project to the DOM with surgical updates.
Installation
npm install @everystate/view @everystate/coreQuick Start (createApp)
The fastest way to get a reactive app running. One call does everything:
import { createEveryState } from '@everystate/core';
import { createApp } from '@everystate/view/app';
import { increment } from './increment.js';
import { decrement } from './decrement.js';
const store = createEveryState({ count: 0 });
const { cleanup } = createApp(store, '#app', {
tag: 'div', class: 'counter', css: { textAlign: 'center' },
children: [
{ tag: 'h1', text: 'Counter: {count}' },
{ tag: 'button', text: '-', onClick: 'decrement' },
{ tag: 'span', text: '{count}', class: 'value',
css: { fontSize: '3rem', fontWeight: '700' } },
{ tag: 'button', text: '+', onClick: 'increment' },
]
}, { increment, decrement });createApp returns { store, cleanup }. It wraps flatten + mount + intent auto-wiring + CSS extraction + handler auto-injection into a single call.
What's new in v1.3.0
- SVG namespace support -
tag: 'svg'and all SVG child elements are created withcreateElementNS. TheinSvgcontext threads automatically through children. - Generic
attrs- Arbitrary attributes viaattrs: { x: 10, fill: 'red' }. Applied withsetAttributeon both regular nodes and template specs. - Reactive
bindAttrs- Per-attribute store subscriptions viabindAttrs: { fill: 'path.to.color' }. Each attribute gets its own surgical subscription. - SVG-safe
classNamehandling (setAttribute('class', ...)instead ofel.classNamefor SVG elements).
What's new in v1.2.0
specPage(spec, handlers?, components?)- Converts a JSON-serializable view spec into a router-compatible component with aboot()method. The router passes{ store, el, view }to boot;specPagederives the store prefix asview.${view}automatically.createSpecPage(handlers?, components?)- Factory that curries shared handlers and components, returning a function(spec) => { boot }. Supports an options-object signature for auto-merging handler factories and auto-naming component mount functions.mergeHandlers(store, factories)- Calls each handler factory with the store and merges all returned objects into a single handler registry.autoComponents(...mountFns)- Builds a component registry from named mount functions. Derives the key fromfn.name:mountAccordion->"accordion",mountFSM->"fsm".
Routed SPA in 3 statements
import { createSpecPage } from '@everystate/view/app';
const page = createSpecPage({
store,
handlers: [tableHandlers, countersHandlers],
components: [mountTabs, mountGrid, mountFSM],
});
const router = createRouter({
store,
routes: [
{ path: '/', view: 'home', component: page(homeSpec) },
{ path: '/data', view: 'data', component: page(dataSpec) },
],
});
router.start();What's new in v1.1.4
- Dynamic bind paths: Added
{context}interpolation support to bind paths, matching the existing text interpolation. Now you can use dynamic paths likebind: "todos.{index}.done"in templates for proper two-way binding in list contexts.
What's new in v1.1.0
- Co-located CSS - Add
css: { ... }to any view node with aclass.createAppauto-extracts it tocss.{class}.{prop}store paths (works with@everystate/cssstyle engine). - Handler auto-inject - Handler functions receive
storeas their first argument automatically. Writeexport function increment(store) { ... }and pass{ increment }- no manual wiring. - Return shape -
createAppnow returns{ store, cleanup }instead of a bare cleanup function. - Signature B -
createApp(el, initialState, viewSpec, handlers)can create the store for you (callcreateApp.use(createEveryState)first).
Declarative show binding
Toggle element visibility based on a store path. No refs, no manual class toggling:
{ tag: 'div', class: 'panel', show: 'ui.panelOpen', children: [
{ tag: 'p', text: 'This panel is visible when ui.panelOpen is truthy' }
]}The engine subscribes to the path and sets display: none when falsy, restores when truthy.
Advanced usage (flatten + mount)
For full control, use the lower-level API directly:
import { createEveryState } from '@everystate/core';
import { flatten } from '@everystate/view/resolve';
import { mount } from '@everystate/view/project';
const store = createEveryState({});
flatten({
tag: 'div',
children: [
{ tag: 'h1', text: 'Hello' },
{ tag: 'p', text: 'World' }
]
}, store, 'view');
const cleanup = mount(store, 'view', document.getElementById('app'), {});Why View-as-State?
- DOMless testing - Assert on view tree in Node.js, no browser required
- Surgical updates - Only changed nodes re-render
- State-driven - View is just another part of your state tree
- Framework-free - Works with vanilla JS or any framework
Documentation
Full documentation available at everystate.dev.
Ecosystem
| Package | Description | License |
|---|---|---|
| @everystate/aliases | Ergonomic single-character and short-name DOM aliases for vanilla JS | MIT |
| @everystate/angular | Angular adapter: usePath, useIntent, useWildcard, useAsync - bridges store to Angular signals | MIT |
| @everystate/core | Path-based state management with wildcard subscriptions and async support | MIT |
| @everystate/css | Reactive CSSOM engine: design tokens, typed validation, WCAG enforcement, all via path-based state | MIT |
| @everystate/examples | Example applications and patterns | MIT |
| @everystate/perf | Performance monitoring overlay | MIT |
| @everystate/react | React hooks adapter: usePath, useIntent, useAsync hooks and EventStateProvider | MIT |
| @everystate/renderer | Direct-binding reactive renderer: bind-*, set, each attributes. Zero build step | MIT |
| @everystate/router | SPA routing as state | MIT |
| @everystate/solid | Solid adapter: usePath, useIntent, useWildcard, useAsync - bridges store to Solid signals | MIT |
| @everystate/test | Event-sequence testing for EveryState stores. Zero dependency. | MIT |
| @everystate/types | Typed dot-path autocomplete for EveryState stores | MIT |
| @everystate/view | State-driven view: DOMless resolve + surgical DOM projector. View tree as first-class state | MIT |
| @everystate/vue | Vue 3 composables adapter: provideStore, usePath, useIntent, useWildcard, useAsync | MIT |
Self-test (CLI, opt-in)
The self-test verifies the pure, DOMless resolve.js module:
normalize, resolveNode, resolveTree, serialize, getByPath,
interpolate, flatten, and extractDataPaths.
It is zero-dependency - no @everystate/core or DOM required.
It is opt-in and never runs automatically on install:
# via npx (no install needed)
npx everystate-view-self-test
# if installed locally
everystate-view-self-test
# or directly
node node_modules/@everystate/view/self-test.jsYou can also run the npm script from the package folder:
npm --prefix node_modules/@everystate/view run self-testIntegration tests (@everystate/test)
The tests/ folder contains a separate integration suite that uses
@everystate/test and @everystate/core (declared as devDependencies / peerDependencies).
The self-test stays zero-dependency, while integration tests
remain available for deeper store-level validation.
For end users (after installing the package):
# Install test dependency
npm install @everystate/test
# Run from package folder
cd node_modules/@everystate/view
npm run test:integration
# or short alias
npm run test:iOr, from your project root:
npm --prefix node_modules/@everystate/view run test:integrationFor package developers (working in the source repo):
npm install
npm run test:integrationLicense
MIT © Ajdin Imsirovic
