GuideRoutes + loaders + API

Routing

Axonyx routes are file-first for pages and explicit for backend-style API blocks. This keeps the app readable without requiring a large JS router.

Page routes

A `page.asx` file maps to a route. The root page is `app/page.asx`. Nested folders create nested routes.

Code
ax
app/page.asx                  -> /
app/docs/page.asx             -> /docs
app/docs/routing/page.asx     -> /docs/routing
app/components/button/page.asx -> /components/button

Dynamic route params

Bracket folders define params. Use loaders to read the param and fetch the matching data.

Code
ax
app/docs/content/[slug]/page.asx -> /docs/content/:slug

Route context

Pages, layouts, and imported shell components can read the current route during server render. This keeps active navigation out of client JavaScript.

Code
ax
page DocsShell() {
  return ASX {
    <nav>
      <a href="/docs" aria-current={route.section == "docs"}>Docs</a>
      <a href="/roadmap" aria-current={route.path == "/roadmap"}>Roadmap</a>
    </nav>
  }
}

// route.path       -> /docs/getting-started
// route.section    -> docs
// route.subsection -> getting-started
// route.params     -> dynamic route params

Load data for a route

Query loaders belong beside the page and return data for render. Keep database reads here instead of hiding them in page markup.

Code
ax
// app/posts/loader.ax
export type Post {
  title: String
  summary?: String
}

query loadPosts(status: String = "published") -> Post[] {
  data posts = db.posts.where({ status: input.status }).order({ created_at: "desc" }).limit(6).all()
  return posts
}

// app/posts/page.asx
page Posts() {
  data status: String = "published"
  data posts = loadPosts(status)

  return ASX {
    <Each items={posts} as="post">
      <Card title={post.title}>
        <Copy>{post?.summary}</Copy>
      </Card>
    </Each>
  }
}

Reusable ASX components

Components can own params, local state, browser behavior, and an explicit `render ASX` boundary. The compiler emits component JS only on routes that actually use the component.

Code
ax
// app/components/theme-switch.asx
component ThemeSwitch(label: String = "Theme") {
  state selected: String = "silver"

  client JS {
    window.__themeSwitchReady = true;
  }

  render ASX {
    <label class="ax-field">
      <span>{label}</span>
      <select bind:value={selected} data-ax-behavior="theme">
        <option value="silver">Silver</option>
        <option value="bronze">Bronze</option>
        <option value="gold">Gold</option>
      </select>
    </label>
  }
}

// app/settings/page.asx
import { ThemeSwitch } from "@/components/theme-switch.asx"

page Settings() {
  return ASX {
    <Card title="Appearance">
      <ThemeSwitch label="Site theme" />
    </Card>
  }
}

Mutate with actions

Actions live beside the route too. They validate input, perform the mutation, then revalidate the page or query key that should refresh.

Code
ax
// app/posts/actions.ax
action CreatePost(title: string, excerpt: string) {
  db.posts.insert({ title: input.title, excerpt: input.excerpt, status: "published" })

  revalidate("/posts")
  return ok()
}

// app/posts/page.asx
<form method="post" action={action CreatePost} className="ax-form">
  <input name="title" className="ax-input" />
  <textarea name="excerpt" className="ax-textarea"></textarea>
  <Button type="submit">Add story</Button>
</form>

API routes

Backend-style routes can declare a method, path, typed input, and return shape. This is the base for forms, CMS actions, and tooling.

Code
ax
route POST "/api/posts" {
  input {
    title: String
    summary?: String = ""
    featured?: Bool = false
  }

  return json(input.title)
}

Inspect routes

Use the CLI to see what the app exposes before wiring UI or deploying.

Code
ax
cargo ax routes
cargo ax routes --format json
cargo ax api
cargo ax api --schema

Current rules

Pages stay readable

Use `.asx` pages for structure and UI composition, not heavy business logic.

Loaders fetch data

Use loaders for route-local data such as content collections or later database reads.

Actions mutate data

Use actions and typed route input for form-like operations and backend changes.

CLI exposes contracts

`routes`, `api`, and `actions` make app structure visible to humans and AI tools.