Skip to content

Repository files navigation

mini

A deliberately tiny signals UI runtime. Real nodes, created once, mutated forever by signals — no virtual DOM, no diffing, no re-render.

status  license  deps  vibe coded


What is mini?

A UI runtime small enough to read in an afternoon, built on alien-signals. It ships in two shapes:

Package What it renders to
@amritk/mini The DOM. Reactive bindings, keyed lists, static-template cloning, and a compilerless JSX runtime.
@amritk/mini-lynx Lynx, through its Element PAPI. The engine's own elements, attributes and events — no vocabulary in between.

A third package, @amritk/mini-helpers, holds the handful of helpers that turned out identical in both — route matching, query parsing, JSON Schema compilation. Both packages depend on it and re-export it, so you never import it directly; it is separate only because its charter is worth enforcing: no reactivity, no platform.

Both are compilerless: there is no custom build step, just the standard react-jsx transform pointed at the package's own runtime.

The one idea

JSX builds a real node once. Signals mutate it forever. Nothing diffs.

const Counter = () => {
  const count = signal(0)
  return (
    <button onclick={() => count(count() + 1)}>
      {() => `clicked ${count()} times`}
    </button>
  )
}

The component function runs a single time. The <button> is created once and never rebuilt; only the text node inside it is ever written to again.

The reactivity rule

Nothing analyses your expressions, so reactivity is decided by value shape at runtime:

  • A function-valued attribute, child, or show is reactive.
  • Any other value is static — applied once, never again.

Signals are zero-argument functions, so passing one without calling it is already a live binding:

<button disabled={streaming}>      {/* reactive — tracks forever    */}
<button disabled={streaming()}>    {/* STATIC — frozen at creation! */}

That second line is the one real footgun, so it is guarded three ways: live in the editor and dev server through @amritk/mini/vite, in this repo's CI through bun run check:reactivity, and in your own CI through the same plugin.

The cap is the design

There is no virtual DOM, no diffing, and no re-render — and that is a commitment, not a roadmap item. It buys two things:

  • The . entry stays tiny and flat. Every feature only some apps need lives on its own subpath with its own module graph, so importing /router pulls in none of /forms. A size-budget test and an import-boundary test hold that line in CI, per package.
  • Do not rebuild what the target already does. @amritk/mini-lynx used to own a platform-neutral vocabulary and a pluggable host so a component could run anywhere. Lynx already solves that one layer down, so the package dropped the abstraction and became a thin layer over the engine — about 5,000 lines lighter, and with the ceiling moved from "what this package has named" to "what the engine can do". See docs/mini-lynx-runtime.md.

If a feature seems to be missing, the correct next step is usually a bigger framework (Preact, Solid), not a new helper here.

Quick start

npm install @amritk/mini
# or: pnpm add / yarn add / bun add
// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@amritk/mini"
  }
}
import { mount, signal } from '@amritk/mini'

mount(document.body, Counter)

Full API, subpath modules, and the Lynx runtime's engine boundary are in the package READMEs:

  • @amritk/mini — DOM bindings, list, template, and the /router /flow /forms /query /hot /vite subpaths.
  • @amritk/mini-lynx — the Lynx element vocabulary, renderPage, the in-memory Element PAPI for tests, and the /flow /composition /router /forms /query /engine /testing subpaths.
  • @amritk/mini-helpers — the pure helpers both of the above share, and the bar for adding to them.

Playgrounds

Two kitchen-sink demos live in apps/, each exercising every public entry point of its package and deployed to Cloudflare Workers as a static SPA:

  • playground-mini — signals, the JSX runtime, every binding, /flow, /forms, /query, and /router (which the app itself runs on).
  • playground-mini-lynx — Lynx elements, CSS, <list>, event propagation, /flow, /composition, /forms, /query and /router, previewed through a DOM implementation of Lynx's Element PAPI. Its /engine screen renders a second tree through the in-memory PAPI and prints the call log, so the reconciler's cost is countable.
bun run --filter '@amritk/playground-mini' dev
bun run --filter '@amritk/playground-mini-lynx' dev

For AI agents & LLMs

Each package ships an AI.md next to its README.md: a mental model, a minimal runnable example, and the gotchas most likely to trip up a model. The repo root carries llms.txt (a curated index, per llmstxt.org) and llms-full.txt (every AI.md inlined, for pasting into context).

Editing the repo rather than consuming it? Start at AGENTS.md, then the per-package AGENTS.md for the invariants that package cannot break.

Requirements

  • Consumers: any ES2022 runtime. @amritk/mini needs a DOM; @amritk/mini-lynx needs only a host.
  • Development: Bun ≥ 1.1.

Development

bun install
bun run test                # every package's tests
bun run check               # biome lint + format
bun run check:reactivity    # the called-signal footgun guard
bun run types:check         # both packages, both of mini-lynx's passes
bun run build               # build both packages
bun run test:dist           # load and drive the built artifacts (needs a build)

See CONTRIBUTING.md for the full workflow, and .claude/architecture.md for how the two packages relate.

Pre-alpha. Breaking changes can land in any 0.x release; they ride a minor bump.

Related

@amritk/mini/forms can validate against a JSON Schema through @amritk/runtime-validators, an optional peer published from the mjst repo, where both packages used to live.

License

MIT

About

Front-end framework modeled after vue vapor but aiming for lighter bundle size, accidendally almost re-creating solid :)

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages