{"id":21810508,"url":"https://github.com/kaiofelps/inertia-rust","last_synced_at":"2026-02-22T19:18:39.312Z","repository":{"id":256514589,"uuid":"854937769","full_name":"KaioFelps/inertia-rust","owner":"KaioFelps","description":"An Inertia.js server-side adapter for Rust applications.","archived":false,"fork":false,"pushed_at":"2025-11-01T02:25:00.000Z","size":481,"stargazers_count":26,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"v2","last_synced_at":"2026-01-08T01:20:49.111Z","etag":null,"topics":["actix-web","inertia","inertiajs","inertiajs-adapter","react","rust"],"latest_commit_sha":null,"homepage":"https://kaiofelps.github.io/inertia-rust/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KaioFelps.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-09-10T02:48:09.000Z","updated_at":"2025-12-26T16:43:21.000Z","dependencies_parsed_at":"2025-10-22T14:36:08.773Z","dependency_job_id":"7aafd133-bec0-4a3d-86b3-c126615be138","html_url":"https://github.com/KaioFelps/inertia-rust","commit_stats":null,"previous_names":["kaiofelps/inertia-rust"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/KaioFelps/inertia-rust","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KaioFelps%2Finertia-rust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KaioFelps%2Finertia-rust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KaioFelps%2Finertia-rust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KaioFelps%2Finertia-rust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KaioFelps","download_url":"https://codeload.github.com/KaioFelps/inertia-rust/tar.gz/refs/heads/v2","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KaioFelps%2Finertia-rust/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29723916,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-22T19:15:09.475Z","status":"ssl_error","status_checked_at":"2026-02-22T19:15:09.045Z","response_time":110,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actix-web","inertia","inertiajs","inertiajs-adapter","react","rust"],"created_at":"2024-11-27T13:36:09.659Z","updated_at":"2026-02-22T19:18:39.271Z","avatar_url":"https://github.com/KaioFelps.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Inertia Rust\n\nA server-side Inertia.js adapter for Rust. Inertia Rust aims to interoperate with any\nRust (micro-)framework and template engine, since a compatible provider exists.\n\nDue to its flexibility, it requires a bit more configuration. Hence, please read\nthis document carefully to ensure that your Inertia application works correctly.\n\n## Getting started\n\n```toml\n[dependencies]\ninertia_rust = { version = \"0.1\", features = [\"default\", \"basic-vite-resolver\"] }\nactix-web = \"4\"\nvite-rust = { version = \"0.2\", features = [\"basic-directives\"] }\n```\n\nTo get Inertia working, you'll need to ensure some peer dependencies are installed.\nCurrently, inertia_rust is still under development and is working to support Actix Web.\nTherefore, ensure you have actix_web on your dependency section and \"default\" or \"actix\"\nfeature is enabled at inertia_rust dependency properties.\n\n\"basic-vite-resolver\" feature enables few basic `vite-rust` directives. Currently, we\nstill do not have default support for template engines, even though you can easily set\nit up by yourself.\n\nThe basic vite resolver is a `template-resolver` function that uses (and thus require you\nto install) `vite-rust` basic directives (also must be enabled manually at features property)\nto inject Vite's tags and Inertia's body and head into your HTML template.\n\n### Creating your own template resolver\nA template resolver function must be provided during Inertia setup. The basic vite resolver\nmight fit for most usages. If you need something more specific, you will need to create\ntwo functions: one to actually resolve the template and a wrapper function, so that the resolver\ncan be stored inside Inertia structure.\n\n```rust\nuse inertia_rust::{InertiaError, TemplateResolverOutput, ViewData};\n\n// the actual resolver\nasync fn resolver(\n    path: \u0026str,\n    view_data: ViewData,\n    some_useful_prop: \u0026SomeUsefulStruct\n) -\u003e Result\u003cString, InertiaError\u003e {\n    /* ... */\n}\n\n// a function that wraps the resolver\npub fn template_resolver(\n    template_path: \u0026'static str,\n    view_data: ViewData,\n    prop: \u0026'static SomeUsefulStruct\n) -\u003e TemplateResolverOutput {\n    Box::pin(resolver(template_path, view_data, prop))\n}\n```\n\nYou might have noted that the third parameter is a reference to `SomeUsefulStruct`. This must be\nsome useful struct used by your resolver. For instance, our basic vite resolver requires a\nstatic reference to a `vite_rust::Vite` struct, because it's what provides the HTML tags of the modules,\nHMR and other important stuff that must be injected into the HTML.\n\nThis struct will also be stored by static reference inside Inertia struct, and Inertia is the one who will\ncall the resolver method when rendering your HTTP response.\n\nIf you don't need any extern struct, you can simply pass a `\u0026'static ()` on Inertia's `template_resolver_data`\nfield. Note that, *Inertia\u003cT\u003e* requires *template_resolver*'s third parameter to be of type *T* either.\n\n### Inertia setup\n\nFor this guide, I'll consider you're using `vite-rust` and `actix-web`, with the above Cargo.toml dependencies.\nInside your `main.rs`, you'll have to:\n\n1. Declare Vite as a static constant;\n2. Initialize Vite;\n3. Initialize Inertia with a static reference to your Vite instance.\n\n```rust\nuse actix_web::web::Data;\nuse actix_web::{App, HttpServer};\nuse inertia_rust::resolvers::basic_vite_resolver;\nuse inertia_rust::{Inertia, InertiaConfig, InertiaVersion};\nuse std::sync::OnceLock;\nuse vite_rust::{Vite, ViteConfig};\n\nstatic VITE: OnceLock\u003cVite\u003e = OnceLock::new();\n\n#[actix_web::main]\nasync fn main() -\u003e std::io::Result\u003c()\u003e {\n    // initializes Vite\n    let vite = match Vite::new(ViteConfig::new_with_defaults(\"public/bundle/manifest.json\")).await {\n        Ok(vite) =\u003e vite,\n        Err(err) =\u003e panic!(\"{}\", err),\n    };\n\n    let vite = VITE.get_or_init(move || vite);\n\n    let inertia_config: InertiaConfig\u003cVite, String\u003e = InertiaConfig::builder()\n        .set_url(\"http://localhost:8080\")\n        // InertiaVersion::Literal(vite.get_hash()), or\n        .set_version(InertiaVersion::Literal(vite.get_hash().to_string()))\n        .set_template_path(\"path/to/your/template.html\")\n        .set_template_resolver(\u0026basic_vite_resolver)\n        .set_template_resolver_data(vite)\n        .build();\n\n    // initializes Inertia struct\n    let inertia = Inertia::new(inertia_config)?;\n\n    // stores Inertia as an AppData in a way that is not cloned for each worker\n    let inertia = Data::new(inertia);\n    let inertia_clone = Data::clone(\u0026inertia);\n\n    HttpServer::new(move || App::new().app_data(inertia_clone.clone()))\n        .bind((\"127.0.0.1\", 8080))?\n        .run()\n        .await\n}\n```\n\n#### Server-side rendering\n\nIf you have Node.js available in the machine your Rust application is running at, you can enable\n**server-side rendering**. For this, you'll need to do some few changes in your code:\n\n```rust\nuse actix_web::web::Data;\nuse actix_web::{App, HttpServer};\nuse inertia_rust::resolvers::basic_vite_resolver;\nuse inertia_rust::{Inertia, InertiaConfig, InertiaVersion, SsrClient};\nuse std::sync::OnceLock;\nuse vite_rust::{Vite, ViteConfig};\n\nstatic VITE: OnceLock\u003cVite\u003e = OnceLock::new();\n\n#[actix_web::main]\nasync fn main() -\u003e std::io::Result\u003c()\u003e {\n    // initializes Vite\n    let vite = match Vite::new(ViteConfig::new_with_defaults(\"public/bundle/manifest.json\")).await {\n        Ok(vite) =\u003e vite,\n        Err(err) =\u003e panic!(\"{}\", err),\n    };\n\n    let vite = VITE.get_or_init(move || vite);\n\n    let inertia_config: InertiaConfig\u003cVite, String\u003e = InertiaConfig::builder()\n        .set_url(\"http://localhost:8080\")\n        // InertiaVersion::Literal(vite.get_hash()), or\n        .set_version(InertiaVersion::Literal(vite.get_hash().to_string()))\n        .set_template_path(\"path/to/your/template.html\")\n        .set_template_resolver(\u0026basic_vite_resolver)\n        .set_template_resolver_data(vite)\n        .enable_ssr()\n        // `set_ssr_client` is optional. If not set, `SsrClient::default()` will be used.\n        .set_ssr_client(SsrClient::new(\"127.0.0.1\", 1000))\n        .build();\n\n    // initializes Inertia struct\n    let inertia = Inertia::new(inertia_config)?;\n\n    // stores Inertia as an AppData in a way that is not cloned\n    let inertia = Data::new(inertia);\n    let inertia_clone = Data::clone(\u0026inertia);\n\n    let server = HttpServer::new(move || App::new().app_data(inertia_clone.clone()))\n        .bind((\"127.0.0.1\", 8080))?;\n\n    // Starts a Node.js child process that runs the Inertia's server-side-rendering server.\n    // It must be started after the server initialization to ensure that the server won't panic and\n    // shutdown without killing Node process.\n    let node = inertia.start_node_server(\"path/to/your/ssr.js\".into())?;\n\n    let server = server.run().await;\n    let _ = node.kill().await;\n\n    return server;\n}\n```\n\n## Page rendering and Responses\nThere are a few couple ways of rendering an Inertia page. Every provider will aim to give you\nas many facilities as possible.\n\nThe following application renders a component \"Index\" without any props at \"/\" endpoint.\n```rust\nuse actix_web::{get, App, HttpRequest, HttpServer, Responder};\nuse inertia_rust::{actix::render, InertiaErrMapper};\n\n#[get(\"/\")]\nasync fn index(req: HttpRequest) -\u003e impl Responder {\n    render::\u003cVite\u003e(\u0026req, \"Index\".into()).await\n}\n\n#[actix_web::main]\nasync fn main() -\u003e std::io::Result\u003c()\u003e {\n    // ...\n    \n    HttpServer::new(move || {\n        App::new()\n            .app_data(inertia_clone.clone())\n            .service(index)\n    })\n    .bind((\"127.0.0.1\", 8080))?\n    .run()\n    .await\n}\n```\n\nThe very same thing could be done in the following way:\n```rust\nuse actix_web::{App, HttpServer};\nuse inertia_rust::InertiaService;\n\n#[actix_web::main]\nasync fn main() -\u003e std::io::Result\u003c()\u003e {\n    // ...\n    \n    HttpServer::new(move || {\n        App::new()\n            .app_data(inertia_clone.clone())\n            .inertia_route::\u003cVite\u003e(\"/\", \"Index\")\n    })\n    .bind((\"127.0.0.1\", 8080))?\n    .run()\n    .await\n}\n```\n\nHowever, it is not possible to render pages with props using `inertia_route` method. It must\nbe done with an ordinary handler function and with `render_with_props` helper:\n\n```rust\nuse actix_web::{get, App, HttpRequest, HttpServer, Responder};\nuse inertia_rust::{actix::render_with_props, InertiaErrMapper, InertiaProp};\nuse std::collections::HashMap;\n\n#[get(\"/\")]\nasync fn index(req: HttpRequest) -\u003e impl Responder {\n    let mut props = HashMap::new();\n    props.insert(\n        \"message\".to_string(),\n        InertiaProp::Always(\"Hello world!\".into()),\n    );\n\n    render_with_props::\u003cVite\u003e(\u0026req, \"Index\".into(), props).await\n}\n\n// ...\n```\n\nAn `InertiaProp` is an enum that can hold a `serde_json::Value` or a callback that returns one of it.\nA hash map of InertiaProp elements is an `InertiaProps` set, and it's resolved during rendering (when\nthe needed props are evaluated).\n\n## Inertia Middleware and Shared Props\n\nThe Inertia Middleware comes from your opted provider. It has few responsibilities:\n- allow you to **share props**, via `with_shared_props` method;\n- ensure that redirects for PUT, PATCH and DELETE requests always use a 303 status code;\n- merge shared props with errors flash messages.\n\nThe middleware's `with_shared_props` method requires a callback, wrapped in an `Arc`, that\nreceives a reference to the current request. You can use it to extract any information you might want\nto share.\n\n```rust\nuse actix_web::{App, HttpServer};\nuse inertia_rust::actix::InertiaMiddleware;\nuse inertia_rust::{InertiaProp, InertiaProps, InertiaService};\n\n#[actix_web::main]\nasync fn main() -\u003e std::io::Result\u003c()\u003e {\n    // ...\n\n    HttpServer::new(move || {\n        App::new()\n            .app_data(inertia_clone.clone())\n            .wrap(InertiaMiddleware::new().with_shared_props(Arc::new(|req| {\n                let mut props: InertiaProps = HashMap::\u003cString, InertiaProp\u003e::new();\n\n                // get the sessions from the request\n                // depending on your opted framework\n                let session = req.get_session();\n                let flash = serde_json::to_value(session.get::\u003cString\u003e(\"flash\").unwrap()).unwrap();\n\n                props.insert(\"flash\".to_string(), InertiaProp::Always(flash));\n                props\n            })))\n            .inertia_route::\u003cVite\u003e(\"/\", \"Index\")\n    })\n    .bind((\"127.0.0.1\", 8080))?\n    .run()\n    .await\n}\n```\n\nIt will also flash errors into the shared props. These props will be injected back into the request\nand will be further merged again with the page props during rendering, thus making all of them\navailable to your client-side page component.\n\nAs inertia-rust is not made for one single framework and any of them actually have built-in sessions\nmanagement, you need to built by yourself a second middleware that injects in the request\ncontext/extensions an `InertiaTemporarySession` object:\n\n```rust\n#[derive(Clone, Serialize)]\npub struct InertiaTemporarySession {\n    // Optional errors hashmap\n    pub errors: Option\u003cMap\u003cString, Value\u003e\u003e,\n    // The previous request URL\n    // useful for redirecting back with errors\n    pub prev_req_url: String,\n}\n```\nInertia Middleware tries to extract this from the request context and merge it with the shared\nprops. This is how validation errors get available to your page components.\n\nCheck a sample middleware that extracts errors from the session and add to extensions:\n```rust\nuse inertia_rust::{InertiaTemporarySession, InertiaMiddleware};\nuse actix_session::{SessionExt, SessionMiddleware};\nuse actix_web::{dev::Service, App, HttpMessage, HttpServer};\nuse serde_json::{Map, Value};\n\n#[actix_web::main]\nasync fn main() -\u003e std::io::Result\u003c()\u003e {\n    HttpServer::new(move || {\n        App::new()\n            .wrap_fn(|req, service| {\n                const PREV_REQ_KEY: \u0026str = \"_prev_req_url\";\n                const CURR_REQ_KEY: \u0026str = \"_curr_req_url\";\n                \n                let session = req.get_session();\n            \n                let errors = session\n                    .remove(\"_errors\")\n                    .map(|errors| serde_json::from_str(\u0026errors).unwrap());\n            \n                let error = session\n                    .remove(\"error\")\n                    .map(|error| serde_json::from_str(\u0026error).unwrap());\n            \n                // gets the previous request's URI and stores the current one's,\n                // so that it becomes the previous request URI of the next request.\n                // ---\n                \n                let prev_url = session\n                    .get::\u003cString\u003e(CURR_REQ_KEY)\n                    .unwrap_or(None)\n                    .unwrap_or(\"/\".to_string());\n            \n                if let Err(err) = session.insert(PREV_REQ_KEY, \u0026prev_url) {\n                    eprintln!(\"Failed to update session previous request URL: {}\", err);\n                };\n            \n                if let Err(err) = session.insert(CURR_REQ_KEY, req.uri().to_string()) {\n                    eprintln!(\"Failed to update session current request URL: {}\", err);\n                };\n                \n                // ---\n            \n                let temporary_session = InertiaTemporarySession {\n                    errors,\n                    error,\n                    prev_req_url: prev_url,\n                };\n            \n                req.extensions_mut().insert(temporary_session);\n            \n                let fut = service.call(req);\n                async {\n                    let res = fut.await?;\n                    Ok(res)\n                }\n            })\n            .wrap(SessionMiddleware::new(...))\n            .wrap(InertiaMiddleware::new())\n            .inertia_route::\u003cVite\u003e(\"/\", \"Index\")\n    })\n    .bind((\"127.0.0.1\", 8080))?\n    .run()\n    .await\n}\n```\n\nYet you need to enable your framework session middleware and manager (or your own). As errors\nare retrieved by `remove` method, they are only available for one request lifetime. Errors and\nflash messages shouldn't persist across multiple requests.\n\n---\n\n\u003e [!WARNING]\n\u003e This is in the very first stages of development. A list of functionalities to be\n\u003e implemented and what have been so far is available in the\n\u003e \u003ca href=\"./REQUIREMENTS.md\"\u003eREQUIREMENTS\u003c/a\u003e file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaiofelps%2Finertia-rust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkaiofelps%2Finertia-rust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaiofelps%2Finertia-rust/lists"}