nanotags

nanotags logo
  • Tiny. Core is under 2.5 KB. Optional nanotags/context and nanotags/render entry points add ~400 B each, only if you import them.
  • Reactive props. Every prop is a Nano Stores atom, validated and coerced. Two-way sync between DOM attributes and state with no manual wiring.
  • Typed refs and events. r.one("button") infers HTMLButtonElement. ctx.on is typed against the correct event map for each target. Custom events are typed end-to-end.
  • Automatic cleanup. Listeners, effects, and bindings registered through ctx are removed on disconnect. No disconnectedCallback boilerplate.
  • Hydration-first. Designed for static-first stacks like Astro: the markup is already on the page, nanotags just wires up behavior.
  • No Shadow DOM, no template engine. Markup stays in regular DOM, styled with normal CSS.
<my-counter count="0">
  <span data-ref="display">0</span>
  <button data-ref="button">+1</button>
</my-counter>
import { define } from "nanotags"

define("my-counter")
  .withProps(p => ({ count: p.number(0) }))
  .withRefs(r => ({ display: r.one("span"), button: r.one("button") }))
  .setup(ctx => {
    ctx.on(ctx.refs.button, "click", () => {
      ctx.props.$count.set(ctx.props.$count.get() + 1)
    })

    ctx.effect(ctx.props.$count, val => {
      ctx.refs.display.textContent = String(val)
    })
  })

Documentation, interactive examples, and an llms.txt for AI assistants are at nanotags.psdcoder.dev.

In the same orbit

Explore more open source projects

Book a call

Irina Nazarova CEO at Evil Martians

Evil Martians is a developer tools consultancy founded in 2006. Creators of PostCSS, imgproxy, and 100+ open source projects with 25 billion downloads.