File Conventions
Special filenames and directory conventions in an Avalon project.
Module-based apps
Scaffolded projects route from app/modules/<name>/pages/. The module name is the URL prefix except for main (also home, root, index), which maps to /.
| Path | Purpose |
|---|---|
app/modules/main/pages/index.tsx | Site root (/) |
app/modules/about/pages/index.tsx | /about |
app/modules/blog/pages/[slug].tsx | /blog/:slug |
app/modules/<name>/layouts/_layout.tsx | Layout for that module’s pages |
app/shared/layouts/_layout.tsx | Shared root layout (layoutsDir) |
Page filename conventions (index.tsx, [param].tsx, 404.tsx, _error.tsx, _middleware.ts) are the same inside any module’s pages/ folder. See Modules and File-System Routing.
Pages-only mode
Without modules in your Vite config, files in src/pages/ become routes automatically.
| Convention | Purpose |
|---|---|
index.tsx | Index route for the directory (/, /blog) |
[param].tsx | Dynamic route segment |
[...slug].tsx | Catch-all route |
404.tsx | Custom 404 page |
_error.tsx | Error boundary page |
_middleware.ts | Route middleware |
Special files (pages-only)
| File | Purpose |
|---|---|
_layout.tsx | Layout wrapper — wraps all pages in the same directory and subdirectories |
_error.tsx | Error boundary — renders when a page throws an error |
_middleware.ts | Route middleware — runs before page handlers in the same directory |
index.tsx | Index route — maps to the directory path (e.g. src/pages/blog/index.tsx → /blog) |
Layouts directory (pages-only)
Files in src/layouts/ wrap pages automatically based on directory structure.
| File | Wraps |
|---|---|
src/layouts/_layout.tsx | All pages |
src/layouts/blog/_layout.tsx | All pages under src/pages/blog/ |
src/layouts/docs/_layout.tsx | All pages under src/pages/docs/ |
Islands
Any component can be used as an island by adding the island prop when using it in a page or layout. There's no requirement to place islands in a specific directory.
// app/modules/main/pages/index.tsx
import Counter from '../components/Counter.tsx';
export default function Home() {
return (
<div>
<h1>Welcome</h1>
{/* This Counter will be hydrated on the client */}
<Counter island={{ condition: 'on:visible' }} initialCount={0} />
</div>
);
}
The framework is auto-detected from the component file extension:
.vue→ Vue.svelte→ Svelte.solid.tsx→ Solid.qwik.tsx→ Qwik.react.tsx→ React.tsx(default) → Preact
API routes
API routes live outside src/pages/ in a routes/ directory:
| File | Route |
|---|---|
routes/api/hello.ts | GET /api/hello |
routes/api/users/[id].ts | GET /api/users/:id |
// routes/api/hello.ts
import { defineEventHandler } from 'h3';
export default defineEventHandler(() => {
return { message: 'Hello' };
});
Config files
| File | Purpose |
|---|---|
vite.config.ts | Vite + Avalon configuration |
tsconfig.json | TypeScript configuration |
Public directory
Static files in public/ are served at the root path. public/logo.svg is available at /logo.svg.
Output directory
bun run build outputs to .output/ by default (configurable via Nitro's buildDir).