The HTML-First Reactive Framework
Build dynamic, reactive web applications using nothing but HTML attributes. No build step. No virtual DOM. No transpiler. No JSX. Just HTML.
<div get="/users/1" as="user">
<h1 bind="user.name">Loading...</h1>
<p bind="user.email"></p>
</div>Zero JavaScript written. Fully reactive. Real API data.
- Declarative HTTP —
get,post,put,patch,deleteas HTML attributes - Reactive Binding —
bind,bind-*,modelfor one/two-way data binding - Conditionals & Loops —
if,else-if,show,hide,each,foreach,switch - State Management —
state(local),store(global),computed,watch,notify() - Head Management —
page-title,page-description,page-canonical,page-jsonldfor SEO-friendly SPAs - SPA Routing —
route,route-view, guards, params, nested routes, wildcard catch-all,focusBehavior - Forms & Validation — Built-in + custom validators, per-rule errors, async support,
$formcontext - Plugin System — Extend with reusable packages: interceptors, globals, directives, lifecycle hooks
- Animations —
animate,transitionwith stagger support - i18n —
tdirective with pluralization, namespaces, browser detection - Filters —
uppercase,currency,date,truncate, 32 built-in pipes - Drag & Drop —
drag,drop,drag-list, multi-select, keyboard DnD - DevTools — Built-in inspector with context mutation, store inspection, element highlighting
- Security — DOMParser-based sanitization, CSP-safe (no eval/Function), header redaction, prototype pollution protection
- Custom Directives — Extend with
NoJS.directive() - TypeScript Support — Type definitions for plugin authors (
types/nojs-plugin.d.ts)
<script src="https://cdn.no-js.dev/"></script>With the CDN, No.JS auto-starts on DOMContentLoaded. You can configure it before it loads:
<script>
NoJS.config({
debug: true,
router: {
useHash: true
},
});
</script>npm install no-js-framework// ESM
import NoJS from 'no-js-framework';
// CommonJS
const NoJS = require('no-js-framework');<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.no-js.dev/"></script>
</head>
<body base="https://jsonplaceholder.typicode.com">
<!-- Fetch & display data -->
<div get="/users" as="users" loading="#skeleton">
<div each="user in users">
<h2 bind="user.name"></h2>
<p bind="user.email"></p>
</div>
</div>
<!-- Local state + events -->
<div state="{ count: 0 }">
<button on:click="count++">Clicked <span bind="count"></span> times</button>
</div>
<!-- Form with validation -->
<form post="/posts" validate success="#ok">
<input name="title" required minlength="3" />
<button type="submit" bind-disabled="!$form.valid">Submit</button>
</form>
<template id="skeleton"><p>Loading...</p></template>
<template id="ok" var="res"><p>Created: <span bind="res.title"></span></p></template>
</body>
</html>No app.mount(). No createApp(). No NgModule. It just works.
Extend No.JS with reusable packages — analytics, auth, feature flags, UI libraries — without modifying the core.
<script>
NoJS.use({
name: 'analytics',
version: '1.0.0',
capabilities: ['interceptors', 'globals'],
install(app, options) {
app.global('analytics', { pageViews: 0 });
app.interceptor('response', (response, url) => {
console.log('API call:', url, response.status);
return response;
});
},
init(app) {
console.log('Analytics ready');
},
dispose(app) {
console.log('Analytics cleaned up');
}
});
</script>Plugins have access to the full API: directive(), filter(), validator(), interceptor(), global(), on(), and more.
Full documentation is available in the docs/ folder:
| Guide | Description |
|---|---|
| Getting Started | Installation, core concepts, how it works |
| Data Fetching | get, post, put, patch, delete, caching, polling |
| Data Binding | bind, bind-html, bind-*, model |
| Conditionals | if, else-if, show, hide, switch/case |
| Loops | each, foreach, loop variables, nested loops |
| Templates | Reusable fragments, slots, remote templates |
| State Management | state, store, into, computed, watch |
| Events | on:*, modifiers, lifecycle hooks |
| Dynamic Styling | class-*, style-* |
| Forms & Validation | validate, $form, custom validators |
| Routing | SPA navigation, guards, nested routes |
| Animations | animate, transition, stagger |
| i18n | Translations, pluralization, formatting |
| Filters | Built-in filters, chaining, custom filters |
| Actions & Refs | call, trigger, ref, $refs |
| Plugins | Plugin API, interceptors, globals, lifecycle |
| Custom Directives | Extend No.JS |
| Error Handling | Error boundaries, global handler |
| Configuration | Global settings, interceptors, template caching, security |
| Cheatsheet | Every directive at a glance |
| Full SPA Example | Complete app with routing, auth, i18n |
- Parse — On
DOMContentLoaded, No.JS walks the DOM for known attributes - Resolve — Each attribute maps to a directive, executed by priority
- React — Data lives in Proxy-backed reactive contexts; changes auto-update the DOM
- Scope — Contexts inherit from parents, like lexical scoping
- Secure — Expressions run in a sandboxed evaluator (no eval, no Function); HTML is sanitized via DOMParser
| Tool | Description |
|---|---|
| NoJS-LSP | VS Code extension — autocomplete, hover docs, diagnostics for No.JS HTML |
| NoJS-MCP | MCP server — AI tools for building No.JS apps |
| NoJS-Skill | Claude Code skill — guided No.JS project generation |
| NoJS-CLI | CLI tool — scaffold, dev server, build & deploy No.JS apps |
Join the conversation and get help:
Contributions are welcome! Please open an issue or submit a pull request.
No.JS — Because the best JavaScript is the JavaScript you don't write.
Zero dependencies · MIT License