kemal-inertia v0.2.0
Kemal + Inertia
Inertia.js v3 adapter for Kemal written in Crystal.
This shard allows you to use Inertia.js with Kemal, enabling modern SPA-like applications using Vue, React, or Svelte without building a separate API.
Installation
-
Add the dependency to your
shard.yml:dependencies: kemal: github: kemalcr/kemal kemal-inertia: github: erayjsx/kemal-inertia -
Run
shards install
Basic Setup
require "kemal"
require "kemal-inertia"
Kemal::Inertia.configure do |config|
config.version = "1.0"
config.html_handler = ->(env : HTTP::Server::Context, page : String) {
render "src/views/layout.ecr"
}
end
add_handler Kemal::Inertia::Middleware.new
get "/" do |env|
Kemal::Inertia.render(env, "home", name: "Kemal")
end
Kemal.run
Your layout.ecr should use the v3 script tag format:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<title>My App</title>
</head>
<body>
<div id="app"></div>
<script id="app" type="application/json"><%= page %></script>
</body>
</html>
Rendering
Simple Render
get "/users" do |env|
users = [
{"id" => 1, "name" => "Alice"},
{"id" => 2, "name" => "Bob"},
]
Kemal::Inertia.render(env, "users/index", users: users, count: users.size)
end
Using a Block
get "/dashboard" do |env|
Kemal::Inertia.render(env, "dashboard") do
{
"stats" => {"users" => 120, "sales" => 42},
"last_updated" => Time.local.to_s,
}
end
end
Shared Data
Shared props are automatically included in every response. Their keys are also listed in the sharedProps field of the page object (v3).
Kemal::Inertia.share("auth") do |env|
if id = env.session.int?("user_id")
{"user" => {"id" => id, "name" => "Current User"}}
else
nil
end
end
Deferred Props
Deferred props are excluded from the initial page load and fetched in a separate request afterwards. You can group them to control parallel fetching.
Use Kemal::Inertia.optional (v3) or Kemal::Inertia.defer:
get "/users" do |env|
Kemal::Inertia.render(env, "users/index",
users: User.all,
permissions: Kemal::Inertia.optional { Permission.all },
teams: Kemal::Inertia.optional("sidebar") { Team.all },
projects: Kemal::Inertia.optional("sidebar") { Project.all },
)
end
Partial Reloads
If a request includes X-Inertia-Partial-Data or X-Inertia-Partial-Except headers, only the requested (or non-excluded) props will be returned.
get "/users" do |env|
Kemal::Inertia.render(env, "users/index",
users: User.all,
filters: params,
)
end
Validation Errors
post "/users" do |env|
errors = {} of String => Array(String)
errors["email"] = ["is required"] if email.empty?
unless errors.empty?
Kemal::Inertia.validation_error(env, errors)
next
end
Kemal::Inertia.redirect(env, "/users")
end
Redirects
Kemal::Inertia.redirect(env, "/dashboard")
SSR
Kemal::Inertia.configure do |config|
config.ssr_enabled = true
config.ssr_url = "http://localhost:13714"
end
Examples
Check the examples/ folder for full working examples:
- React (Vite + React 19 + @inertiajs/react v3)
- Vue (Vite + Vue 3 + @inertiajs/vue3 v3)
- Svelte (Vite + Svelte 5 + @inertiajs/svelte v3)
Contributing
- Fork it (https://github.com/erayjsx/kemal-inertia/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
kemal-inertia
- 2
- 1
- 0
- 0
- 1
- about 5 hours ago
- February 2, 2026
Sat, 18 Apr 2026 22:10:27 GMT