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

@miurajs/miura-render

v0.2.7

Published

The rendering engine for the miura framework. Provides tagged template literals (`html`/`css`), a state-machine parser, a binding manager, structural directives, and performance utilities including LIS-based keyed diffing, async rendering, and virtual scr

Readme

@miurajs/miura-render

The rendering engine for the miura framework. Provides tagged template literals (html/css), a state-machine parser, a binding manager, structural directives, and performance utilities including LIS-based keyed diffing, async rendering, and virtual scrolling.

Features

  • Tagged Templateshtml and css tagged template literals
  • State-Machine Parser — Correctly handles text, attribute, and multi-expression contexts
  • Binding Manager — 10 binding types: Node, Property, Event, Boolean, Class, Style, Attribute, Reference, Directive, Bind
  • Structural Directives#if, #for, #switch with lazy loading support
  • Functional Directiveswhen(), choose(), repeat(), resolveAsync(), computeVirtualSlice()
  • LIS-Based Keyed Diff — O(n log n) algorithm for minimal DOM moves during list reconciliation
  • Template Instance Reuse — Same template structure = update values in place, skip DOM teardown
  • Directive System — Extensible with @directive / @lazyDirective decorators

Installation

pnpm add @miurajs/miura-render

Template Syntax

Text Interpolation

html`<h1>Hello ${this.name}</h1>`

Binding Prefixes

| Prefix | Type | Description | |--------|------|-------------| | (none) | Node | Text content or nested templates | | @ | Event | DOM event listener with modifier support | | . | Property | Set a DOM property directly | | ? | Boolean | Toggle an HTML attribute on/off | | & | Bind | Two-way binding (property + event listener) | | # | Directive / Ref | Structural directives or element references | | class | Class | Object map to class list | | style | Style | Object map to inline styles |

Event Binding

html`<button @click=${this.handleClick}>Click</button>`

// With modifiers
html`<form @submit|prevent=${this.handleSubmit}>...</form>`
html`<button @click|prevent,stop=${this.handler}>Go</button>`

Property Binding

html`<input .value=${this.text}>`
html`<my-component .data=${this.config}>`

Boolean Binding

html`<button ?disabled=${this.loading}>Submit</button>`
html`<details ?open=${this.expanded}>...</details>`

Two-Way Binding (&)

// Tuple form: [currentValue, setter]
html`<input &value=${[this.name, (v) => this.name = v]}>`

// Binder object form: { value, set }
html`<input &value=${{ value: this.name, set: (v) => this.name = v }}>`

Auto-detected events: value -> input, checked/selected/files -> change.

Class Binding

html`<div class=${{ active: this.isActive, disabled: this.off }}>...</div>`

Style Binding

html`<div style=${{ color: 'red', fontSize: '16px' }}>...</div>`

Multi-Expression Attributes

html`<div title="Hello ${this.first} ${this.last}">...</div>`

Multiple expressions in the same attribute are automatically grouped and concatenated.

Functional Directives

when(condition, trueCase, falseCase?)

Conditional rendering. Only the active branch is evaluated.

${when(this.loggedIn,
  () => html`<user-panel></user-panel>`,
  () => html`<login-form></login-form>`
)}

choose(value, cases, defaultCase?)

Multi-branch conditional (like a switch expression).

${choose(this.view, [
  ['list',   () => html`<list-view></list-view>`],
  ['grid',   () => html`<grid-view></grid-view>`],
  ['detail', () => html`<detail-view></detail-view>`],
], () => html`<not-found></not-found>`)}

repeat(items, keyFn, templateFn)

Keyed list rendering with LIS-based diffing.

${repeat(this.items,
  (item) => item.id,
  (item, index) => html`<item-row .data=${item}></item-row>`
)}

The algorithm identifies items already in correct relative order (via Longest Increasing Subsequence), then only moves out-of-order items. Minimizes DOM operations from O(n) to O(n - LIS length).

resolveAsync(tracker, resolved, pending?, rejected?)

Declarative promise-based rendering.

import { createAsyncTracker, resolveAsync } from '@miurajs/miura-render';

// Create a tracker
const tracker = createAsyncTracker(
  fetch('/api/user').then(r => r.json()),
  () => this.requestUpdate()
);

// Render based on state
${resolveAsync(tracker,
  (data) => html`<p>${data.name}</p>`,
  ()     => html`<p>Loading...</p>`,
  (err)  => html`<p>Error: ${err.message}</p>`
)}

#async Directive

Directive that tracks a Promise and renders <template pending>, <template resolved>, or <template rejected> — the same pattern as #switch with <template case> / <template default>:

<div #async=${this.userPromise}>
  <template pending>
    <p>Loading…</p>
  </template>
  <template resolved>
    <p>Data loaded!</p>
  </template>
  <template rejected>
    <p>Something went wrong.</p>
  </template>
</div>

The directive:

  • Scans child <template> elements for pending, resolved, rejected attributes
  • Shows the pending template immediately when a new promise is assigned
  • Swaps to resolved or rejected when the promise settles
  • Ignores stale promises if a new one is assigned before settlement

#virtualScroll Directive

Structural directive that virtualizes a large list. Manages the scroll container, spacer, and visible slice internally — no manual scroll listeners needed:

html`<div #virtualScroll=${{
  items: this.allItems,    // full array
  itemHeight: 40,          // px per row
  containerHeight: 400,    // viewport px
  render: (item, i) => html`<div>${item.name}</div>`,
  overscan: 3,             // buffer rows
}}></div>`

The directive:

  • Creates a scroll container with the specified height
  • Adds a spacer div for the correct total scrollable height
  • Renders only the visible items plus overscan buffer
  • Updates on scroll via requestAnimationFrame (no reactive cycle needed)

computeVirtualSlice(config, scrollTop)

Lower-level pure function for custom virtual scroll implementations:

import { computeVirtualSlice } from '@miurajs/miura-render';

const vs = computeVirtualSlice({
  items: this.allItems,
  itemHeight: 40,
  containerHeight: 400,
  render: (item, i) => html`<div>${item.name}</div>`,
  overscan: 3,
}, this.scrollTop);

// Use vs.visibleItems, vs.totalHeight, vs.startIndex, etc.

Structural Directives

Built-in directives that control DOM structure:

| Directive | Description | |-----------|-------------| | #if | Conditional rendering | | #for | List iteration (callback mode or template mode with {{$item}}/{{$index}}) | | #switch | Multi-case rendering | | #async | Promise-driven pending/resolved/rejected rendering | | #virtualScroll | Virtual scrolling for large lists |

Custom directives can be registered via @directive or @lazyDirective decorators. Lazy directives are only loaded when first used.

Architecture

Parser

A state-machine (TemplateParser) that walks template strings character by character, tracking context (text, tag, attribute name, attribute value, comment) to correctly identify binding positions. Outputs an HTML string with markers and a TemplateBinding[] array.

Binding Manager

BindingManager creates binding instances from the parser output and initializes them with values. Each binding type implements the Binding interface:

interface Binding {
  setValue(value: unknown, context?: unknown): void | Promise<void>;
  clear(): void;
  disconnect?(): void;
}

Template Instance Reuse

When a NodeBinding receives a new TemplateResult with the same strings reference as the previous render, it calls instance.update(newValues) instead of tearing down and rebuilding the DOM.

Keyed Diff (LIS Algorithm)

KeyedListState manages keyed list reconciliation:

  1. Compute new keys, remove items with deleted keys
  2. Reuse existing TemplateInstance objects for surviving keys
  3. Build a position map (old index per key) and compute the Longest Increasing Subsequence
  4. Items in the LIS stay in place; all others are moved via insertBefore

This is the same algorithm used by Vue and Svelte for list reconciliation.

AOT Compiler

In addition to the default JIT rendering path, miura-render ships a TemplateCompiler that generates optimised render()/update() JS functions via new Function(). Component classes opt in with static compiler = 'AOT' as const on MiuraElement.

How it works

Template string → TemplateParser → ParsedTemplate (HTML + TemplateBinding[])
                                          ↓
                               CodeFactory.generateRenderFunction()
                               CodeFactory.generateUpdateFunction()
                                          ↓
                               CompiledTemplate { render, update, nodeBindingIndices, directiveBindingInfos }

First rendercompiled.render(values) clones the template, walks it once with TreeWalker to build a refs[] array (element/comment node refs indexed by binding marker), applies initial values, returns { fragment, refs }.

Subsequent updatescompiled.update(refs, values) patches refs[N].el.value, refs[N].el.setAttribute(…) etc. directly on cached refs — zero DOM queries.

Three-tier binding strategy

| Binding kind | Compiled code | External manager | |---|---|---| | Property / Boolean / Event / Class / Style / ObjectClass / ObjectStyle / Spread / Bind / Async / Reference | ✅ Inlined in generated JS | — | | Node (text, TemplateResult, repeat()) | — | NodeBinding instance per ref | | Directive (#if, #for, #switch, custom) | — | DirectiveBinding instance per ref |

CompiledTemplate exposes nodeBindingIndices and directiveBindingInfos so the caller can wire up the correct instances after the initial DOM render.

Direct usage

import { TemplateCompiler } from '@miurajs/miura-render';

const compiler = new TemplateCompiler();

// First call — parses + compiles (cached by strings reference)
const compiled = compiler.compile(result);

// First render
const { fragment, refs } = compiled.render(result.values);
shadowRoot.appendChild(fragment);

// Subsequent updates — zero DOM queries
compiled.update(refs, newResult.values);

CSS Tagged Template

import { css } from '@miurajs/miura-render';

const styles = css`
  :host { display: block; }
  .title { font-weight: bold; }
`;

Returns a CSSResult that can be applied to a shadow root via adoptedStyleSheets or a <style> element.

License

MIT