Routing
Routes are defined by filesystem convention inside the routes/ directory. No configuration file, no route registration — the file structure is the routing config.
File → URL mapping
routes/
index.page.md → /
about.page.html → /about
projects.page.ts → /projects
projects/
[id].page.ts → /projects/:id
[id]/
tasks.page.ts → /projects/:id/tasks
crypto/
index.page.md → /crypto/*
eth.page.ts → /crypto/eth
[coin].page.ts → /crypto/:coin
Dynamic parameters
Square brackets in filenames become URL parameters:
routes/projects/[id].page.ts → /projects/:id
Access parameters in your component via params:
override async getData({ params }: this['DataArgs']) {
// params.id is the matched URL segment
return fetchProject(params.id);
}
Flat file vs directory index
This distinction matters for nesting:
- Flat file
projects.page.tsmatches/projectsexactly - Directory index
projects/index.page.mdcatches all unmatched children under/projects/*
Both can coexist:
routes/
projects.page.ts → /projects (exact match)
projects/
index.page.md → /projects/* (catch-all for unmatched children)
[id].page.ts → /projects/:id (specific child)
/projects→projects.page.ts(exact match wins)/projects/42→projects/[id].page.ts(specific child)/projects/42/unknown→projects/index.page.md(catch-all)
Priority rules
When multiple routes could match a URL:
- Static segments win over dynamic:
/crypto/ethmatcheseth.page.tsbefore[coin].page.ts - Specific routes win over catch-all:
/projects/42matches[id].page.ts, notindex.page.md - Flat file wins for exact path:
/projectsmatchesprojects.page.ts, notprojects/index.page.md - File type precedence within the same route: when a route has more than one companion (
.ts,.html,.md),.page.ts>.page.html>.page.md— a.page.tscomponent always takes full control of rendering over sibling.html/.mdfiles at the same path. See Pages for the fallback chain when.page.tsis absent.
Error handling files
Special file types for error scenarios:
routes/
index.error.ts → Root error handler (catches everything)
projects/
index.error.ts → Error boundary for /projects/* (the name before
`.error.ts` is ignored — only its directory matters)
404.page.html → Custom "Not Found" page
401.page.ts → Custom "Unauthorized" page
Redirects
// routes/old-page.redirect.ts
import type { RedirectConfig } from '@emkodev/emroute';
export default { to: '/new-page', status: 301 } satisfies RedirectConfig;
Next: Nesting