# No.JS Complete Documentation > No.JS is an HTML-first reactive framework for building dynamic web applications using only HTML attributes. Zero dependencies, no build step, no virtual DOM, CSP-compliant. Include one script tag and start writing directives. No.JS replaces JavaScript framework boilerplate with declarative HTML attributes. Directives like `get`, `bind`, `state`, `foreach`, `if`, and `on:click` handle data fetching, rendering, state management, and interactivity — all without writing JavaScript. Data lives in reactive contexts (Proxy-backed) that inherit through the DOM like lexical scoping. Directives execute by priority: state (0) → HTTP/error-boundary (1) → computed/watch (2) → ref (5) → structural (10) → rendering/events (20) → validation (30). ## Getting Started - [Installation & Quick Start](#getting-started) — CDN, npm, and your first page ## Core Guides - [Data Fetching](#data-fetching) — `get`, `post`, `put`, `patch`, `delete`, base URL, caching - [Data Binding](#data-binding) — `bind`, `bind-html`, `bind-*`, `model` - [Conditionals](#conditionals) — `if`, `else-if`, `show`, `hide`, `switch`/`case` - [Loops](#loops) — `foreach` (primary), `each`/`for` (aliases), loop variables, nesting - [Templates](#templates) — Reusable fragments, slots, remote templates - [State Management](#state-management) — `state`, `store`, `into`, `computed`, `watch`, persistence ## Interaction - [Events](#events) — `on:*`, modifiers, lifecycle hooks, `$event`, `$el` - [Forms & Validation](#forms--validation) — `validate`, `$form`, custom validators - [Actions & Refs](#actions--refs) — `call`, `trigger`, `ref`, `$refs` ## Presentation - [Dynamic Styling](#dynamic-styling) — `class-*`, `class-map`, `style-*`, `style-map` - [Animations](#animations--transitions) — `animate`, `transition`, stagger, built-in names - [Filters & Pipes](#filters--pipes) — Built-in filters, chaining, custom filters ## Advanced - [Routing](#routing--spa-navigation) — SPA navigation, params, guards, nested routes, View Transitions - [Head Management](#head-management) — Per-route `page-title`, `page-description`, `page-canonical`, `page-jsonld` - [Internationalization](#internationalization-i18n) — `t`, pluralization, locale switching - [Custom Directives](#custom-directives) — Extend No.JS with your own directives - [Error Handling](#error-handling) — Per-element errors, boundaries, global handler - [Configuration](#configuration--security) — Global settings, interceptors, CSRF, security - [Drag and Drop](#drag-and-drop) — `drag`, `drop`, sortable lists, multi-select - [Plugins](#plugins) — Plugin system with lifecycle hooks, interceptors, globals ## Reference - [Directive Cheatsheet](#directive-cheatsheet) — Every directive at a glance - [Full SPA Example](#examples) — Complete app with routing, auth, i18n & more - [Playground](#playground) — Interactive code editor # Getting Started ## Installation ### CDN (recommended) ```html ``` > When using the CDN `

Loading...

``` That's it. No `app.mount()`. No `createApp()`. No `NgModule`. It just works. --- ## How It Works 1. **Parse** — On `DOMContentLoaded`, No.JS walks the DOM looking for elements with known attributes. 2. **Resolve** — Each attribute maps to a **directive** that is executed by priority (data fetching first, then conditionals, then rendering). 3. **React** — All data lives in **reactive contexts** (Proxy-backed). When data changes, every bound element updates automatically. 4. **Scope** — Contexts inherit from parent elements, like lexical scoping. A `bind` inside an `each` loop can access both the loop item and ancestor data. --- ## Core Concepts ### Reactive Context Every element can have a **context** — a reactive data object. Contexts are created by `state`, `get`, `store`, etc. Child elements inherit their parent's context automatically. ``` body → context: { baseUrl } div[get] → context: { user: { name, email } } ← inherits from body span[bind="user.name"] ← reads from div's context div[each] → context: { post: { title } } ← inherits from div (can access user + post) ``` ### Directive Priority Directives run in a defined order: | Priority | Directives | Description | |----------|-----------|-------------| | 0 | `state`, `store` | Initialize local/global state | | 1 | `get`, `post`, `put`, `patch`, `delete`, `error-boundary`, `i18n-ns` | Fetch data, error boundaries, i18n namespace | | 2 | `computed`, `watch` | Derived values and side-effect watchers | | 5 | `ref` | Element references | | 10 | `if`, `switch`, `foreach`, `each`, `for`, `use`, `drag-list` | Structural (add/remove DOM) | | 15 | `drag`, `drop` | Drag and drop setup | | 16 | `drag-multiple` | Multi-select drag setup | | 20 | `bind`, `bind-*`, `model`, `class-*`, `style-*`, `on:*`, `show`, `hide`, `t`, `call`, `trigger`, `page-title`, `page-description` | Rendering, events, i18n, actions, head | | 30 | `validate` | Form validation side effects | ### Expression Syntax Most directive values accept **JavaScript expressions** evaluated against the current context: ```html
``` --- ## See Also - [Data Fetching](data-fetching.md) — make your first API call - [Directive Cheatsheet](cheatsheet.md) — every directive at a glance - [Configuration](configuration.md) — global settings and security **Next:** [Data Fetching →](data-fetching.md) # Data Fetching No.JS makes HTTP requests declarative — just add attributes to HTML elements. ## Base URL Set once on any ancestor element. All descendant `get`, `post`, etc. resolve relative URLs against it. ```html
...
...
``` Override for specific sections: ```html
...
``` Absolute URLs skip base resolution: ```html
...
``` ### Programmatic Configuration ```html ``` > **Note:** `retries` applies to 5xx server errors and network failures. Client errors (4xx) are not retried. ### Per-Request Headers ```html
``` --- ## `get` — Fetch and Render Data ```html
``` ### Attributes | Attribute | Type | Description | |-----------|------|-------------| | `get` | `string` | URL to fetch (GET request) | | `as` | `string` | Name to assign the response in the context. Default: `"data"` | | `loading` | `string` | Template ID to show while loading (e.g. `"#skeleton"`) | | `error` | `string` | Template ID to show on fetch error (e.g. `"#errorTpl"`) | | `empty` | `string` | Template ID to show when response is empty array/null | | `refresh` | `number` | Auto-refresh interval in ms (polling) | | `cached` | `boolean\|string` | Cache responses. `cached` = memory, `cached="local"` = localStorage, `cached="session"` = sessionStorage | | `into` | `string` | Write response to a named global store | | `debounce` | `number` | Debounce in ms (useful with reactive URLs) | | `headers` | `string` | JSON string of additional headers | | `params` | `string` | Expression that resolves to query params object | | `skeleton` | `string` | ID of a DOM element to show while loading and hide when done | ### Full Example ```html
``` ### Reactive URLs URLs that reference state variables re-fetch automatically when those values change: ```html
...
``` ### URL Interpolation and Encoding > ⚠️ **Breaking change (v1.10):** Values inside `{…}` placeholders are encoded with `encodeURIComponent`. This means `/` is encoded as `%2F`, which is correct for **query-string values** but will break **path segments** that intentionally contain slashes. > > ```html > >
...
> > >
...
> > >
...
> > >
...
> ``` --- ## `post`, `put`, `patch`, `delete` — Mutating Requests Used on forms or triggered via `call`. > **Tip:** The `call` directive now supports the same attributes as form-based HTTP directives — including `loading`, `headers`, `redirect`, and `body`. See [Actions & Refs → `call`](actions-refs.md) for full details. ### Form Submission ```html
``` ### PUT / PATCH / DELETE ```html
...
``` ### Mutation Attributes | Attribute | Description | |-----------|-------------| | `post`, `put`, `patch`, `delete` | URL for the request | | `body` | Request body (JSON string with interpolation). For forms, auto-serializes fields | | `success` | Template ID to render on success. Receives response as `var` | | `error` | Template ID to render on error. Receives error as `var` | | `loading` | Template ID to show during request | | `confirm` | Show browser `confirm()` dialog before sending | | `redirect` | URL to navigate to on success (SPA route) | | `then` | Expression to execute on success (e.g. `"users.push(result)"`) | | `into` | Write response to a named global store | | `cached` | Cache responses (memory/local/session) | ### Request Lifecycle ``` [idle] → [loading] → [success | error] ↓ ↓ render tpl render tpl exec `then` log to console `redirect` ``` --- ## Skeleton Placeholders (`skeleton=`) The `skeleton` attribute keeps a pre-rendered placeholder element visible while a request is in flight, then hides it once the response arrives. This prevents Cumulative Layout Shift (CLS) — the page holds its shape during loading instead of collapsing and re-expanding. ```html

``` The skeleton is hidden automatically when: - The response arrives (success) - A cached response is used - The response is empty (before rendering the `empty=` template) - The request fails (before rendering the `error=` template) ### Combined with `loading=` `skeleton=` and `loading=` serve different purposes and can be used together: | | `skeleton=` | `loading=` | |---|---|---| | Content | Pre-rendered element already in the DOM | Template cloned into the fetch element | | Visibility | Shown before JS, hidden after response | Injected by JS, replaced after response | | CLS impact | None — space is already reserved | May cause layout shift | | SSG-friendly | Yes | No | ```html
``` ### Skeleton element visibility The skeleton element (`id="skeleton"`) should start **visible** in the HTML (no `display: none` in CSS). `_hideSkeleton` sets `display: none` as an inline style, and `_showSkeleton` removes that inline style. If the skeleton has `display: none` in a CSS rule, removing the inline style will reveal the CSS `display: none` again — effectively keeping it hidden. Start the skeleton visible and let No.JS control its visibility: ```html
…placeholder…
``` ### Accessibility Consider adding `aria-busy="true"` on the fetch container while the request is in progress and `aria-hidden="true"` on the skeleton element when it is hidden. These attributes are not injected automatically in the current release. --- ## Interceptors Interceptors hook into the fetch pipeline to modify requests, inspect responses, or short-circuit the entire flow. They support async functions and have a 5-second timeout per interceptor. ### Basic Usage ```html ``` ### Async Interceptors Interceptor functions can be async. Each interceptor is given a 5-second timeout — if it does not resolve within that window, it is skipped with a warning and the chain continues. ```html ``` ### Cancelling Requests with `NoJS.CANCEL` Return an object keyed by `NoJS.CANCEL` from a request interceptor to abort the fetch. The request throws an `AbortError`, which triggers the element's `error` template if one is set. ```html ``` ### Serving Cached Responses with `NoJS.RESPOND` Return an object keyed by `NoJS.RESPOND` from a request interceptor to skip the HTTP call entirely and return the value directly as the response data. ```html ``` ### Replacing Response Data with `NoJS.REPLACE` Return an object keyed by `NoJS.REPLACE` from a response interceptor to replace the parsed response body with custom data. ```html ``` ### Header Redaction By default, interceptors receive redacted copies of requests and responses. Sensitive headers (`Authorization`, `Cookie`, `X-API-Key`, `X-CSRF-Token`, etc.) are stripped from the options object before it reaches untrusted interceptors, and sensitive response headers (`Set-Cookie`, `WWW-Authenticate`, etc.) are removed from the response proxy. URL query parameters matching patterns like `token`, `key`, `secret`, `auth`, `password`, or `credential` are replaced with `[REDACTED]`. Plugins installed with `{ trusted: true }` via `NoJS.use()` receive the full, unredacted request and response objects. See [Plugins → Trusted Interceptors](plugins.md#trusted-interceptors) for details. ### Sentinel Summary | Sentinel | Interceptor Type | Effect | | -------- | ---------------- | ------ | | `NoJS.CANCEL` | Request | Aborts the request (`AbortError`) | | `NoJS.RESPOND` | Request | Returns data directly, no HTTP call | | `NoJS.REPLACE` | Response | Replaces the parsed response body | --- ## See Also - [Actions & Refs](actions-refs.md) — `call` directive for click-triggered requests - [State Management](state-management.md) — `into` for writing responses to stores - [Configuration](configuration.md) — global API settings, CSRF, interceptors - [Error Handling](error-handling.md) — per-element and global error handling --- **Previous:** [Head Management ←](head-management.md) | **Next:** [Data Binding →](data-binding.md) # Data Binding Data binding connects your reactive state to the DOM. When state changes, bound elements update automatically — no manual DOM manipulation needed. ## `bind` — Text Content Replaces the element's `textContent` with the evaluated expression. ```html ``` Expressions inside `bind` are reactive — they re-evaluate automatically whenever the referenced state changes. You can also use [filters](filters.md) to transform values for display: ```html ``` --- ## `bind-html` — Inner HTML Renders evaluated expression as HTML. Sanitized by default. ```html
``` > ⚠️ Uses built-in DOMParser-based sanitization to prevent XSS. > **Tip:** You can configure a custom sanitizer via `NoJS.config({ sanitizeHtml: fn })`. See [Configuration → Security](configuration.md#security) for details. --- ## `bind-*` — Attribute Binding Bind any HTML attribute dynamically. ```html Profile
``` --- ## `model` — Two-Way Binding For form inputs, `model` creates automatic two-way data binding: ```html

Hello, . You are .

``` ### Radio Buttons ```html

Selected:

``` ### Multi-Select ```html

``` --- ## Common Mistakes ```html
``` --- ## See Also - [State Management](state-management.md) — creating the state that `bind` reads from - [Filters & Pipes](filters.md) — transform bound values for display - [Forms & Validation](forms-validation.md) — `model` with form validation - [Dynamic Styling](styling.md) — bind CSS classes and styles reactively --- **Previous:** [Data Fetching ←](data-fetching.md) | **Next:** [State Management →](state-management.md) # Conditionals ## `if` / `then` / `else` Renders one of two templates based on a condition. ```html

Welcome back!

``` --- ## `else-if` — Chained Conditionals ```html
``` --- ## `show` / `hide` Toggles `display: none` without adding/removing DOM elements. Better for frequently toggled elements. ```html
Welcome!
Please log in.
``` ### `if` vs `show` | | `if` | `show` | |--|------|--------| | Mechanism | Adds/removes DOM elements | Toggles CSS `display` | | Best for | Rarely toggled content | Frequently toggled content | | Preserves state | No (re-creates) | Yes | --- ## Switch / Case Render one of many templates based on a value. ```html
``` ### Inline Content (no templates) ```html
⏳ Pending 📦 Shipped ✅ Delivered ❌ Cancelled Unknown
``` ### Multi-Value Case ```html
``` --- --- ## See Also - [Loops](loops.md) — `foreach` with `filter` for conditional rendering of list items - [Dynamic Styling](styling.md) — `show`/`hide` alternative via `class-*` - [Templates](templates.md) — template-based conditional content with `then`/`else` **Previous:** [State Management ←](state-management.md) | **Next:** [Loops →](loops.md) # Loops ## `foreach` — Iterate Over Arrays `foreach` is the primary iteration directive. It repeats its content for each item in an array. ### Inline Children (default) When no `template` attribute is provided, the element's children are used as the repeating template: ```html
``` ### External Template Use the `template` attribute to reference a `