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)
The dev loop
Day-to-day, you edit code and see the result. The recommended loop is a single command.
Start here: ekko dev
ekko dev watches the filesystem, rebuilds the client bundle when browser code changes, and pushes an HMR
reload to open tabs over a WebSocket. It replaces the build, run, and restart dance with one process. The
default entry is server.tsx.
By default it runs with full trust (it is a dev tool, so permissions are not enforced). Real enforcement
is ekko run's job in production; ekko dev is full trust unless you opt into enforcement.
To discover exactly which permissions your app needs before shipping, pass --allow. That flips ekko dev
into deny-by-default, granting only what you list, so a missing capability surfaces during development:
The mental model: two compilations
When you reach for the manual loop, it helps to know that rune has two distinct compilation paths: the
server code (transpiled and executed by the runtime as it runs) and the client bundle (built ahead
of time by ekko build --client).
| You changed... | What to do |
|---|---|
A page's server render or ssr() only | Restart ekko run server.tsx. |
| Anything that runs in the browser (component JSX, event handlers, client logic) | ekko build --client, then restart the server so it serves the new bundle. |
SCSS (styles/global.scss) | Restart the server, SCSS is compiled at startup. |
Markdown docs (content/docs-src/*.md) | ekko run _build/build-docs.ts --allow=fs to regenerate content/docs/docs.data.ts, then ekko build --client + restart. |
server.tsx, routes, config | Restart the server. |
Because the page component is rendered both on the server (for SSR) and in the browser (after hydration), most visible changes need the client rebuild and a restart.
Manual / CI fallback
When you are not watching (a CI build, a production image, or a one-off run), drive the two compilations
yourself instead of ekko dev:
This is the explicit build-then-run path, with real permission enforcement. Use it for non-watch and
production builds; use ekko dev for interactive development.
Live reload (HMR)
When the client bundle changes, rune can tell open browsers to reload. The server exposes a tiny WebSocket
at /__ekko_hmr and watches .ekko/build/client/__hmr (a token file the build updates). When the token
changes, connected pages receive a reload message and refresh. This gives you "save, rebuild, the tab
refreshes itself" without a manual F5, the page still does a full reload (it is not React Fast Refresh), but
it is automatic.
The flow:
- You run the build watcher (or re-run
ekko build --client). - The build writes a new token into
.ekko/build/client/__hmr. - The running server notices the token changed and pushes
reloadto every open tab.
If you prefer, ignore HMR and just restart, the result is identical.
A practical setup
For most work, one terminal running ekko dev is enough. If you prefer the manual loop, use two terminals:
For docs-heavy projects, regenerate the embedded docs data when you touch markdown:
What "it built" looks like
A successful client build prints the chunks it wrote and the manifest path:
Building client bundles (N entries + hydrate)...
→ 84 chunks to .ekko/build/client/
→ manifest: .ekko/build/manifest.jsonThe server, on start, prints the listening URL and (for eager SSR) how many pages it pre-rendered:
SSR cache: 48 pages rendered (eager)
EkkoJS SSR listening on http://0.0.0.0:3000Ports
The example apps read a PORT environment variable (requires --allow=env) and fall back to 3000:
Run two rune apps side by side by giving them different ports:
You now have the loop. Next, build something real end to end in the Tutorial, or jump to Core Concepts for the model behind it all.