Documentation
Docs
Introduction
Getting Started
Tutorial: build an app
Core Concepts
Routing
Server-Side Rendering
Mimir, state management
Pages & Layouts
API Routes
Styling & Theming
Building & Deploying
API Reference
Guides
Recipes
FAQ (use cases)
File-based routing
In rune, the filesystem is the route table. Files under pages/ become URLs by their path, no central
list to maintain. scanRoutes("pages") reads the directory and produces a sorted list of routes the server
registers.
The mapping rules
scanRoutes walks pages/, ignores convention files and tests, and converts each remaining file path to a
route pattern:
| File | Pattern | Notes |
|---|---|---|
pages/index.tsx | / | index is the segment root |
pages/about.tsx | /about | a plain file |
pages/blog/index.tsx | /blog | nested index |
pages/blog/post.tsx | /blog/post | nested file |
pages/blog/[slug].tsx | /blog/:slug | dynamic segment |
pages/shop/[...rest].tsx | /shop/*rest | catch-all |
pages/(marketing)/pricing.tsx | /pricing | (group) folders are stripped |
The transforms, in the order scanRoutes applies them:
(group)/and a trailing(group)are removed (grouping without affecting the URL).[...name]becomes*name(catch-all).[name]becomes:name(dynamic segment).index(or a trailing/index) collapses to the segment root.- A leading
/is added and trailing slashes trimmed.
Convention files are not routes
These names have special meaning and are skipped by route scanning:
layout.tsx _layout.tsx # layouts
loading.tsx # loading UI
error.tsx _error.tsx # error boundary
not-found.tsx # 404
route.tsx # non-page route moduleFiles containing .test. are skipped too. Everything else is a page.
Route priority and ordering
When several patterns could match a URL, the most specific wins. scanRoutes assigns a priority and
sorts:
0, static routes (/blog/new)1, dynamic routes (/blog/:slug)2, catch-all routes (/blog/*rest)
So /blog/new is matched by pages/blog/new.tsx even though pages/blog/[slug].tsx also matches the shape,
static beats dynamic beats catch-all. Within a priority, routes sort alphabetically for determinism.
Registering the scanned routes
scanRoutes returns objects, not components (it cannot import your modules for you). The idiom is to map
each pattern to its imported module:
Each scanned route carries:
| Field | Meaning |
|---|---|
pattern | The URL pattern (/blog/:slug). |
file / pageKey | The page file (the manifest key). |
dynamic | true if it has : or *. |
catchAll | true for *. |
priority | 0/1/2 as above. |
Why explicit mapping? rune does not auto-import your page files, that would couple the framework to a bundler convention. Listing modules keeps imports explicit and tree-shakeable, and lets you generate routes programmatically (the docs site generates one route per markdown page this way, see Programmatic routes).
Groups: organize without affecting URLs
Wrap files in a (name) folder to group them in your source tree without changing their URLs:
pages/
(marketing)/
index.tsx -> /
pricing.tsx -> /pricing
(app)/
dashboard.tsx -> /dashboardGroups are handy for applying different layouts to different sections (see Layouts).
Next: parameters and catch-alls, Dynamic routes.