Skip to content
Inertia

Installation

This guide takes you from an empty Kona app (or a brand-new kit) to a working Inertia page: HTML on the first visit, JSON on later visits, with a real Vue / React / Svelte client.

Client adapters, visit options, and form helpers are documented upstream at inertiajs.com. This site documents the Kona server side and how we wire the official clients.


Path When to use
A. Starter kit Greenfield app — everything scaffolded
B. ./grind install inertia Existing Kona app — dependency + interactive frontend
C. Manual You want every file written by hand

Paths A and B are the supported “no-brainer” routes. Path C is at the end.


Terminal window
kona new my-app --kit=inertia
# Java:
kona new my-app --kit=inertia --lang=java
cd my-app

The kit includes:

  • Maven dependency org.konaframework:kona-inertia
  • InertiaServiceProvider on the provider list
  • Config inertia.root_view=app, inertia.version=1
  • Crema root shell src/main/resources/views/app.crema.html
  • HandleInertia middleware pushed after boot
  • Welcome route that calls Inertia.render("Pages/Welcome", …)
  • Vue 3 Vite app (package.json, vite.config.js, js/app.js, js/Pages/Welcome.vue)
Terminal window
npm install
npm run dev # terminal 1 — Vite on :5173, writes public/hot
./grind serve # terminal 2 — Kona HTTP (default :4000)

Open http://127.0.0.1:4000/. You should see the Welcome page. In DevTools → Network, a full document load has no X-Inertia header; after client-side navigation (once you add links), requests include X-Inertia: true and receive JSON.

Until artifacts are on Maven Central for your version line, publish once:

Terminal window
# Kona checkout
./gradlew publishToMavenLocal -x test
# kona-inertia checkout
./gradlew :kona-inertia:publishToMavenLocal

Generated apps resolve org.konaframework from mavenLocal() then Central.


From the app root (directory with build.gradle.kts / build.gradle):

Terminal window
./grind install inertia
# or from anywhere under the app tree:
kona grind install inertia

That always:

  1. Adds implementation("org.konaframework:kona-inertia:…") to Gradle.
  2. Appends org.konaframework.inertia.InertiaServiceProvider to src/main/resources/META-INF/kona/providers.

Then it scaffolds the frontend (interactive when a TTY is available):

Which Inertia frontend stack should we scaffold?
[1] Vue 3 (@inertiajs/vue3)
[2] React (@inertiajs/react)
[3] Svelte (@inertiajs/svelte)
[4] Skip — I will wire the client myself

Non-interactive / CI defaults to Vue 3. Override anytime:

Terminal window
./grind install inertia --frontend=vue
./grind install inertia --frontend=react
./grind install inertia --frontend=svelte
./grind install inertia --frontend=none # server package only
./grind install inertia --frontend=vue --no-interactive
Flag Meaning
--frontend= vue, react, svelte, or none
--no-interactive Never prompt; use --frontend or default Vue
--version= Pin the Maven artifact version
--base= App root if grind’s cwd is wrong

The scaffold writes (when not none):

File Role
package.json Vite + official @inertiajs/* adapter
vite.config.js Build to public/build with manifest; dev origin for hot
src/main/resources/js/app.(js|jsx) createInertiaApp entry
src/main/resources/js/Pages/Welcome.* Sample page component
src/main/resources/views/app.crema.html Root HTML shell (only if missing)
public/.gitkeep Public dir for hot + build/

Refresh Gradle after install:

Terminal window
./gradlew build
# or restart ./grind serve

Set these before or while registering providers (kit apps already do):

.configure { cfg ->
cfg.set("inertia.root_view", "app") // Crema view name
cfg.set("inertia.version", "1") // asset version string
// optional:
// cfg.set("inertia.public_path", app.basePath.resolve("public").toString())
// cfg.set("inertia.root_html", "<!DOCTYPE html>…") // instead of a Crema view
}
Key Purpose
inertia.root_view Crema view that wraps {!! pageAttr !!} + {!! vite !!}
inertia.root_html Raw HTML string if you do not use Crema
inertia.version Compared on Inertia visits; mismatch → 409
inertia.public_path Directory containing Vite hot and build/ (default <base>/public)

Details: Configuration.

React note: the scaffold entry is app.jsx. Point Vite tags at it:

.afterBoot { app ->
Inertia.manager().viteEntries = listOf("src/main/resources/js/app.jsx")
}

(Default viteEntries is src/main/resources/js/app.js, which matches Vue/Svelte.)

InertiaServiceProvider must boot before you push middleware so Inertia.manager() exists.

.afterBoot { application ->
application.resolve(HttpKernel::class.java)
.pushMiddleware(HandleInertiaRequests())
// or your kit wrapper, e.g. HandleInertia()
}

Subclass HandleInertiaRequests to share flash/auth props or override version(request). See Middleware.

Crema view app (name must match inertia.root_view):

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title inertia>Kona</title>
{!! vite !!}
</head>
<body>
<div id="app" data-page="{!! pageAttr !!}"></div>
</body>
</html>
  • {!! vite !!} — script/link tags from the Vite hot file or production manifest (Vite & assets).
  • {!! pageAttr !!} — escaped JSON page object for the first visit.
  • #app — mount element expected by the official adapters.
router.get("/") {
Inertia.render(
"Pages/Welcome",
mapOf(
"title" to "Welcome",
"message" to "Inertia is running on Kona",
),
)
}

The string "Pages/Welcome" must match a file under js/Pages/ (e.g. Welcome.vue), as resolved by your createInertiaApp resolve function.

Terminal window
npm install
npm run dev # keep running — creates public/hot
./grind serve

Production build (no Vite dev server):

Terminal window
npm run build # writes public/build/manifest.json
./grind serve # serves hashed assets via {!! vite !!}
  1. First visit — GET / without X-Inertia → 200 HTML, #app[data-page] present, Vite client script tags present when public/hot exists.
  2. Inertia visit — same URL with headers:
GET / HTTP/1.1
X-Inertia: true
X-Inertia-Version: 1

→ 200 JSON { "component", "props", "url", "version" } plus X-Inertia: true.

  1. Version mismatch — send X-Inertia-Version: stale while server version is 1 → 409 so the client hard-reloads (Asset versioning).

  2. Browser — Welcome component renders title / message props.

If HTML has an empty body script section, Vite is not running and there is no production manifest yet — start npm run dev or run npm run build.


Piece Role
kona-inertia artifact Facade, manager, middleware, provider
InertiaServiceProvider Binds InertiaManager + Vite AssetManifest
HandleInertiaRequests Per-request share, version check, response headers
Root view / HTML First-visit document shell
Config keys Root view, version, public path

Package discovery loads providers listed in META-INF/kona/providers when the app boots through grind. See Kona’s Installing packages.


Kona does not re-implement the browser protocol. Use the official adapters:

Stack Packages Upstream
Vue 3 @inertiajs/vue3, vue, @vitejs/plugin-vue Vue setup
React @inertiajs/react, react, react-dom, @vitejs/plugin-react React setup
Svelte @inertiajs/svelte, svelte, @sveltejs/vite-plugin-svelte Svelte setup

Deeper client topics (forms, partial reloads, deferred props, when available in your adapter version) live on inertiajs.com. Kona’s job is correct HTML/JSON responses and shared props — see Rendering, Shared data, and Your first page.


dependencies {
implementation("org.konaframework:kona-http:0.1.0")
implementation("org.konaframework:kona-crema:0.1.0")
implementation("org.konaframework:kona-inertia:0.1.0")
}

Register InertiaServiceProvider, push middleware, add the root view, then either run ./grind install inertia --frontend=vue (reuses scaffold without failing if the dependency exists) or copy the Vite files from Path B’s table by hand.


Symptom Likely cause
InertiaException / no manager Provider not registered or middleware before boot
Empty {!! vite !!} No public/hot and no public/build/manifest.json
Blank page, console error “page not found” Component name ≠ file under js/Pages/
Always full HTML on XHR Missing HandleInertiaRequests or client not sending X-Inertia
Persistent 409 Client X-Inertia-Version ≠ server inertia.version
React assets 404 viteEntries still pointing at app.js instead of app.jsx