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)
A Rune app from scratch
For a real project, start with
ekko init runeinstead. This page builds an app by hand to show how all the pieces fit together, it is for understanding, not the recommended way to start. The scaffold writes the same correct structure for you.
This is one small, complete Rune app you can copy verbatim. It has two pages, client navigation, a Mimir atom, an Asgard form that POSTs to a same-process API route, and themable chrome. Every snippet uses the correct patterns, so it runs as written.
If anything here looks unfamiliar, see What's different in Ekko: ESM
only, ekko: prefixes, extensionless code imports, value-not-event input handlers, and a raw-string
req.body.
The file layout
my-app/
ekko.json
server.tsx
atoms/
greeting.ts
pages/
layout.tsx
index.tsx
about.tsx
styles/
global.scss1. ekko.json
Type run, with only the valid permission keys. This app reads files (fs), listens and fetches
(net), and hashes a value (crypto).
The valid permission categories are exactly fs, net, crypto, process, env, ffi, and all. Any
other key warns and grants nothing. See Permissions.
2. server.tsx, the entry point
It registers the renderer, compiles SCSS at startup, maps page modules to routes, declares one API route
using await req.json(), and starts listening.
Imports have no file extension:
./pages/index, not./pages/index.tsx. Ekko owns module resolution and rejects extensioned code specifiers at run. See Why extensionless?.
3. A Mimir atom
State that survives navigation lives in an atom, not useState.
4. The layout
The shell wraps every page. It hosts the Asgard ThemeProvider, and <ThemeCssVars/> mirrors the active
theme onto :root as CSS variables so your own SCSS chrome themes too.
<ThemeProvider> themes Asgard components; <ThemeCssVars/> exposes the same theme tokens as CSS variables
(for example var(--ekko-background-primary)) so your markup matches. See
Asgard integration.
5. The home page, an Asgard form
The page reads and writes the atom, renders an Asgard TextBox and Button, and POSTs to the API. Note two
intentional patterns: the input onChange receives the value (not an event), and the submit button uses
htmlType="submit".
The greeting is stored in greetingAtom, so it is still there when you navigate to About and back.
6. The about page
A plain page that reads the atom set on the home page, proof the state survived navigation.
7. The styles
global.scss is compiled at server start. It can lean on the CSS variables <ThemeCssVars/> publishes.
8. Run it
Lead with ekko dev. It watches files, rebuilds the client bundle, and HMR-reloads the browser:
For a non-watch or production run, build the client once then run with explicit permissions:
Open http://localhost:3000, type a name, and submit. The greeting comes back from the same-process API
route, lands in a Mimir atom, and survives the trip to About and back. See The dev loop.
Where to go next
- Forms, drafts in atoms, submission state, and a no-JS fallback.
- Authentication, sessions,
req.user, and thecryptogrant in
practice.
- The tutorial builds a larger app step by step.