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

denier

v0.3.3

Published

Denier is a tiny library for building web frontend stuff

Readme

Denier - a tiny lib for weaving web apps. :thread:

Denier is a tiny library for building web frontend stuff.

Yes — it's yet another kind-of reactive web-UI library. I guess we should throw in "opiniated" too.

Denier has templates, events, context-provided objects, state, efficient DOM updates, typed styles et.c.

All in about 850 lines of code, with no external run-time dependencies. The minimized and gzipped version (why do we still list this?) is about 3k.

  • Simple life-cycle, no magic.
  • No extra DOMs or diffing.
  • Mixes well with other libraries and frameworks.
  • No special syntax, just plain HTML and Javascript/Typescript.
  • Nice debug helpers

The name "Denier" refers to the unit for tiny thread sizes, and the fact that we deny the existence of all other similar frameworks. Or not. It's just a name. :socks:

Getting started

Basics

A Denier template is created using html - tagged strings:

const message = "Hello, world!";
const template = html`
  <span>${message}</span>
`;

Templates are rendered into host tags:

<body>
  <div id="my-app"></div>
</body>
const host = document.getElementById("my-app");
template.render(host!);

This would result in the following:

<body>
  <span>Hello, world!</span>
</body>

Note that the template replaces the host tag!

Examples

Sub templates / components

By using a template within another template, we have a simple method for creating components:

function title(t) {
  return html`<h2>${t}</h2>`;
}

const template = html`
  <div>${title("Hello!")}</div>
`;

Template lifecycle

Templates are plain Javascript raw strings with interpolated values. Once created, they are static.

Templates can have dynamic parts by using functions or directives as interpolated values:

const now = () => new Date().toLocaleString("sv");
const dynamic = html`
  <span>${now}</span>
`;

However, templates are not updated just because time passes. That's what the template update() method is for.

By calling update(), the template and interpolated values are re-evaluated and updated in place.

Updating a template is shallow - sub-templates are not updated when the parent template is.

Events

Denier has a simple attribute directive for adding event handlers:

function doStuff(e) {
  // Stuff being done here
}

const button = html`
  <button ${on("click", (e) => doStuff(e))}>PRESS ME</button>
`;

Keyed lists

List items can be keyed to allow for efficient update handling:

const list = html`
  <ul>${items.map((item) => html`<li>${item.name}</li>`.key(item.id))}</ul>
`

"Reactive" components and state

  • state in, UI out
  • events in, state out

TBW

Cleanup

  • Cleanup handler

TBW

Directives

on

The on directive registers event handlers, as described in Events above.

ref

Use ref to get a reference to an HTML element:

<div ${ref((e) => doStuffWithElement(e))}></div>

flag

The flag directive sets or resets a boolean HTML attribute:

<div ${flag("visible", false)}></div>

provide

The provide directive injects an object instance into the DOM context, making it available to sub-elements:

html`
  <div
    ${
      provide(() => new MyObject())
    }
  >
  <!-- ... -->
  </div>
`;

using

The using directive looks up the closest provided object in the DOM context:

html`
    ${
      using(
        MyObject,
        (o) => html`The object can be accessed here: ${o.someProperty}`
      )
    }
`;

Note that the template is not rebuilt when the provided object is updated. Since the provided object can be of any type, it is not even a well-defined what "updated" means.

For this, you need Denier state objects.

build and state objects

To get automatic builds on state changes, provide an object that extends DenierState:

class CounterState extends DenierState<{ count: number }> {
  constructor() {
    super({ count: 42 });
  }
}

Then use the build directive:

html`
    ${
      // Build a template, initialized with the provided state.
      // The template gets updated on state changes.
      build(
        CounterState,
        (s) =>
          html`
            <div>
              Initial: ${s.get().count}, Current: ${() => s.get().count}
            </div>
          `
      )
    }
`;

Note the difference between using static and dynamic evaluation to get the initial and current values.

style

TBW

TODO

  • More docs
  • More tests and testing
  • More examples
  • Callbacks at mount to handle actions on remounted items.
  • HMR