crystal-inertia v0.1.1

Integrates Crystal with Inertia.js without frameworks

crystal-inertia

Inertia.js integration for Crystal (experimental)

This shard simplifies the integration of Inertia.js with Crystal applications, encouraging convention over configuration and without dependencies on external libraries or frameworks.

[!WARNING] This project is experimental and not all the features defined by Inertia.js protocol have been implemented.

Sharing this for others to explore and perhaps continue to develop it.

Features

  • Not tied to any framework, just plain HTTP::Server::Context.
  • Flexible templating rendering options, allowing to use any library, like ECR.

Usage

Setup your own renderer

For the non-Inertia requests, is necessary to render an HTML response. You can implement your own by including Inertia::Renderer module and implementing the render method:

class MyRenderer
  include Inertia::Renderer

  def render(inertia : Inertia, context : HTTP::Server::Context, locals)
    # Extract the page data (already JSON-encoded)
    page_data = HTML.escape(locals[:page])

    # Set content type and write HTML response
    context.response.content_type = "text/html"
    context.response << <<-HTML
      <!DOCTYPE html>
      <html>
        <head>
          <title>My App</title>
          <script src="/assets/app.js"></script>
        </head>
        <body>
          <div id="app" data-page='#{page_data}'></div>
        </body>
      </html>
    HTML
  end
end

Now you can use this renderer in your Inertia instance:

inertia = Inertia.new(version: "1.0.0", renderer: MyRenderer.new)

Connect Inertia to your HTTP Server

Inertia.js requires that certain types of requests are handled differently than normal applications. Eg. Redirection after PUT, PATCH or DELETE requires 303 (See Other) instead of regular 301 or 302 status codes.

In order to do that, this project provides an HTTP handler that can be added to your server middleware stack:

inertia = Inertia.new(version: "1.0.0", renderer: MyRenderer.new)

server = HTTP::Server.new([
  HTTP::ErrorHandler.new,
  HTTP::LogHandler.new,
  inertia.http_handler,
])
server.bind_tcp("0.0.0.0", 3000)
server.listen

Note that this handler must be before your application handler, so it can process the responses sent by it.

A minimal example

The following example shows a naive implementation of the routing for an application:

inertia = Inertia.new(version: "1.0.0", renderer: MyRenderer.new)

handlers = [
  HTTP::LogHandler.new,
  # to serve files from `public` directory
  HTTP::StaticFileHandler.new("public", directory_listing: false),
  inertia.http_handler,
]

server = HTTP::Server.new(handlers) do |context|
  case context.request.path
    when "/"
      next inertia.render(
        context,
        component: "Home",
        props: { title: "Home" }
      )
    when "/about"
      next inertia.render(
        context,
        component: "About",
        props: {
          title: "About",
          user: {
            id: 1,
            name: "John Doe",
            email: "john@example.com"
          }
        }
      )
  end
end

server.bind_tcp("0.0.0.0", 8080)
server.listen

It now remains for you to implement in the frontend stack of your choice (React, Vue, Svelte), the necessary logic. Refer to Inertia.js' client-side documentation for more information.

Missing features

A lot:

If you're interested in implementing one of those features, please open an issue to discuss it, so others see it to either collaborate with you or to avoid duplication of effort.

License

Licensed under the Apache License, Version 2.0. You may obtain a copy of the license here.

Repository

crystal-inertia

Owner
Statistic
  • 0
  • 0
  • 0
  • 1
  • 0
  • 3 days ago
  • May 29, 2025
License

Apache License 2.0

Links
Synced at

Tue, 03 Jun 2025 16:17:17 GMT

Languages