# no build · no deps · no rot · ai-native

Build like it's a modern framework. Ship like it's just files.

Reactive components, two-way binding, routing and a 60+ component UI library — running straight from source in the browser.

— a VDX component dropped into this page. Nothing compiled it.

What actually lands in your repository

The dependency you never installed can't be the one that bites you.

node_modules/

hundreds of packages

pulled transitively, from maintainers you'll never meet

  • Re-resolved on every clone and CI run
  • A live attack surface — Shai-Hulud & friends ship through updates
  • Rots underneath you; upgrade or rewrite every few years

vdx/

one folder of files

read once, reviewed once, then simply yours

  • Vendored into the repo — clones and updates pull nothing
  • Nothing to compromise on npm update, because there is no npm
  • Runs the same in five years as it does today

Edit it. Run it. No build in the loop.

These run in a sandbox from the same source you'd write — code on the left, the living result on the right. Change the code and hit Run.

// a component is a class — reactive state, no setState, no virtual DOM

Reactive by assignment

// two-way binding + lists — x-model, each(), when(), on-* handlers

Forms that sync themselves

// shared state, zero ceremony — one store, many components, no prop-drilling or provider

Shared state without the boilerplate

A modern framework, without the modern tax

$ (no build)

No build step

ES modules run directly in the browser. Save a file, refresh. There is no compiler between you and the page.

deps: 0

Zero dependencies

Nothing from npm — not even transitively. The whole framework is a small set of files you can read.

router + ui

Batteries included

Hash and HTML5 routing, reactive stores, and a 60+ component cl-* UI library, all in the box.

<my-widget>

Static-site friendly

Import a component into any HTML page and use its tag. Perfect for a backend that just serves files.

xss ✕

Defense in depth

Auto-escaping, URL-scheme allowlisting, and raw HTML gated behind an explicit, trust-marked opt-in.

light DOM

DevTools-native

No shadow DOM. Your components are right there in the element tree — inspect, tweak, and debug them like normal HTML.

You don't have to pick your poison

A modern component model without React's build chain — or vanilla's manual DOM wiring.

 VDXReactVanilla JShtmx
Build stepNoneRequiredNoneNone
Bundling & lintingOptionalRequiredDIYNone
TypeScriptOptionalVia buildDIY
npm dependenciesZeroManyZeroOne script
Reactive stateBuilt inBuilt inRoll your ownServer round-trip
Manual DOM syncNeverNeverConstantServer-driven
XSS-safe by defaultYesYesOn youOn you
Two-way bindingx-modelManualManual
Router + UI libraryIncludedAdd packagesAdd packages
Supply-chain surfaceNoneLargeNoneTiny
Survives a framework eraVendoredChurnsForeverStable

# ai-native

An agent can read the whole thing

Most frameworks assume a human who already knows them — the right hooks, this year's patterns, the current build config. VDX assumes a reader starting from the files in front of them, human or model.

# one file

Small enough to hold in context

Point an agent at the source — or the single-file FRAMEWORK.md — and it has the whole model. No plugin matrix, no version-specific lore, no build config to reverse-engineer.

el.state

Inspect the running app

No shadow DOM, so any component is reachable from the DOM. Read or poke its live state in plain JS ($ is your devtools' querySelector) — no framework extension needed.$('cart-badge').stores.cart.count

no hooks

Testing without the ceremony

No hooks, no renderer, no act(). Grab the element, call its methods, read its state. The wiring that made React hooks a testing chore simply isn't here.

You opt out of safety, never into it

Escaping isn't a function you remember to call — it's the default. Paste hostile input into the demo: the markup renders as plain text and the javascript: URL goes nowhere. Rendering raw HTML is the part you ask for — hit Edit to read the markup.

  • Raw HTML is trust-markedraw() tags its output at runtime, so a JSON string from an API can never masquerade as trusted markup.
  • Misuse is legible — an unsafe render is a literal raw(userInput) in the source, easy to spot in review and to grep for.
  • Nothing is hidden — no shadow DOM, so every node your components render is right there in DevTools to inspect and audit.

Straight answers

The questions you'd actually ask before betting on a small framework.

Is anyone actually using this?

Honestly: it's a side project, not a movement. But it's one the author builds real software with — the largest app on it is an extensive music player whose windowed 10,000-track queues and touch gestures are the reason those are first-class features here — and it's maintained for the most reliable reason there is: so those apps never rot.

What if the maintainer walks away?

Then you have exactly what you had yesterday: a folder of readable source with zero upstream dependencies. The long-term bet is that browsers keep not breaking userspace — a far safer bet than every package in a typical React tree staying healthy. The codebase is also small and legible enough that AI agents do real maintenance on it today; the commit history is the proof.

How big is it, really?

The framework is a single ~27 KB gzipped file (~33 KB with the router and utils), and the readable core is about 8,000 lines — an afternoon, not an archaeology project. That loads faster than many bundled SPAs; a heavily optimized SSR stack will still beat it to first paint. It's small and honest, and there's no hydration step to pay for.

Which browsers?

Anything modern — evergreen desktop and mobile. The polyfill era is over: build steps today mostly exist for TypeScript and JSX, and here both are unnecessary — templates are tagged literals, and TypeScript is supported via shipped .d.ts files and tsc --noEmit.

What about SEO and SSR?

No SSR, on purpose. Where SEO actually matters — content pages — the islands model keeps your content (or whatever your backend, CMS, or Hugo generates) as real static HTML and drops components into it, so there's nothing to hydrate and no server-side rendering surface to secure. For app screens behind a login, SSR was never buying you much. Service workers and offline support are in the box.

How do I update a vendored copy?

VDX ships as versioned releases (v1 up) with release notes. Updating is: replace your vdx/ folder, skim the notes, run your tests. No lockfile solver, no peer-dependency sudoku — the diff is the whole story.

Vendor it in two minutes

No installer to trust, no toolchain to set up. Drop the files in, serve them, done.

1
curl -LO https://github.com/iwalton3/vdx-web/releases/latest/download/vdx-full.zip
unzip vdx-full.zip -d your-app/   # vendored into your-app/vdx/
# smaller cuts: vdx-core.zip · vdx-framework.zip · vdx-ui.zip — or clone the repo and copy lib/ui/styles
2
cd your-app && python3 -m http.server 9000   # any static server — nothing to install
3
<!-- in your page -->
<my-widget></my-widget>
<script type="module">
  import { defineComponent, Component, html } from './vdx/lib/framework.js';
</script>