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

@dooboostore/dom-render

v1.0.113

Published

html view template engine

Downloads

137

Readme

@dooboostore/dom-render

NPM version Build and Test License: MIT

Full Documentation: https://dooboostore-develop.github.io/@dooboostore/dom-render

A reactive and component-oriented DOM template engine for fine-grained rendering. @dooboostore/dom-render tracks state changes through proxy-based observation and updates only affected render units.


Features

  • Reactive object proxy rendering (DomRender, DomRenderProxy)
  • HTML template bindings (${...}$, #...#, @this@)
  • Structural directives (dr-if, dr-for, dr-for-of, dr-repeat, dr-strip, dr-appender)
  • Event directives (dr-event-*, dr-window-event-*, dr-on-init, dr-on-rendered-init)
  • Form directives (dr-form and validator integration)
  • Component system (ComponentBase, createComponent, target elements/attrs)
  • Lifecycle hooks (onCreateRender, onInitRender, onRawSetRendered, etc.)
  • Built-in messenger for inter-component pub/sub
  • Router integration (Path/Hash) via config

Installation

pnpm add @dooboostore/dom-render
# or
npm install @dooboostore/dom-render

Quick Start

import { DomRender } from '@dooboostore/dom-render';

const app = document.querySelector('#app')!;

let state = {
  title: 'Hello DomRender',
  count: 0,
  increment() {
    this.count += 1;
  }
};

app.innerHTML = `
  <h1>${@[email protected]}$</h1>
  <p>Count: ${@[email protected]}$</p>
  <button dr-event-click="@[email protected]()">+1</button>
`;

const result = new DomRender({
  rootObject: state,
  target: app,
  config: { window }
});

state = result.rootObject;

Public API (Root Export)

@dooboostore/dom-render now exposes modules from root entry:

  • components
  • configs
  • decorators
  • events
  • lifecycle
  • messenger
  • operators
  • rawsets
  • types
  • DomRender
  • DomRenderProxy

This enables single-entry usage without relying on package subpath exports.


Core Concepts

1) Reactive Root Object

DomRender wraps your root object with a proxy. Mutating fields on result.rootObject triggers reactive rendering.

const result = new DomRender({
  rootObject: { name: 'kim', age: 20 },
  target: element,
  config: { window }
});

const root = result.rootObject;
root.age = 21; // dependent template fragments update

2) Template Expressions

  • ${expr}$: evaluate JS expression in render context
  • #it#, #nearForOfIndex#: loop context placeholders
  • @this@: current render/component instance pointer
<div>${@[email protected]}$</div>
<li dr-for-of="@[email protected]">${#it#.title}$</li>

3) Incremental Render Units (RawSet)

DOM fragments are segmented into units with dependency tracking. When path changes, only connected units are re-executed.


Structural Directives

dr-if

Conditional rendering based on expression truthiness.

<div dr-if="@[email protected]">
  Welcome, ${@[email protected]}$
</div>

dr-for

Classic loop-style directive.

<li dr-for="let i=0; i<@[email protected]; i++" dr-option-it="@[email protected][i]">
  ${destIt.name}$
</li>

dr-for-of

Array/object iteration with #it# context replacement.

<ul>
  <li dr-for-of="@[email protected]">
    ${#it#.name}$
  </li>
</ul>

dr-repeat

Repeat block by count/range expression.

<div dr-repeat="@[email protected]">Cell ${#it#}$</div>

dr-appender

Optimized append/update/delete for list-like incremental data.

<ul>
  <li dr-appender="@[email protected]">${#it#}$</li>
</ul>

dr-strip / dr-option-strip

Strip wrapper element while preserving children.

<div dr-if="@[email protected]" dr-option-strip="true">#innerHTML#</div>

Template Utilities

Dynamic content directives

  • dr-inner-html: bind innerHTML
  • dr-inner-text: bind innerText
  • dr-attr: bind attribute object
  • dr-this, dr-this-property: bind target context object
  • dr-before, dr-after: pre/post scripts around operator execution
<div dr-inner-text="@[email protected]"></div>
<div dr-inner-html="@[email protected]"></div>
<img dr-attr="{ src: @[email protected], alt: @[email protected] }" />

Form utility (dr-form)

dr-form wires fields and optional validator metadata.

<form dr-form="@[email protected]">
  <input name="email" dr-form:name="'email'" dr-form:event="change" />
</form>

Appender utility class

Appender supports keyed incremental collection updates.

import { Appender } from '@dooboostore/dom-render';

const state = {
  rows: new Appender('A', 'B')
};

state.rows.set('key-1', 'Updated A');
state.rows.delete('key-1');
state.rows.clear();

Event System

dr-event-* directive

Bind DOM events declaratively.

<button dr-event-click="@[email protected]($event)">Submit</button>
<input dr-event-input="@[email protected]($event)" />

dr-window-event-* directive

Listen to window-scoped events.

<div dr-window-event-resize="@[email protected]($event)"></div>

EventManager behavior

  • delegated listener strategy for common events
  • direct attachment for non-delegatable events
  • execution context variables: $event, $target, and render-bound objects

Component Architecture

ComponentBase

ComponentBase provides:

  • attribute binding (@attribute)
  • query binding (@query)
  • event binding (@event)
  • child component tracking
  • lifecycle integration
  • decorator refresh APIs (refreshDecorators, refreshQueryDecorators, refreshEventDecorators)

@attribute

import { ComponentBase, attribute } from '@dooboostore/dom-render';

class Card extends ComponentBase {
  @attribute('title')
  title = '';

  @attribute({ name: 'count', converter: (v) => Number(v ?? 0) })
  count = 0;
}

@query

import { ComponentBase, query } from '@dooboostore/dom-render';

class Panel extends ComponentBase {
  @query('.title')
  titleEl?: HTMLElement;

  @query({ selector: '.row', refreshRawSetRendered: true })
  rows: HTMLElement[] = [];
}

@event

import { ComponentBase, event } from '@dooboostore/dom-render';

class Toolbar extends ComponentBase {
  @event({ query: '.save-btn', name: 'click' })
  onSave() {
    console.log('saved');
  }
}

createComponent

import { DomRender } from '@dooboostore/dom-render';

class UserCard {
  name = 'Anonymous';
}

const userCard = DomRender.createComponent({
  type: UserCard,
  tagName: 'user-card',
  template: `<div>${@[email protected]}$</div>`
});

const app = new DomRender({
  rootObject: { users: [] },
  target: document.querySelector('#app')!,
  config: { window, targetElements: [userCard] }
});

Lifecycle Hooks

Implement interfaces to receive lifecycle callbacks:

  • onProxyDomRender(config)
  • onCreateRender(...args)
  • onCreateRenderData(data)
  • onInitRender(param, rawSet)
  • onRawSetRendered(rawSet, otherData)
  • onChildRawSetRendered()
  • onDestroyRender(params)
  • onChangeAttrRender(name, value, other)
class ViewModel {
  onCreateRender() {
    console.log('render create');
  }

  async onInitRender() {
    console.log('render initialized');
  }
}

Router Integration

DomRender can create router by routerType:

  • 'hash' -> HashRouter
  • 'path' -> PathRouter
  • custom router object/factory
const app = new DomRender(
  {
    rootObject: { page: 'home' },
    target: document.querySelector('#app')!,
    config: { window, routerType: 'hash' }
  },
  { firstUrl: 'home' }
);

app.router.go('/about');

Messenger

DefaultMessenger and Messenger provide channel-based communication across render roots/components.

Use this when direct parent-child access is not suitable.


Requested Decorator Mapping

This section is added for teams using both dom-render and simple-web-component.

@addEventListener (extended)

@addEventListener is a decorator from @dooboostore/simple-web-component, not from dom-render directly.

In dom-render, equivalent patterns are:

  1. Template directive: dr-event-*
  2. Class decorator in component: @event({ query, name })
<button dr-event-click="@[email protected]($event)">Click</button>
class MyComp extends ComponentBase {
  @event({ query: '.btn', name: 'click' })
  onClick(e: Event) {}
}

@replaceChildren (extended)

@replaceChildren is from simple-web-component.

dom-render alternatives:

  1. structural replacement via directives (dr-if, dr-for-of, dr-appender)
  2. explicit DOM strategy in component methods (replaceChildren) when needed
updateRows(container: HTMLElement, nodes: Node[]) {
  container.replaceChildren(...nodes);
}

For reactive templates, prefer declarative updates over manual replacement.

@appendChild (extended)

@appendChild is from simple-web-component.

dom-render alternatives:

  1. dr-appender + Appender for incremental append/delete
  2. direct appendChild in imperative setup code
state.rows.set('k1', 'first row'); // reactive append through dr-appender

Best Practices

  1. Always mutate result.rootObject (proxied object), not the original plain object.
  2. Prefer declarative directives over direct DOM mutation.
  3. Use dr-appender for large frequently changing collections.
  4. For dynamic component trees, call refreshDecorators() when bindings need explicit refresh.
  5. Use DomRenderNoProxy for fields that should never trigger proxy traversal.
  6. Keep heavy computations outside template expressions.

Troubleshooting

UI not updating after mutation

  • Check that you are mutating result.rootObject, not stale reference.
  • Verify expression paths are valid (@[email protected]).

Event not firing

  • Confirm directive name (dr-event-click, dr-event-input, etc.).
  • Check script context variables ($event, $target).

Render loop/performance drop

  • Avoid expensive script evaluation in template.
  • Prefer keyed Appender updates instead of full array replacement.

License

This package is licensed under the MIT License.