Build a site with Azox

A link-sharing page, built from an empty directory and deployed at the end. Around twenty minutes. Every step explains why, not just what.

You need Node 18 or newer. Nothing else.

  1. Create the project

    The scaffold gives you two pages and a component, so routing and composition are visible from the start.

    terminal
    npx azoxjs create linkbox
    cd linkbox
    npm install

    Start the dev server and leave it running:

    terminal
    npm run dev

    Open http://localhost:4321. Every save rebuilds and reloads the page.

  2. Understand what you were given

    Look at pages/index.azox. Three parts, all optional except the markup.

    pages/index.azox
    <head>
      <!-- copied into the document head -->
    </head>
    
    <script>
      // imports and state
    </script>
    
    <main>
      <!-- the markup: one root element -->
    </main>

    The <script> block runs once when the page is built, not on every update. There is no render function, so there is no "runs on every render" to worry about.

  3. Replace the page

    Start with static markup. No state yet — it is worth seeing that a page with nothing reactive ships no update code at all.

    pages/index.azox
    <head>
      <title>Linkbox</title>
    </head>
    
    <main class="page">
      <h1>Linkbox</h1>
      <p class="tagline">Somewhere to keep good links.</p>
    
      <ul class="links">
        <li><a href="https://azox.dev">Azox</a></li>
        <li><a href="https://developer.mozilla.org">MDN</a></li>
      </ul>
    </main>

    Save. The page reloads. Nothing is styled yet — that is next.

  4. Add styles

    Azox has no CSS pipeline on purpose. Files in public/ are copied to the build root and served at the same path, so public/style.css is /style.css.

    public/style.css
    :root {
      color-scheme: dark;
      --bg: #0d0d12;
      --text: #f0f0f5;
      --dim: #9a9ab0;
      --accent: #6ee7f0;
    }
    
    body {
      margin: 0;
      background: var(--bg);
      color: var(--text);
      font: 16px/1.6 system-ui, sans-serif;
    }
    
    .page {
      max-width: 34rem;
      margin: 0 auto;
      padding: 4rem 1.5rem;
    }
    
    h1 { font-size: 2.2rem; margin: 0 0 0.3rem; }
    .tagline { color: var(--dim); margin: 0 0 2.5rem; }
    
    .links { list-style: none; padding: 0; display: grid; gap: 0.5rem; }
    .links a {
      display: block;
      padding: 0.9rem 1.1rem;
      background: #16161f;
      border: 1px solid #26263a;
      border-radius: 10px;
      color: var(--accent);
      text-decoration: none;
    }
    .links a:hover { border-color: var(--accent); }
  5. Make something reactive

    Now add state. A signal holds a value; reading it in markup binds that spot in the DOM to it.

    pages/index.azox
    <script>
      import { signal } from 'azox/reactivity';
    
      const clicks = signal(0);
    </script>
    
    <main class="page">
      <h1>Linkbox</h1>
      <p class="tagline">Somewhere to keep good links.</p>
    
      <ul class="links">
        <li><a href="https://azox.dev">Azox</a></li>
        <li><a href="https://developer.mozilla.org">MDN</a></li>
      </ul>
    
      <p class="count">Visited {clicks()} times</p>
    
      <button on:click={() => clicks.set(clicks() + 1)}>
        I clicked one
      </button>
    </main>

    Only the text inside .count updates when you click. The heading, the tagline and the links are never touched again after the page loads — there is no code that could touch them.

  6. Extract a component

    The two links are the same shape. A component declares what it accepts with props().

    components/Link.azox
    <script>
      const { href, label, note } = props();
    </script>
    
    <li class="link">
      <a href={href}>
        <strong>{label}</strong>
        <span class="note">{note}</span>
      </a>
    </li>

    Use it with a capitalised tag:

    pages/index.azox
    <script>
      import Link from '../components/Link.azox';
      import { signal } from 'azox/reactivity';
    
      const clicks = signal(0);
    </script>
    
    <main class="page">
      <h1>Linkbox</h1>
    
      <ul class="links">
        <Link
          href="https://azox.dev"
          label="Azox"
          note="The framework this is built with"
        />
        <Link
          href="https://developer.mozilla.org"
          label="MDN"
          note="Web documentation"
        />
      </ul>
    </main>

    Pass a prop the component never declared and the build stops with the name of the offending prop. Silently dropping it would hide a typo.

  7. Add a second page

    Create pages/about.azox. It becomes /about — the file layout is the routing table, and there is nothing to register.

    pages/about.azox
    <head>
      <title>About — Linkbox</title>
    </head>
    
    <main class="page">
      <h1>About</h1>
      <p class="tagline">
        Built with Azox, which compiles this page into HTML
        and a small module that wires up the bindings.
      </p>
      <p><a href="/">Back home</a></p>
    </main>

    Link to it from the home page with a plain <a href="/about">. There is no client-side router in this version, so each navigation is a real page load — which also means each page arrives as server-rendered HTML.

  8. Check your work

    doctor parses every page and resolves every component reference, so a mistake surfaces before you deploy.

    terminal
    npx azox doctor
    output
    Azox Framework v1.2.0 - The Sound of Future Web
    
      ✓ Node 22.11.0
      ✓ Azox v1.2.0
      ✓ pages/ (2 pages)
      ✓ /  (pages/index.azox)
      ✓ /about  (pages/about.azox)
    
    Everything looks good.
  9. Build it

    terminal
    npm run build

    Look at what came out. Each route is a directory with an index.html, which is what makes clean URLs work everywhere without rewrite rules.

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

    Serve it locally to check:

    terminal
    cd .azox/build
    python3 -m http.server 4321
  10. Deploy

    The output is a plain static site, so any host works. Two settings, whichever you use:

    SettingValue
    Build commandnpm run build
    Output directory.azox/build

    See deployment for Vercel, Netlify, Cloudflare Pages and GitHub Pages specifics.

What you learned

  • Pages are markup with optional <head> and <script> blocks
  • A signal binds one spot in the DOM to one value
  • Components declare props and are inlined at build time
  • The file layout is the routing table
  • public/ is copied as-is
  • The build is a static site that runs anywhere

Where to go next

  • Templates — complete sites to start from
  • Limitations — what Azox deliberately does not do, before you commit to it
  • Playground — try ideas without creating a project