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

tagu-tagu

v4.4.1

Published

A lightweight helper for vanilla `HTMLElement`, with reactivity.

Readme

tagu-tagu

A lightweight helper for vanilla HTMLElement, with reactivity. No config, no jsx — only clean javascript.

tagu-tagu is

just a helper for HTMLElement:

import {button} from "https://cdn.jsdelivr.net/npm/[email protected]/dist/bundle.min.js";

const myButton = button("Hello!");// `HTMLButtonElement`
document.body.appendChild(myButton);

with reactivity!

import {button, span, div, useState} from "https://cdn.jsdelivr.net/npm/[email protected]/dist/bundle.min.js";

function CounterExample(){
    const count = useState(4);

    function decrementCount() {
        count.set(count.get() - 1);
    }
    function incrementCount() {
        count.set(count.get() + 1);
    }

    return div([
        button("-", { on: { click: decrementCount } }),// `HTMLButtonElement`
        span(count),// `HTMLSpanElement`
        button("+", { on: { click: incrementCount } }),// `HTMLButtonElement`
    ])
}

document.body.appendChild(CounterExample());

JSFiddle

No need to compile. But typescript is supported.

Examples

Initializers

Elements are initialized by rest parameters. Arguments can be any order.

button("Hello!", {css: {background: "blue"}});
button({css: {background: "blue"}}, "Hello!");

text initializer

import { div } from "tagu-tagu";

function SimpleTextExample() {
	return div("Hello");
}

document.body.appendChild(SimpleTextExample());

JSFiddle

or

import { div } from "tagu-tagu";

// Element: textContent
function TextExample() {
	return div({ text: "Hello" });
}

document.body.appendChild(TextExample());

JSFiddle

Children initializer

import { button, div, h1 } from "tagu-tagu";

// Element: append
function ChildrenExample() {
	return div(["Hello", button("World!"), "HELLO", h1("WORLD!")]);
}

document.body.appendChild(ChildrenExample());

JSFiddle

html initializer

import { div } from "tagu-tagu";

// Element: innerHTML
function HtmlExample() {
	return div({ html: `<button>Hello World!</button>` });
}

document.body.appendChild(HtmlExample());

JSFiddle

css initializer

import { button } from "tagu-tagu";

// element.style.setProperty
function CssExample() {
	return button({
		css: { width: "300px", height: "300px" },
	});
}

document.body.appendChild(CssExample());

JSFiddle

attr initializer

import { input } from "tagu-tagu";

// Element: setAttribute
function AttrExample() {
	return input({
		attr: { type: "color" },
	});
}

document.body.appendChild(AttrExample());

JSFiddle

prop initializer

import { option, select } from "tagu-tagu";

// Javascript properties
function PropExample() {
	return select([option("One"), option("Two"), option("Three")], {
		prop: { selectedIndex: 1 },
	});
}

document.body.appendChild(PropExample());

JSFiddle

on initializer

import { button } from "tagu-tagu";

// Element: addEventListener
function OnExample() {
	return button("Click Me", { on: { click: () => alert("Hello!") } });
}

document.body.appendChild(OnExample());

JSFiddle

data initializer

import { div, waitForData } from "tagu-tagu";

function DataExample() {
	return div({ data: { "my-data-key": "Hello World!" } });
}

const element = DataExample();
console.log(await waitForData(element, "my-data-key")); // Hello World!

JSFiddle

import { div, Modify, waitForData } from "tagu-tagu";

/**
 * Node can get data from ancestors.
 * Node can't get data from descendants.
 * */
function DataFromParentExample() {
	const parent = div();
	const element = div({ data: { "my-data-key": "Hello World!" } });
	const child = div();
	waitForData(parent, "my-data-key").then((data) => {
		parent.textContent = `Parent: ${data}`; // never
	});
	waitForData(child, "my-data-key").then((data) => {
		child.textContent = `Child: ${data}`; // displayed
	});
	return Modify(parent, [Modify(element, [child])]);
}

document.body.appendChild(DataFromParentExample());

JSFiddle

Modify existing element

You can use initializers for existing element.

import { Modify } from "tagu-tagu";

Modify(document.body, {
	text: "💣",
	css: {
		background: "skyblue",
	},
	on: {
		click: () => {
			document.body.textContent = "💥";
		},
	},
});

JSFiddle

$ initializer

<form>
    <div>Name: <input id="name"></div>
    <div>Age: <input id="age"></div>
    <button id="submit">Submit</button>
</form>
<script type="module" src="index.ts"></script>
import { Modify, style } from "tagu-tagu";

// Element: querySelector
function $Example() {
	Modify("form", {
		$: {
			"#name": { prop: { defaultValue: "Einstein" } },
			"#age": { attr: { type: "number" }, prop: { defaultValue: 26 } },
			"#submit": [
				style({
					"#submit": {
						background: "blue",
						color: "white",
						border: "none",
						"border-radius": "10px",
					},
					"#submit:hover": {
						background: "skyblue",
					},
				}),
			],
		},
	});
}

$Example();

JSFiddle

$$ initializer

<meta charset="utf-8">
<div>
    <h1>Unfertilized Eggs</h1>
    <button>🥚</button>
    <button>🥚</button>
    <button>🥚</button>
    <button>🥚</button>
    <button>🥚</button>
</div>
<div id="fertilized">
    <h1>Click!</h1>
    <button>🥚</button>
    <button>🥚</button>
    <button>🥚</button>
    <button>🥚</button>
    <button>🥚</button>
</div>
<script type="module" src="index.ts"></script>
import { Modify } from "tagu-tagu";

// Element: querySelectorAll
function $$Example() {
	Modify("#fertilized", {
		$$: {
			button: {
				on: {
					click: (e) => {
						(e.target as HTMLButtonElement).textContent = "🐣";
					},
				},
			},
		},
	});
}

$$Example();

JSFiddle

or you can use ModifyAll

import { ModifyAll } from "tagu-tagu";

function ModifyAllExample() {
	ModifyAll("#fertilized button", {
		on: {
			click: (e) => {
				(e.target as HTMLButtonElement).textContent = "🐣";
			},
		},
	});
}

ModifyAllExample();

JSFiddle

Callback initializer

import { button, div } from "tagu-tagu";

function InitializerCallbackExample() {
	return div([
		div([
			div([
				button("Deep", (button) =>
					console.log("debug:", button, "is created!"),
				),
			]),
		]),
	]);
}

document.body.appendChild(InitializerCallbackExample());

JSFiddle

Animation

animate initializer

Signal

useState

import { button, div, useState } from "tagu-tagu";

function SimpleStateExample() {
	const count = useState(0);

	function incrementCount() {
		count.set(count.get() + 1);
	}

	return div([div(count), button("+", { on: { click: incrementCount } })]);
}

document.body.appendChild(SimpleStateExample());

JSFiddle

useComputed

import { button, div, useState, useComputed } from "tagu-tagu";

function ComputedExample() {
	const count = useState(0);

	function incrementCount() {
		count.set(count.get() + 1);
	}

	return div([
		div(useComputed(() => (count.get() ? count.get() : "Zero"))),
		button("+", { on: { click: incrementCount } }),
	]);
}

document.body.appendChild(ComputedExample());

JSFiddle

import { button, div, useState, useComputed } from "tagu-tagu";

function TwoComputedSignalsExample() {
	const count = useState(0);

	function incrementCount() {
		count.set(count.get() + 1);
	}

	return div([
		div(
			useComputed(() => (count.get() ? count.get() : "Zero")),
			{
				css: {
					color: useComputed(() => (count.get() % 2 === 0 ? "blue" : "tan")),
				},
			},
		),
		button("+", { on: { click: incrementCount } }),
	]);
}

document.body.appendChild(TwoComputedSignalsExample());

JSFiddle

useEffect

import { button, div, useEffect, useState } from "tagu-tagu";

function EffectExample() {
	const count = useState(0);

	function incrementCount() {
		count.set(count.get() + 1);
	}

	useEffect(() => {
		console.log("count:", count.get());
	});

	return div([
		div("See console"),
		button("+", { on: { click: incrementCount } }),
	]);
}

document.body.appendChild(EffectExample());

JSFiddle

Cleanup in useEffect:

import { button, div, useEffect, useState } from "tagu-tagu";

function EffectWithCleanupExample() {
	const count = useState(0);

	function incrementCount() {
		count.set(count.get() + 1);
	}

	useEffect((effect) => {
		const countValue = count.get();
		effect.onCleanup(() => {
			console.log("Cleanup:", countValue);
		});
		console.log("Count:", countValue);
	});

	return div([
		div("See console"),
		button("+", { on: { click: incrementCount } }),
	]);
}

document.body.appendChild(EffectWithCleanupExample());

JSFiddle

If

import { div, If, input, span, useState } from "tagu-tagu";

function IfExample() {
	const isVisible = useState(false);

	function toggle() {
		isVisible.set(!isVisible.get());
	}

	return div([
		input({
			attr: { type: "checkbox" },
			prop: { checked: isVisible },
			on: { click: toggle },
		}),
		If(isVisible, () =>
			div({
				css: { background: "blue", width: "300px", height: "300px" },
			}),
		),
		span("Check to show rectangle"),
	]);
}

document.body.appendChild(IfExample());

JSFiddle

Switch

import { button, div, Switch, useState } from "tagu-tagu";

function SwitchExample() {
	const state = useState("triangle");

	return div([
		button("Triangle", { on: { click: () => state.set("triangle") } }),
		button("Rectangle", { on: { click: () => state.set("rectangle") } }),
		button("Circle", { on: { click: () => state.set("circle") } }),
		button("Pentagon", { on: { click: () => state.set("pentagon") } }),
		Switch(
			state,
			{
				triangle: () => div("▲"),
				rectangle: () => div("■"),
				circle: () => div("●"),
			},
			() => div("?"),
		),
	]);
}

document.body.appendChild(SwitchExample());

JSFiddle

For

import { button, div, For, useState } from "tagu-tagu";

function ForExample() {
	const numbers = useState([1, 2, 3].map((n) => ({ n })));
	let id = numbers.get().length;

	function addNumber() {
		id++;
		numbers.set([...numbers.get(), { n: id }]);
	}
	function removeNumber(n: number) {
		numbers.set(numbers.get().filter((value) => value.n !== n));
	}

	return div([
		div([
			For(numbers, (n) =>
				button(`${n.n}`, {
					on: { click: () => removeNumber(n.n) },
				}),
			),
		]),
		button("+", { on: { click: addNumber } }),
	]);
}

document.body.appendChild(ForExample());

JSFiddle

Await

import { div, span, Await, sleep } from "tagu-tagu";

function AwaitExample() {
	async function asyncFunction(){
		await sleep(2000);
		return "Finished!";
	}
	
	return div([
		Await(asyncFunction(), {
			pending: () => span("Loading..."),
			fulfilled: (value) => span(value)
		})
	]);
}

document.body.appendChild(AwaitExample());

JSFiddle

Promise<Element>

import { Await, div, sleep, span } from "tagu-tagu";

async function PromiseComponent() {
	await sleep(2000);
	return span("Finished!");
}

function AwaitPromiseOfElementExample() {
	return div([
		Await(PromiseComponent(), {
			pending: () => span("Loading..."),
		}),
	]);
}

document.body.appendChild(AwaitPromiseOfElementExample());

JSFiddle

Data binding

You can use data of ancestors.

import { button, div, useBinding, useState } from "tagu-tagu";

function Sky() {
	return div("Sky", {
		css: {
			background: useBinding("theme", (theme) =>
				theme === "dark" ? "darkblue" : "skyblue",
			),
		},
	});
}

function DataBindingExample() {
	const theme = useState("dark" as "dark" | "light");

	return div({ data: { theme } }, [
		Sky(),
		button("dark", { on: { click: () => theme.set("dark") } }),
		button("light", { on: { click: () => theme.set("light") } }),
	]);
}

document.body.appendChild(DataBindingExample());

JSFiddle

Seamless migration

Since tagu-tagu is just a helper, you can migrate from anywhere.