vdx-web (core framework) + vdx-ui (component library)
A modern web framework with ZERO npm dependencies. Reactive state, components, routing, two-way binding - all running directly in the browser with no build step.
- Zero dependencies - No npm, no node_modules, no supply chain risk
- No build step - ES6 modules run directly in the browser
- TypeScript support - Optional
.d.tsfiles for type checking (docs) - Reactive state - Vue-style proxy-based reactivity
- Two-way binding -
x-modelwith automatic type conversion - Component system - Web Components with fine-grained reactive rendering
- Router - Hash and HTML5 routing with lazy loading
- Static site friendly - Embed components in any HTML page
cd app
python3 test-server.pyThen open: http://localhost:9000/
That's it! No npm install, no build process, no dependencies to install.
New to VDX? Check out the Step-by-Step Tutorial for a comprehensive guide.
For CSS and HTML syntax highlighting in components, install es6-string-html.
Ctrl+P, then:
ext install Tobermory.es6-string-html
Use a /*css*/ comment in your styles to get highlighting:
styles: /*css*/`
.button {
background: #007bff;
}
`<!DOCTYPE html>
<html>
<body>
<my-counter></my-counter>
<script type="module">
import { defineComponent, html } from './lib/framework.js';
defineComponent('my-counter', {
data() {
return { count: 0 };
},
template() {
return html`
<div>
<h1>Count: ${this.state.count}</h1>
<button on-click="${() => this.state.count++}">
Increment
</button>
</div>
`;
}
});
</script>
</body>
</html>No compilation. No bundling. Just refresh your browser.
A feature even React doesn't have - automatic two-way binding with type conversion:
defineComponent('user-form', {
data() {
return {
username: '',
age: 18,
newsletter: false
};
},
template() {
return html`
<form on-submit-prevent="handleSubmit">
<input type="text" x-model="username" placeholder="Username">
<input type="number" x-model="age" min="13" max="120">
<label>
<input type="checkbox" x-model="newsletter">
Subscribe to newsletter
</label>
<button type="submit">Submit</button>
</form>
<pre>
Username: ${this.state.username}
Age: ${this.state.age} (type: ${typeof this.state.age})
Newsletter: ${this.state.newsletter}
</pre>
`;
},
methods: {
handleSubmit(e) {
console.log({
username: this.state.username,
age: this.state.age, // Already a number!
newsletter: this.state.newsletter // Already a boolean!
});
}
}
});The framework automatically uses the correct attribute (value or checked), sets up the right event, and converts types.
| Demo | Description |
|---|---|
| E-commerce Shop | Full shopping experience with routing and state management |
| Component Library | Interactive showcase of all vdx-ui components |
| Playground | Core framework features demo |
| Static Integration | Embedding components in static HTML |
| PWA Offline | Service worker with versioned caching for offline support |
| Task Manager (TS Demo) | Task management demo application written in typescript |
| Framework Tests | Comprehensive test suite |
- docs/tutorial.md - Step-by-step tutorial with hands-on examples
- docs/components.md - Component development patterns, props, children
- docs/templates.md - Template system, x-model, helpers
- docs/reactivity.md - Reactive state, stores, computed
- docs/routing.md - Router setup, lazy loading
- docs/security.md - XSS protection, input validation, CSP
- docs/bundles.md - Using pre-bundled versions
- docs/typescript.md - TypeScript support and demo app
- docs/api-reference.md - Complete API reference
- CLAUDE.md - Full reference for AI coding assistants (framework development)
- FRAMEWORK.md - Compact reference for AI coding assistants (app development)
Projects using VDX can reference the compact framework guide in their CLAUDE.md:
## Framework
This project uses VDX. See [FRAMEWORK.md](path/to/vdx/FRAMEWORK.md) for patterns.In the age of code rot, supply chain vulnerabilities, and dependency chains too big to review, this project explores another path.
- Zero npm dependencies - No package.json, no node_modules, no supply chain risk
- No build step - Runs directly in the browser using ES6 modules
- Fine-grained reactivity - Direct DOM updates, no virtual DOM diffing overhead
- Modern DX - Reactive state, components, routing, two-way binding
- Production ready - 290+ passing tests, XSS protection, used in real apps
- Template compilation system - Compiles `html`` templates once, applies values on re-render
- Smart two-way binding -
x-modelwith automatic type conversion - Chainable event handlers - Combine x-model with on-input/on-change for custom logic
- Auto-bound methods - No manual
.bind(this)needed - Computed properties - Memoized values with dependency tracking
- Virtual scrolling - Efficiently render massive lists
Components work like native HTML elements, making them perfect for enhancing static sites:
- Vanilla JS event listeners - Use
addEventListener()on components like any HTML element - DOM attribute propagation -
setAttribute()changes automatically flow into components - Direct prop setting - Pass arrays/objects/functions directly:
element.items = [...] - Children props - Pass HTML content inside component tags, access via
this.props.children - Nested component hydration - VDX components in light DOM children hydrate automatically, enabling SSG patterns
- No framework lock-in - Components integrate with jQuery, vanilla JS, or any other code
Important: Components are a boundary between vanilla JS and VDX. The framework manages everything inside a component's template - don't use DOM manipulation (appendChild, innerHTML, etc.) on elements inside components, as the reactive system will overwrite changes on the next render.
See the Static Integration Demo for live examples.
VDX supports automatic hydration of component trees from static HTML - perfect for static site generators (Hugo, Jekyll, Eleventy):
<!-- Server-rendered or static HTML -->
<collapsible-panel title="Settings">
<theme-switcher mode="dark"></theme-switcher>
<volume-control level="80"></volume-control>
</collapsible-panel>When the page loads, VDX automatically captures children, parses them as VNodes, and hydrates nested components recursively. No special hydration API needed.
- Template Compilation - `html`` templates compiled once to AST structure
- Template Instantiation - DOM nodes created with reactive bindings for each dynamic value
- Fine-Grained Updates - Only the specific DOM nodes that depend on changed state are updated
// Template compiled once when first rendered
const template = html`<div>${this.state.count}</div>`;
// On state change: only the text node for ${count} is updated, not the whole treeVDX uses fine-grained reactive rendering inspired by SolidJS:
- Each template binding (
${value}) creates its own reactive effect - State changes trigger O(1) updates to specific DOM nodes
- No full-tree diffing - direct DOM manipulation for maximum performance
- Scales to thousands of items with consistent performance
app/
├── lib/ # vdx-web: Core framework
│ ├── framework.js # Main barrel export
│ ├── framework.d.ts # TypeScript definitions
│ ├── router.js # Router system
│ ├── router.d.ts # TypeScript definitions
│ ├── utils.js # Utilities (notify, darkTheme, etc.)
│ ├── utils.d.ts # TypeScript definitions
│ └── core/ # Framework internals (~3000 lines)
├── dist/ # Pre-bundled versions for embedding
├── componentlib/ # vdx-ui: Professional UI component library
├── components/ # Shared UI components
├── tests/ # Test suite (293 tests)
├── ts-demo/ # TypeScript demo application
└── index.html # Entry point
1. Library imports (development):
import { defineComponent, html, reactive } from './lib/framework.js';
import { Router } from './lib/router.js';
import { notify, darkTheme } from './lib/utils.js';2. Pre-bundled (embedding/simple projects):
import { defineComponent, html, reactive } from './dist/framework.js';
// Everything in one ~74KB file- Symbol-based trust markers -
HTML_MARKERandRAW_MARKERare non-exported Symbols - Context-aware escaping - URLs, attributes, text content each handled correctly
- toString() attack prevention - Uses
Object.prototype.toString.call()for objects - No dangerous fallbacks - Removed all
dangerouslySetInnerHTMLexcept inraw()
// SAFE - Auto-escaped
html`<div>${userInput}</div>`
// SAFE - URL sanitized
html`<a href="${userUrl}">Link</a>`
// USE ONLY FOR TRUSTED CONTENT
html`<div>${raw(trustedApiHtml)}</div>`cd app
python3 test-server.pyThen open: http://localhost:9000/tests/
Requires modern browsers with ES6+ support:
- Chrome/Edge 61+
- Firefox 63+
- Safari 10.1+
No IE11 support - By design (IE11 EOL 2022)
Since there's no build step required, deployment is simple:
- Copy the
app/directory to your web server - Configure server routing if using HTML5 mode (see docs/routing.md)
That's it!
While VDX runs directly in the browser with no build step, there's an optional optimizer and bundler for production deployments. Unlike typical JavaScript tooling, these tools have zero npm dependencies - they're pure Node.js scripts.
| Feature | Benefit |
|---|---|
| Linting | Catch reactivity issues before deployment |
| Optimization | Automatic fine-grained reactivity (wrap expressions in contain()) |
| Minification | Smaller file sizes with accurate source maps |
| Bundling | Combine framework files with tree-shaking |
# Lint for reactivity issues (catches bugs that break fine-grained updates)
node optimize.js -i ./src -l
# Lint in strict mode (for CI - fail on unfixable issues)
node optimize.js -i ./src -l --strict
# Build with minification and source maps
node optimize.js -i ./src -o ./dist -m -s
# Bundle framework files
node bundler-esm.js- Wraps reactive expressions - Transforms
${this.state.x}in templates to use fine-grained reactivity automatically - Fixes early dereferences - Inlines
const { x } = this.statepatterns that would break reactivity - Strips runtime wrappers - Removes
eval(opt())calls (they become redundant after build-time optimization) - Minifies with source maps - Full JavaScript minification with accurate debugging support
The optimizer and bundler are ~3000 lines of vanilla JavaScript each. They:
- Parse and transform code using custom parsers
- Generate source maps with VLQ encoding
- Perform tree-shaking and dead code elimination
No Babel, no Terser, no webpack, no esbuild. Just Node.js.
You can:
- Never use the build tools - Everything works directly in the browser
- Use only the linter - Catch bugs without changing your workflow
- Use full optimization - For production deployments with smaller files
See docs/optimization.md for the complete guide.
You can use a service worker with vdx-web, which retains the caching and update benefits of a typical application bundler with less complexity and the added benefit of offline support. All you need to do is generate a new manifest before each build and the service worker handles the rest.
- spider-deps.js discovers all JS/CSS dependencies from your entry point
- Generates cache-manifest.json with content hash version
- Service worker caches all files atomically with version isolation
- App works offline with cache-first strategy
# Before deploying, generate the manifest
node spider-deps.jsSee the PWA Offline Demo for a working example, or check out the source code.
- Sustainable projects - No dependency rot or supply chain issues
- Embedded environments - Jellyfin Media Player, Electron apps, etc.
- Quick prototypes - Just drop in one file and start building
- Legacy modernization - Add reactive components without a complete rewrite
- Security-conscious projects - Audit the entire codebase (~3000 lines)
- Learning - Understand how modern frameworks work under the hood
In a world where a simple "hello world" requires hundreds of megabytes of dependencies, we asked: What if we didn't?
Zero dependencies doesn't mean zero features.
- lit-html - Tagged template literals for HTML
- Vue 3 - Proxy-based reactivity system
- SolidJS - Fine-grained reactive rendering
- Svelte - Component-first architecture
- Web Components - Native browser APIs
VDX Framework and all components are MIT licensed.
VDX consists mainly of code created using Claude. Quality assurance is achieved through extensive unit and end to end testing, as well as manual tests, performance evaluation, and through usage in other personal projects such as mrepo-web and my personal website.
This project is a quality framework with a high degree of optimization and iteration based on actual development experience and testing, but don't expect it to be perfect. There will be changes in the future but it's mostly feature complete and the core APIs are established and unlikely to change significantly.
There's no warranty if you build a business around my side project and get burned, but the framework in general avoids a lot of other risks such as code rot and supply chain breaches associated with extremely large NPM dependency graphs. (There are no external dependencies.) Since there is no build step, you could theoretically use this framework long into the future, as long as there are no major breaking changes in web browsers (less likely than your framework/build system/bundler/etc going out of style or deciding to leave you in the cold with a mountain of breaking changes).
PRs are welcome! Please ensure:
- No npm dependencies are added
- Tests pass (see
/app/tests/and/componentlib-e2e/) - Code follows existing patterns
- Security best practices maintained
- SolidJS - For inspiration on fine-grained reactivity
- Vue.js - For inspiration on reactivity system
- lit-html - For tagged template literal ideas
- developit/htm - For optimization ideas for template engine