# nanotags

> A typed, reactive Web Components wrapper powered by Nano Stores. Under 2.5 KB.

- URL: https://evilmartians.com/opensource/nanotags
- GitHub: https://github.com/psd-coder/nanotags
- Website: https://nanotags.psdcoder.dev/
- Authors: Pavel Grinchenko

---

* **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.

```html
<my-counter count="0">
  0
  <button data-ref="button">+1</button>
</my-counter>
```

```js
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](https://nanotags.psdcoder.dev/).
