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)
Conventions
rune leans on a handful of naming and structural conventions. Learn these once and the framework stops needing configuration, the filesystem expresses your intent.
File-name conventions in pages/
scanRoutes treats most files as routes, but reserves a set of convention file names that are not
routes:
layout.tsx _layout.tsx # a layout (wraps its segment's pages)
loading.tsx # a loading UI for the segment
error.tsx _error.tsx # an error boundary for the segment
not-found.tsx # the 404 page
route.tsx # a route handler module (non-page)Any other .tsx/.jsx/.ts/.js file is a page. Files containing .test. are skipped.
Route-name conventions
The path of a page file becomes its URL via these rules (full detail in File-based routing):
| On disk | URL |
|---|---|
pages/index.tsx | / |
pages/about.tsx | /about |
pages/blog/index.tsx | /blog |
pages/blog/[slug].tsx | /blog/:slug |
pages/docs/[...path].tsx | /docs/*path (catch-all) |
pages/(marketing)/pricing.tsx | /pricing (the (group) folder is stripped) |
Routes are sorted so static beats dynamic beats catch-all (/blog/new wins over /blog/:slug).
Folder conventions
| Folder | Convention |
|---|---|
pages/ | Routes + convention files. |
components/ | Non-route UI. |
atoms/ | Mimir atoms (one module per concern). |
lib/ | Plain modules: site.ts (brand/config), theme.ts (Asgard theme objects), helpers. |
styles/ | global.scss, compiled at server start. |
static/ | Verbatim assets, served at staticPrefix (commonly /assets). |
content/ | Data sources; e.g. content/docs-src/*.md → generated content/docs/docs.data.ts. |
.ekko/build/ | Generated client bundle + manifest. |
Page-module conventions
A page module exports:
default, the React component (required to render anything).ssr()(optional) , a server-only function returning{ title?, head?, __atoms? }. Its presence is
what makes a static route get a full cached SSR render rather than a shell.
ssr() runs on the SERVER only , keep server modules out of the client
A page module is bundled for both the server (SSR) and the browser (hydration). ssr() runs only on
the server, but anything you import at the top of the file is bundled into the client too. So if ssr()
imports a server-only module , ekko:db / ekko:db/orm, ekko:fs, ekko:crypto, or your own lib/db
that connects to a database , that import leaks into the browser bundle, where it cannot load. The page
server-renders fine but then fails to hydrate (the browser rejects the chunk with a CORS / "module script
is text/html" error), so it looks rendered but is dead: no theme toggle, no filters, no form submits.
Wrap ssr() and its server-only imports in /* START SSR */ ... /* END SSR */. The client build strips
everything between those markers, so the server module never reaches the browser:
The build errors if a server-only ekko:* module reaches the client bundle (telling you the module and
the fix), so you cannot ship a silently-broken page. The alternative to seeding via ssr() is to fetch the
data from an API route (app.api(...)) after hydration. Either way, the browser never imports a
server module.
Atom conventions
- One atom per concern, with a globally unique
keystring. - Keep keys stable, the key is the persistence and hydration identifier; renaming it orphans saved state.
- Co-locate atoms in
atoms/and import them where used.
Navigation convention
Use the router for in-app links so navigation stays client-side:
The "two declarations" theming convention
Brand colors live in two places that must stay in sync: the SCSS custom properties in
styles/global.scss (:root light + .dark) and the @ekko/asgard theme objects in lib/theme.ts. The
single source of truth for which theme is active is a Mimir atom. See Theming.
That completes Core Concepts. From here, dive into an area: Routing, Server-Side Rendering, or the big one, Mimir. Or build an app end to end in the Tutorial.