Getting started

Azox compiles .azox files into server-rendered HTML plus a small module that wires up signal bindings in the browser.

Stable. Azox is at v1.2.0. The template syntax, the reactivity exports and the shape of the build output will not change without a 2.0. See limitations for what it deliberately does not do.

Requirements

Node 18 or newer. Nothing else — Azox has no dependencies.

Create a project

terminal
npx azoxjs create my-app
cd my-app
npm install
npm run dev

The dev server starts at http://localhost:4321, rebuilds when you save, and reloads the browser.

The npm package is azoxjs, but the command it installs is azox. The short name was already blocked on npm.

What you get

my-app/
my-app/
├── package.json
├── pages/
│   ├── index.azox        →  /
│   └── about.azox        →  /about
├── components/
│   └── Counter.azox
└── public/               copied to the build root

There is no configuration file. The folder layout is the configuration: pages become routes, and public/ is copied as-is.

Your first page

A page is markup, with an optional <script> block for state and an optional <head> block for document metadata.

pages/index.azox
<head>
  <title>My app</title>
</head>

<script>
  import { signal } from 'azox/reactivity';

  const count = signal(0);
</script>

<main>
  <h1>Hello</h1>

  <button on:click={() => count.set(count() + 1)}>
    Clicked {count()} times
  </button>
</main>

signal(0) creates a reactive value. Reading it as count() inside markup binds that spot in the DOM to it — and only that spot.

Build for production

terminal
npm run build

The output lands in .azox/build/:

.azox/build/
index.html            the / route
page.client.js        its bindings
azox-runtime.js       shared by every page
about/
├── index.html        the /about route
└── page.client.js
style.css             copied from public/

That directory is a complete static site. Every route is a directory with an index.html, so clean URLs work on any host without rewrite rules. See deployment.

Next