Routing and Layouts
aihu uses file-based routing. Pages live under src/pages/ and automatically become routes when compiled.
File-based routing
Any .aihu file under src/pages/ that contains an @route block is treated as a page route. The file path determines the default URL pattern:
| File | Default pattern |
|---|---|
src/pages/index.aihu |
/ |
src/pages/about.aihu |
/about |
src/pages/users/[id].aihu |
/users/:id |
File-based routing tree example
src/
pages/
index.aihu → /
about.aihu → /about
users/
index.aihu → /users
[id].aihu → /users/:id
admin/
index.aihu → /admin
users.aihu → /admin/users
layouts/
default.aihu → wraps all routes without an explicit layout
admin.aihu → wraps routes with layout: adminThe @route block
@route {
path: /admin/users
name: admin-users
middleware: [auth, admin]
ssr: true
layout: admin
}Fields:
path— explicit URL path override. If omitted, the path is derived from the file location.name— route name used in programmatic navigation and the route manifest.middleware— array of middleware names applied to this route.ssr— boolean.trueenables server-side rendering for this route.layout— layout name to wrap this route's content.
.route.json sidecars
The compiler emits a .route.json file alongside each compiled SFC that has an @route block. Example:
{
"pattern": "/admin/users",
"name": "admin-users",
"middleware": ["auth", "admin"],
"ssr": true,
"layout": "admin"
}Read a sidecar programmatically with readRouteSidecar(path) from @aihu/router/plugin.
viteRouterIntegration()
viteRouterIntegration() is a Vite plugin (from @aihu/router/plugin) that scans src/pages/ at build time, reads all .route.json sidecars, and assembles a virtual route manifest module:
// vite.config.ts
import { defineConfig } from 'vite'
import { viteRouterIntegration } from '@aihu/router/plugin'
export default defineConfig({
plugins: [viteRouterIntegration()],
})The virtual module virtual:aihu-routes exports the assembled RouteDefinition[] array. The runtime createRouter consumes it to handle navigation.
viteRouterPlugin is a deprecated alias removed at v1.0.
createRouter(routes)
Creates a router instance from an array of route definitions. Typically you pass the virtual module directly:
import { createRouter } from '@aihu/router'
import routes from 'virtual:aihu-routes'
const router = createRouter(routes)Layouts
Layouts live under src/layouts/. The default layout is src/layouts/default.aihu. A layout wraps the page's rendered output via <slot>:
@template {
<header>My App</header>
<main>
<slot />
</main>
<footer>Footer</footer>
}scanLayouts(dir) from @aihu/router/plugin returns all discovered layout names.
Router middleware
Middleware is defined with defineRouterMiddleware and composed with composeRouterMiddleware:
import { defineRouterMiddleware, composeRouterMiddleware } from '@aihu/router'
const authMiddleware = defineRouterMiddleware(async (ctx, next) => {
if (!ctx.params.token) {
return { kind: 'redirect', location: '/login', status: 302 }
}
return next()
})
const loggingMiddleware = defineRouterMiddleware(async (ctx, next) => {
console.log('navigating to', ctx.url.pathname)
return next()
})
export const composed = composeRouterMiddleware(loggingMiddleware, authMiddleware)Stage ordering
Middleware passed to composeRouterMiddleware are called in array order. Any middleware that returns a non-void result (e.g. { kind: 'redirect' } or { kind: 'cancel' }) short-circuits the chain — subsequent middleware and the route handler are not called.
Standard stage ordering convention:
- Logging / tracing
- Auth / session
- Redirect rules
- Render
Reactive routing primitives
The router exposes reactive primitives for use in SFCs and TypeScript:
| Export | Description |
|---|---|
useRoute() |
Reactive accessor returning the current route match |
useRouter() |
Access the router instance |
navigate(path, opts?) |
Programmatic navigation |
createRouteSignal(router) |
Signal bound to the current route |
createPrefetcher(router) |
Create a route prefetcher |
provideRouteContext(router) |
Provide route context to the component tree |
RouteContext |
Context token for the current route |