Crate axum_inertia

source ·
Expand description

An implementation of the inertia.js protocol for axum.

The basic idea is that any axum handler that accepts the Inertia struct as a function parameter is an inertia endpoint. For instance:

use axum_inertia::Inertia;
use axum::{Json, response::IntoResponse};
use serde_json::json;

async fn my_handler_fn(i: Inertia) -> impl IntoResponse {
    i.render("Pages/MyPageComponent", json!({"myPageProps": "true"}))
}

This does the following:

  • If the incoming request is the initial page load (i.e., does not have the X-Inertia header set to true), the render method responds with an html page, which is configurable when setting up the initial Inertia state (see Getting started below).

  • Otherwise, the handler responses with the standard inertia “Page” object json, with the included component and page props passed to render.

  • If the request has a mismatching asset version (again, this is configurable), the handler responds with a 409 Conflict to tell the client to reload the page. The function body of the handler is not executed in this case.

§Getting started

First, you’ll need to provide your axum routes with InertiaConfig state. This state boils down to two things: an optional string representing the asset version and a function that takes serialized props and returns an HTML string for the initial page load.

The vite module provides a convenient way to set up this state with axum::Router::with_state. For instance, the following code sets up a standard development server:

use axum_inertia::{vite, Inertia};
use axum::{Router, routing::get, response::IntoResponse};

// Configuration for Inertia when using `vite dev`:
let inertia = vite::Development::default()
    .port(5173)
    .main("src/main.ts")
    .lang("en")
    .title("My inertia app")
    .into_config();
let app: Router = Router::new()
    .route("/", get(get_root))
    .with_state(inertia);

The Inertia struct is then available as an axum Extractor and can be used in handlers like so:

use axum::response::IntoResponse;
use serde_json::json;

async fn get_root(i: Inertia) -> impl IntoResponse {
    i.render("Pages/Home", json!({ "posts": vec!["post one", "post two"] }))
}

The Inertia::render method takes care of building a response conforming to the inertia.js protocol. It takes two parameters: the name of the component to render, and the page props (serializable to json).

Using the extractor in a handler requires that you use axum::Router::with_state to initialize Inertia in your routes. In fact, it won’t compile if you don’t!

§Using InertiaConfig as substate

It’s likely you’ll want other pieces of state beyond InertiaConfig. You’ll just need to implement [axum::extract::FromRef] for your state type for InertiaConfig. For instance:

use axum_inertia::{vite, Inertia, InertiaConfig};
use axum::{Router, routing::get, extract::FromRef};

#[derive(Clone)]
struct AppState {
    inertia: InertiaConfig,
    name: String
}

impl FromRef<AppState> for InertiaConfig {
    fn from_ref(app_state: &AppState) -> InertiaConfig {
        app_state.inertia.clone()
    }
}

let inertia = vite::Development::default()
    .port(5173)
    .main("src/main.ts")
    .lang("en")
    .title("My inertia app")
    .into_config();
let app_state = AppState { inertia, name: "foo".to_string() };
let app: Router = Router::new()
    .route("/", get(get_root))
    .with_state(app_state);

§Configuring development and production

See the vite module for more information.

Re-exports§

Modules§

Structs§