AvalonAvalon
GitHub

File-System Routing

How Avalon maps page files to URL routes — module prefixes and src/pages mode.

How routing works

create-avalon uses module-based routing. Each folder under app/modules/ is a URL prefix. main (also home, root, and index) is the site root.

FileURL
app/modules/main/pages/index.tsx/
app/modules/about/pages/index.tsx/about
app/modules/blog/pages/[slug].tsx/blog/:slug

Add a module to add a route prefix. Full module layout, shared layouts, and aliases: Modules.

Without modules in your Vite config, routes come from src/pages/ instead. The file path maps directly to the URL path.

Static routes

FileURL
src/pages/index.tsx/
src/pages/about.tsx/about
src/pages/blog/index.tsx/blog
src/pages/blog/getting-started.tsx/blog/getting-started
src/pages/docs/introduction.mdx/docs/introduction

Dynamic routes

Wrap a segment in square brackets to create a dynamic route:

FileURLParam
src/pages/blog/[slug].tsx/blog/:slugslug
src/pages/users/[id]/profile.tsx/users/:id/profileid

Access the param via the event object in your page:

export default function BlogPost({ event }: { event: H3Event }) {
  const slug = event.context.params?.slug;
  return <article><h1>{slug}</h1></article>;
}

Catch-all routes

Use [...slug] to match any number of segments:

src/pages/docs/[...slug].tsx  →  /docs/anything/nested/here

Special files

In a module’s pages/ folder (e.g. app/modules/main/pages/) or under src/pages/:

FilePurpose
_layout.tsxLayout wrapper for that segment
_middleware.tsRoute middleware
_error.tsxError boundary
404.tsxCustom 404 page

API routes

API routes live in routes/api/ (not src/pages/):

routes/api/hello.ts          →  GET /api/hello
routes/api/users/[id].ts     →  GET /api/users/:id
// routes/api/hello.ts
export default defineEventHandler(() => {
  return { message: 'Hello from the API' };
});

MDX pages

.mdx files work as pages too. They use the same frontmatter-based layout system:

---
title: My Doc
currentPath: /docs/my-doc
---

# My Doc

Content here.

Client navigation opt-out

When clientRouter: true is on, a page can force a full load instead of a DOM swap:

export const clientNavigation = false;

MDX: clientNavigation: false in frontmatter. Per-link opt-out is data-router-reload. See Client Navigation.