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.
| File | URL |
|---|---|
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
| File | URL |
|---|---|
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:
| File | URL | Param |
|---|---|---|
src/pages/blog/[slug].tsx | /blog/:slug | slug |
src/pages/users/[id]/profile.tsx | /users/:id/profile | id |
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/:
| File | Purpose |
|---|---|
_layout.tsx | Layout wrapper for that segment |
_middleware.ts | Route middleware |
_error.tsx | Error boundary |
404.tsx | Custom 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.