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

@flowview/dom

v0.3.0

Published

Tiny DOM runtime for flowview-generated render functions.

Readme

@flowview/dom

@flowview/dom writes the HTML returned by a compiled FlowView render function into one native DOM element. It does not compile templates or depend on a reactive package.

import { createView } from "@flowview/dom";
import { render } from "./items.flow";

const view = createView("#items", render);
view.render({ items: [] });
view.update({ items: [{ id: 1, name: "Ship it" }] });

Both render() and update() replace innerHTML. Nodes do not survive an update, so Elements returned by refs are snapshots of the current render. Read the refs again after replacement.

Scoped references

Mark ordinary template elements with data-flow-ref:

<p data-flow-ref="summary">{{ context.items.length }} items</p>

@for (item of context.items) {
  <li data-flow-ref="item">{{ item.name }}</li>
}

view.ref<TElement>(name) returns the one matching current Element, or null if absent. It throws when multiple elements have that name; use view.refs<TElement>(name) to get every match in DOM order. Ref names are compared as attribute values, not interpolated into selectors. Conditional refs are absent when their branch is not rendered. Refs never cross into an active nested view. An outer view can still reference the nested view root itself when that root has a ref attribute.

Binding and ownership

view.bind(source) accepts any source with a subscribe(callback) method that returns an unsubscribe function. The source emits its current value on subscription, so binding performs the initial render synchronously. FlowView Signal and Computed values satisfy this contract without a runtime package dependency from DOM to Reactive.

The returned stop() is idempotent. It disconnects the source while leaving manual render() and update() available. A view accepts one active binding at a time.

view.dispose() is idempotent, stops its binding, disposes active nested views, and releases root ownership. It leaves rendered DOM in place. Disposed views reject further operations. Only one active view can own a root element; dispose the current owner before reusing it. Parent replacement also disposes active descendant views before replacing their subtree.

FlowView Events keeps using its delegated event listeners across replacement; DOM views do not create or own another Events lifecycle. FlowView remains a string renderer with no virtual DOM, reconciler, hydration or implicit component lifecycle.

Native form controls

Bindings such as [checked], [selected], [open], [disabled] and [required] serialize ordinary HTML boolean attributes. They seed the browser's initial and default control state; they do not keep live DOM properties bound after user interaction.

<input data-flow-ref="name" value="{{ context.name }}" />
<textarea data-flow-ref="notes">{{ context.notes }}</textarea>
<input type="checkbox" [checked]="context.enabled" />
<option value="south" [selected]="context.region === 'south'">South</option>
<details [open]="context.showDetails">...</details>

For textareas, render the initial value as element text, not a value attribute. The browser may change input.value, checked, select.value and option.selected while the corresponding attributes/default properties remain unchanged. form.reset() restores native controls to their HTML defaults.

Read a live value through a scoped native reference when needed:

const name = view.ref<HTMLInputElement>("name");
const draft = name?.value;

Keep active typing browser-local and commit at an explicit event boundary, such as form submit. view.bind(source) synchronously replaces the entire view on every source emission; connecting each keystroke to a bound source can detach the focused input immediately. Every render() or update() also replaces controls, losing their local dirty values and node identity. Focus, selection, caret and IME composition are not automatically restored. Reacquire refs after replacement. There is no two-way binding, form state model, validation layer, general property setter, or reconciliation.

FlowView Events does not prevent form submission. A handler chooses whether to call event.preventDefault() and can read current controls with native APIs such as new FormData(form). See the browser-tested acceptance example in examples/astro-demo/e2e/forms.spec.ts.