Skip to content

Getting started

What is Keel

Host-owned routing. Packs implement page ids. The call site passes the pack.

Keel is a protocol and a small set of libraries for serving MPAs with a frameworked UI from Kotlin. The host owns URLs, page ids, and payload types. The call site passes which pack to render. A pack is a versioned frontend that implements those ids.

You always do the same two things: declare a contract on the host, then pass a pack that implements it into render.

Glue an app. Register pages, write one frontend with any supported adapter (Svelte, React, Vue, Solid, Preact, Lit, or Angular), and ship that pack. Keel is the router between Ktor and the UI (visits, seeds, forms) without a client-side router owning the URL.

Install themes. Keep the same registry. Publish the page ids and serializers as the contract. Authors provide packs typed for that data. The call site passes the pack it wants at render. Visitors never store a theme.

The first host is Ktor. Keel ships seven pack adapters. Svelte 5, React 18/19, Vue 3, Solid 1.9, Preact 10, Lit 3, and Angular 19; Svelte was the first. See Framework adapters for the matrix.

Walkthrough: serverclientKtor hostpackfirst page.

Where Keel sits

Inertia4J speaks the Inertia SPA protocol over Ktor or Spring. Kinetica and Kilua keep the UI in Kotlin (SSR, islands, route meta). Keel keeps routes, page ids, and the typed seed on the host, and ships the UI as swappable Vite packs (.feb) that implement those ids. Same server contract, different frontends, including the page’s document head.

The split

  • Keel, product, CLI, Maven group dev.kolektiv.keel
  • Pack, a versioned frontend (.feb zip + manifest) keyed by page id, not path
  • Adapter, mount / unmount / update for a framework (Svelte, React, Vue, Solid, Preact, Lit, Angular)

What the host does

  • Declares page<T>(id, path), id + payload type + path
  • Declares action<I, O>(id), typed writes, JSON in / JSON out
  • Passes the pack that implements that id into render (one pack, or one of several installed packs)
  • Renders a shell with #__keel_seed (and seed.head in the document) or answers a visit (X-Keel-Visit) with the same JSON

What a pack must not do

  • Own URL patterns
  • Fetch its own page payload as a second source of truth, actions are writes; reads stay the seed
  • Parse the seed outside bootstrap
  • Choose its own pack, or read a visitor theme preference from the browser
{
  "v": 1,
  "page": "home",
  "path": "/",
  "data": {
    "greeting": "Hello"
  },
  "theme": {
    "id": "harbor",
    "version": "1.0.0"
  },
  "entry": "/__keel/pack/harbor/pages/harbor.home.js",
  "host": "#__keel_root"
}

theme is the serving pack’s { id, version }; entry is the page module Keel loads from that pack.