### Actix Web Server Setup Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/installation.md Initializes the Inertia manager and sets up an Actix Web server to serve Inertia requests. Ensure dotenv and env_logger are initialized. ```rust // src/main.rs use std::sync::{Arc, OnceLock}; use actix_web::{dev::Path, web::Data, App, HttpServer}; use config::inertia::initialize_inertia; mod config; #[actix_web::main] async fn main() -> std::io::Result<()> { dotenvy::dotenv().ok(); env_logger::init(); // starts a Inertia manager instance. let inertia = initialize_inertia().await?; let inertia = Data::new(inertia); HttpServer::new(move || App::new().app_data(inertia.clone())) .bind(("127.0.0.1", 3000))? .run() .await } ``` -------------------------------- ### Rendering with Props using HashMap Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/props.md Demonstrates how to create a HashMap of props and render a view with them using `Inertia::render_with_props`. This example defines a `User` struct and fetches a list of users to pass as a prop. ```rust use inertia_rust::{hashmap, Inertia, InertiaFacade, InertiaProp}; use actix_web::{get, web, HttpRequest, Responder}; use serde_json::{json}; use serde::Serialize; #[derive(Serialize)] struct User { pub name: String, pub email: String, } impl User { // let's just pretend this is a method that fetch users from some database pub async fn all() -> Vec { vec![ User { name: "John Doe".into(), email: "johndoe@gmail.com".into() }, User { name: "Ada Lovelace".into(), email: "thereWereNoEmailByThatTimeActually!@gmail.com".into(), } ] } } #[get("/users")] async fn home(req: HttpRequest) -> impl Responder { let props = hashmap![ "users" => InertiaProp::data(User::all().await), ]; Inertia::render_with_props(&req, "Users/Index".into(), props).await } ``` -------------------------------- ### React Component for Users Page Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/responses.md A React component example for the 'Users/Index' Inertia page. It includes a Head component for the title and maps over a list of users to display their details. ```jsx import { Head } from "@inertiajs/react"; type User = { name: string; email: string; } type UsersPageProps = { users: User[]; } export default function UsersPage({ user }: UsersPageProps) { return ( <>

Users

{users.map(user => (
Name: {user.name} E-mail: {user.email}
))}
) } ``` -------------------------------- ### Root Template for ViteHBSTemplateResolver Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/installation.md Example of a root HTML template using ViteHBSTemplateResolver. Includes placeholders for Vite assets, React refresh, and Inertia head/body content. Conditional inclusion for React refresh is noted. ```html {{!-- only include this line if you're using react --}} {{{ vite_react_refresh }}} {{{ vite }}} {{{ inertia_head }}} {{{ inertia_body }}} ``` -------------------------------- ### Integrate Node.js SSR Server in Main Application Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/ssr.md Start the Node.js SSR server as a child process after the main HTTP server is initialized. Ensure the Node.js process is killed upon application shutdown. ```diff // src/main.rs use std::sync::{Arc, OnceLock}; use actix_web::{dev::Path, web::Data, App, HttpServer}; use config::inertia::initialize_inertia; mod config; #[actix_web::main] async fn main() -> std::io::Result<()> { dotenvy::dotenv().ok(); env_logger::init(); // starts a Inertia manager instance. let inertia = initialize_inertia().await?; let inertia = Data::new(inertia); + let inertia_clone = inertia.clone(); - HttpServer::new(move || App::new().app_data(inertia.clone())) - .bind(("127.0.0.1", 3000))? - .run() - .await + + let server = HttpServer::new(move || App::new().app_data(inertia_clone.clone())) + .bind(("127.0.0.1", 3000))?; + + // Starts a Node.js child process that runs the Inertia's server-side rendering server. + // It must be started after the server initialization to ensure that the http server won't + // panic and shutdown without killing the Node.Js process. + let node = inertia.start_node_server("path/to/your/ssr.js".into())?; + + let server = server.run().await; + let _ = node.kill().await;+ + + return server; } ``` -------------------------------- ### Redirecting Back with Validation Errors Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/advanced/flash-messages.md Example of how to redirect back to the previous page with specific validation errors using Inertia Rust. Ensure your session middleware is configured to handle and persist these errors. ```rust use actix_web::{get, HttpRequest, Responder}; use inertia_rust::{hashmap, Inertia, InertiaFacade}; #[get("/foo")] async fn foo(req: HttpRequest) -> impl Responder { Inertia::back_with_errors(&req, hashmap![ "age" => to_value("You must be over 13 y.o. to access this website").unwrap() ]) } ``` -------------------------------- ### Access Root Template Data in HTML Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/responses.md Example of accessing root template data passed via `view_data` within an HTML file using the deprecated `ViteTemplateResolver`. ```html ``` -------------------------------- ### Root Template for Deprecated ViteTemplateResolver Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/installation.md Example of a root HTML template for the deprecated ViteTemplateResolver. Uses '@' syntax for Vite and Inertia directives. Includes a comment for conditional React integration. ```html @vite @inertia::head @inertia::body ``` -------------------------------- ### ViteTemplateResolver Implementation Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/advanced/template_resolvers.md Example implementation of the `TemplateResolver` trait for Vite. It shows how to initialize the resolver with Vite and a template path, and how to resolve the template by injecting assets, HMR, and React refresh directives, along with Inertia SSR or CSR content. ```rust use crate::{template_resolvers::TemplateResolver, InertiaError, ViewData}; use async_trait::async_trait; use std::path::Path; use vite_rust::{features::html_directives::ViteDefaultDirectives, Vite}; // It can contain anything you might need to resolve the template pub struct ViteTemplateResolver { pub vite: Vite, template_root: String } impl ViteTemplateResolver { pub fn new(vite: Vite, template_path: &str) -> Self { let file = std::fs::read(template_path).expect("file {} should exist.", template_path); let template_root = String::from_utf8(file).expect("template file should contain a valid utf-8 encoded text."); Ok(Self { vite, template_root }) } } #[async_trait(?Send)] impl TemplateResolver for ViteTemplateResolver { async fn resolve_template( &self, view_data: ViewData<'_>, ) -> Result { let mut html = self.template_root.clone(); let _ self.vite.vite_directive(&mut html); self.vite.assets_url_directive(&mut html); self.vite.hmr_directive(&mut html); self.vite.react_directive(&mut html); if let Some(ssr) = &view_data.ssr_page { html = html.replace("@inertia::body", ssr.get_body()); html = html.replace("@inertia::head", &ssr.get_head()); } else { let stringified_page = serde_json::to_string(&view_data.page).unwrap(); let container = format!("
\n", stringified_page); html = html.replace("@inertia::body", &container); html = html.replace("@inertia::head", ""); } Ok(html) } } ``` -------------------------------- ### Access Root Template Data in Handlebars Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/responses.md Example of accessing root template data passed via `view_data` within a Handlebars (.hbs) file using the `ViteHBSTemplateResolver`. ```hbs {{!-- using the ViteHBSTemplateResolver, in a .hbs file --}} ``` -------------------------------- ### Inertia Middleware Setup for Actix Web Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/installation.md Integrates the InertiaMiddleware into an Actix Web application. This middleware handles shared props, correct redirect status codes for specific HTTP methods, and merging flash messages. ```rust use actix_web::{App, HttpServer}; use inertia_rust::actix::InertiaMiddleware; use inertia_rust::{hashmap, InertiaProp, InertiaProps, InertiaService}; #[actix_web::main] async fn main() -> std::io::Result<()> { // ... HttpServer::new(move || { App::new() .app_data(inertia.clone()) .wrap(InertiaMiddleware::new()) }) .bind(("127.0.0.1", 3000))? .run() .await } ``` -------------------------------- ### Testing Inertia Responses with Assertions Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/advanced/testing.md Use `TestRequest::inertia()` to get an Inertia response and `into_assertable_inertia()` to make assertions on the page URL, component, and props. Ensure the response is JSON by calling `.inertia()` before making the request. ```rust use inertia_rust::hashmap; use inertia_rust::test::{InertiaTestRequest, IntoAssertableInertia}; #[tokio::test] async fn test() { let app = actix_web::test::init_service(/* ... */).await; let request = actix_web::test::TestRequest::get() .inertia() .uri("/") .to_request(); let inertia_page = actix_web::test::call_service(&app, request) .await .into_assertable_inertia(); assert_eq!("/", inertia_page.get_url()); assert_eq!("Index", inertia_page.get_component()); assert_eq!( &Map::from(hashmap!["foo" => "bar".to_string().into()]) assertable.get_props() ); } ``` -------------------------------- ### Initialize Inertia.rs with ViteHBSTemplateResolver Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/installation.md Initializes Inertia.rs using ViteHBSTemplateResolver, configuring the application URL, asset version, and template path. It integrates with a pre-initialized Vite instance. ```rust // src/config/inertia.rs use super::vite::initialize_vite; use inertia_rust::{ template_resolvers::ViteHBSTemplateResolver, Inertia, InertiaConfig, InertiaError, InertiaVersion, }; use std::io; vite_rust::ViteMode; pub async fn initialize_inertia() -> Result { let vite = initialize_vite().await; let version = vite.get_hash().unwrap_or("development").to_string(); let dev_mode = *vite.mode() == ViteMode::Development; let resolver = ViteHBSTemplateResolver::builder() .set_vite(vite) .set_template_path("www/root.hbs") // the path to your root handlebars template .set_dev_mode(dev_mode) .build() .map_err(InertiaError::to_io_error)?; Inertia::new( InertiaConfig::builder() .set_url("http://localhost:3000") .set_version(InertiaVersion::Literal(version)) .set_template_resolver(Box::new(resolver)) .build(), ) } ``` -------------------------------- ### Initialize Vite Instance for Inertia.rs Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/installation.md Sets up and initializes a Vite instance with a specified manifest path and client-side entry points. This function is used to configure Inertia.rs. ```rust // src/config/vite.rs use vite_rust::{Vite, ViteConfig}; pub async fn initialize_vite() -> Vite { let vite_config = ViteConfig::default() .set_manifest_path("path/to/manifest.json") // you can add the same client-side entrypoints to vite-rust, // so that it won't panic if the manifest file doesn't exist but the // development server is running .set_entrypoints(vec!["www/app.ts"]); match Vite::new(vite_config).await { Err(err) => panic!("{}", err), Ok(vite) => vite } } ``` -------------------------------- ### Manual Async Inertia Prop Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/props.md Demonstrates the manual implementation of an asynchronous Inertia prop using `InertiaProp::Lazy` and `Arc`. This approach requires significant boilerplate for asynchronous callbacks. ```rust use std::sync::Arc; use inertia_rust::InertiaProp; use inertia_rust::IntoInertiaPropResult; let lazy_prop = InertiaProp::Lazy(Arc::new(move || Box::pin(async move { User::all().await.into_inertia_value(); }))); ``` -------------------------------- ### Redirect Back with Inertia.js Rust Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/responses.md Illustrates how to perform a 'redirect back' to the previous page using the `Inertia::back` facade method. This relies on Inertia's temporary session middleware or the referer header. ```rust use inertia_rust::{Inertia, InertiaFacade}; use actix_web::{get, Responder, HttpRequest}; #[get("/")] async fn index(req: HttpRequest) -> impl Responder { Inertia::back(&req) } ``` -------------------------------- ### Render Simple Page with Inertia.js Rust Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/responses.md Demonstrates two equivalent ways to render a simple Inertia page: using a framework-specific macro and a direct function call with `Inertia::render`. ```rust use inertia_rust::{Inertia, InertiaFacade, InertiaService}; use actix_web::{app, get, Responder, HttpRequest}; // this App::new().inertia_route("/", "Index"); // is the same as this #[get("/")] async fn index(req: HttpRequest) -> impl Responder { Inertia::render(&req, "Index".into()).await } App::new().service(index); ``` -------------------------------- ### InertiaProp Data Helpers Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/props.md Shows how to use `InertiaProp` helpers like `data` to create props. These helpers simplify the process of preparing serializable data for Inertia.js. ```rust use inertia_rust::{InertiaProp, IntoInertiaPropResult}; let prop = InertiaProp::Data("foo".into_inertia_value()); // or let prop = InertiaProp::data("foo"); // using `data` helper for generating a `InertiaProp::Data` ``` -------------------------------- ### Simplified Async Inertia Prop with `prop_resolver!` Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/props.md Shows how to use the `prop_resolver!` macro to simplify the creation of asynchronous Inertia props. This macro reduces the boilerplate code required for async callbacks. ```rust use std::sync::Arc; use inertia_rust::{prop_resolver, InertiaProp, IntoInertiaPropResult}; let lazy_prop = InertiaProp::Lazy(prop_resolver!({ User::all().await.into_inertia_value() })); ``` -------------------------------- ### Wrap Routes with Encrypt History Middleware Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/security/history-encryption.md Integrate EncryptHistoryMiddleware to enable encryption for a group of routes. Import the middleware from inertia_rust::actix. ```rust use actix_web::App; use inertia_rust::actix::EncryptHistoryMiddleware; App::new().wrap(EncryptHistoryMiddleware::new()); ``` -------------------------------- ### Inertia Rust Prop Evaluation Strategies Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/props.md Illustrates the equivalent behaviors of PHP's Inertia prop evaluation strategies (always included, optional, lazy, demand) in Inertia Rust using `InertiaProp::data`, `InertiaProp::lazy`, `InertiaProp::demand`, and `InertiaProp::always`. ```rust return Inertia::render_with_props(&req, "Users/Index", hashmap![ // ALWAYS included on standard visits // OPTIONALLY included on partial reloads // ALWAYS evaluated "users" => InertiaProp::data(User::all().await), // ALWAYS included on standard visits // OPTIONALLY included on partial reloads // ONLY evaluated when needed "users" => InertiaProp::lazy(prop_resolver!({ User::all().await.into_inertia_value() })), // NEVER included on standard visits // OPTIONALLY included on partial reloads // ONLY evaluated when needed "users" => InertiaProp::demand(prop_resolver!({ User::all().await.into_inertia_value() })), // ALWAYS included on standard visits // ALWAYS included on partial reloads // ALWAYS evaluated "users" => InertiaProp::always(User::all().await), ]); ``` -------------------------------- ### Manual Async Inertia Prop with Mocked Variables Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/props.md Illustrates the manual handling of moving variables into an asynchronous Inertia prop closure, often needed for testing or mocking. This involves cloning `Arc`-wrapped variables. ```rust use std::sync::Arc; use inertia_rust::InertiaProp; use inertia_rust::IntoInertiaPropResult; let user = Arc::new(User { name: "John Doe".into(), email: "johndoe@gmail.com".into() }); let permissions = Arc::new(vec!["read", "delete", "update", "delete"]); InertiaProp::lazy(Arc::new(move || { let user = user.clone(); let permissions = permissions.clone(); Box::pin(async move { user_can(user, permissions).await.into_inertia_value() }) })); ``` -------------------------------- ### Async Inertia Prop with `prop_resolver!` and Mocked Variables Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/props.md Demonstrates using `prop_resolver!` to simplify moving variables into an asynchronous Inertia prop closure. It allows specifying cloning statements and the async block separately. ```rust use std::sync::Arc; use inertia_rust::{prop_resolver, InertiaProp, IntoInertiaPropResult}; let user = Arc::new(User { name: "John Doe".into(), email: "johndoe@gmail.com".into() }); let permissions = Arc::new(vec!["read", "delete", "update", "delete"]); InertiaProp::lazy(prop_resolver!( let user = user.clone(), // statements separated by comma let permissions = permissions.clone(); // statements and block separated by semicolon { user_can(user, permissions).await.into_inertia_value() } // block )); ``` -------------------------------- ### Render Inertia Page Component with Props Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/responses.md Demonstrates rendering an Inertia page component named 'Users/Index' using `Inertia::render`. The component expects props, which will be provided by Inertia Rust. ```rust use inertia_rust::{Inertia, InertiaFacade}; use actix_web::{get, Responder, HttpRequest}; #[get("/users")] async fn index(req: HttpRequest) -> impl Responder { Inertia::render(&req, "Users/Index".into()).await } ``` -------------------------------- ### Redirect to External Site with Inertia.js Rust Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/responses.md Shows how to redirect to an external URL using `Inertia::location`. This method defers the redirect to the browser. ```rust use inertia_rust::{Inertia, InertiaFacade}; use actix_web::{get, Responder, HttpRequest}; #[get("/")] async fn index(req: HttpRequest) -> impl Responder { Inertia::location(&req, "https://inertiajs.com") } ``` -------------------------------- ### Pass Root Template Data with ViteTemplateResolver Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/responses.md Shows how to pass data to the root template using `Inertia::view_data` with the `ViteTemplateResolver`. The data is then accessible within the HTML template. ```rust use inertia_rust::{hashmap, Inertia, InertiaFacade}; use actix_web::{get, Responder, HttpRequest}; #[get("/")] async fn index(req: HttpRequest) -> impl Responder { Inertia::view_data(&req, hashmap![ "meta" => "Your page description".into() ]); Inertia::render(&req, "Index".into()).await } ``` -------------------------------- ### Render with Direct Inertia Instance Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/facades.md This snippet demonstrates the direct usage of the `Inertia` instance to render an Inertia response. It requires explicitly extracting the `Inertia` instance from the request context. ```rust use inertia_rust::Inertia; use actix_web::{get, Responder, HttpRequest, web::Data}; #[get("/")] async fn index(req: HttpRequest, inertia: Data) -> impl Responder { inertia.inner_render(&req, "Index").await } ``` -------------------------------- ### Cargo.toml Dependencies for Inertia.rs Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/installation.md Add these dependencies to your Cargo.toml file to use Inertia.rs with Actix Web and the vite-hbs-template-resolver. ```toml # Cargo.toml [dependencies] inertia-rust = { version = "2", features = ["actix", "vite-hbs-template-resolver"] } actix-web = "4" vite-rust = { version = "0.2" } ``` -------------------------------- ### Handle form submission and validation with Inertia.js Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/forms.md This Rust code demonstrates how to handle form submissions in an Inertia.js application using Actix Web. It utilizes the `validate_or_back` helper to validate incoming data and redirect back with errors if validation fails. It also shows how to handle general creation errors and redirect to a different page upon success. ```rust use actix_web::{get, post, Responder, HttpRequest, Redirect}; use inertia_rust::{hashmap, Inertia, InertiaFacade, InertiaProp}; use inertia_rust::validators::InertiaValidateOrRedirect; use validator::Validator; use serde::Deserialize; #[get("/users")] async fn index(req: HttpRequest) -> impl Responder { Inertia::render(&req, "Users/Index".into(), hashmap![ "users" => InertiaProp::data(User::all().await), ]) } #[post("/users/store")] async fn store(req: HttpRequest, body: Json) -> impl Responder { let data = match body.validate_or_back(&req) { Err(err_redirect) => return err_redirect, Ok(data) => data, }; if let Err(err) = User::create(data).await { return Inertia::back_with_errors(&req, hashmap![ "error" => "Oops, we could not create this user. Try again later." ]); }; Redirect::to("/users").see_other() } // the validation struct #[derive(Deserialize, Validator)] struct CreateUserDto { #[validate( required(message = "First name is a mandatory field."), length(max = 50, message = "Your first name must be shorter than 50 characters") )] first_name: Option, #[validate( required(message = "Last name is a mandatory field."), length(max = 50, message = "Your last name must be shorter than 50 characters") )] last_name: Option, #[validate( required(message = "Email is a mandatory field."), length(max = 50, message = "Your e-mail address must be shorter than 50 characters".), email(message = "You must provide a valid e-mail address.") )] email: Option } ``` -------------------------------- ### Render with Inertia Facade Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/facades.md This snippet shows how to use the `InertiaFacade` trait to render an Inertia response. It's the recommended and more elegant approach. ```rust use inertia_rust::{Inertia, InertiaFacade}; use actix_web::{get, Responder, HttpRequest}; #[get("/")] async fn index(req: HttpRequest) -> impl Responder { Inertia::render(&req, "Index".into()).await } ``` -------------------------------- ### Enable SSR in Inertia Config Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/ssr.md Modify your Inertia configuration to enable server-side rendering and set the SSR client. The SSR client can be defaulted or explicitly defined. ```diff // src/config/inertia.rs use super::vite::initialize_vite; use inertia_rust::{ template_resolvers::ViteHBSTemplateResolver, Inertia, InertiaConfig, InertiaError, - InertiaVersion, + InertiaVersion, + SsrClient }; use std::io; pub async fn initialize_inertia() -> Result { let vite = initialize_inertia().await; let version = vite.get_hash().unwrap_or("development").to_string(); let dev_mode = *vite.mode() == ViteMode::Development; let resolver = ViteHBSTemplateResolver::builder() .set_vite(vite) .set_template_path("www/root.hbs") // the path to your root handlebars template .set_dev_mode(dev_mode) .build() .map_err(InertiaError::to_io_error)?; Inertia::new( InertiaConfig::builder() .set_url("http://localhost:3000") .set_version(InertiaVersion::Literal(version)) .set_template_resolver(Box::new(resolver)) + .enable_ssr() + // `set_ssr_client` is optional. If not set, `SsrClient::default()` will be used, + // which is is "127.0.0.1:13714" + .set_ssr_client(SsrClient::new("127.0.0.1", 1000)) .build(), ) } ``` -------------------------------- ### Sharing Props with InertiaMiddleware Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/props/shared.md Use `with_shared_props` on `InertiaMiddleware` to define a callback that extracts and shares session data like flash messages on every request. This method requires a callback that receives the HTTP request and returns a future resolving to a `HashMap` of shared props. ```rust use actix_web::{App, HttpServer}; use inertia_rust::actix::InertiaMiddleware; use inertia_rust::{hashmap, InertiaProp, InertiaProps, InertiaService}; #[actix_web::main] async fn main() -> std::io::Result<()> { // ... HttpServer::new(move || { App::new() .app_data(inertia.clone()) .wrap(InertiaMiddleware::new().with_shared_props(Arc::new(|req| { // get the sessions from the request // depending on your opted framework let session = req.get_session(); let flash = session.get::("flash").unwrap(); async move { hashmap![ "flash" => InertiaProp::data(flash) ] } .boxed_local() }))) }) .bind(("127.0.0.1", 3000))? .run() .await } ``` -------------------------------- ### Making a Prop Mergeable with InertiaProp::merge Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/props/merging.md This Rust code demonstrates how to render a component with a mergeable prop using `InertiaProp::merge`. The `results` prop will be merged with existing client-side data if it already exists. ```rust use actix_web::{web, get, HttpRequest, Responder}; use inertia_rust::{hashmap, Inertia, InertiaProp}; use serde::Deserialize; #[derive(Deserialize)] struct RequestQuery { page: Option, per_page: Option } #[get("/users")] pub async fn users(req: HttpRequest, query: web::Query) -> impl Responder { let page = query.page.unwrap_or(1); let per_page = query.per_page.unwrap_or(10); Inertia::render_with_props(&req, "Users/Index", hashmap![ 'results' => InertiaProp::merge(User::paginate(page, per_page).await), ]) } ``` -------------------------------- ### Making a Deferred Prop Mergeable Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/props/merging.md This Rust code shows how to render a component with a deferred prop that is also mergeable. The prop will be fetched separately and then merged with existing client-side data. ```rust #[get("/users")] pub async fn users(req: HttpRequest, query: web::Query) -> impl Responder { let page = query.page.unwrap_or(1); let per_page = query.per_page.unwrap_or(10); return Inertia::render_with_props(&req, "Users/Index", hashmap![ 'results' => InertiaProp::defer(prop_resolve!({ User::paginate(page, per_page).await })).into_mergeable(), ]) } ``` -------------------------------- ### Grouping Deferred Props Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/props/deferred.md Organize deferred props into custom groups using `InertiaProp::defer_with_group`. Props within the same group are fetched in parallel by the client Inertia router. ```rust #[get("/users")] pub async fn users(req: HttpRequest) -> impl Responder { Inertia::render(&req, "Users/Index", hashmap![ 'users' => InertiaProp::data(User::all().await), 'roles' => InertiaProp::data(Role::all().await), 'permissions' => InertiaProp::defer(prop_resolver!({ Permission::all().await.into_inertia_value() })), 'teams' => InertiaProp::defer_with_group(prop_resolver!({ Team::all().await.into_inertia_value() }), 'attributes'), 'projects' => InertiaProp::defer_with_group(prop_resolver!({ Project::all().await.into_inertia_value() }), 'attributes'), 'tasks' => InertiaProp::defer_with_group(prop_resolver!({ Task::all().await.into_inertia_value() }), 'attributes'), ]).await } ``` -------------------------------- ### Add inertia-rust with actix-validator feature Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/forms.md To enable the `actix-validator` feature for form handling, add the specified dependency to your Cargo.toml file. Ensure you are using a compatible version of `inertia-rust` and the `validator` crate. ```toml # Cargo.toml [dependencies] inertia-rust = { version = "2", features = ["actix-validator", ...] } validator = "0.20" # ... ``` -------------------------------- ### Enable Global History Encryption Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/security/history-encryption.md Globally enable history encryption by configuring the Inertia struct. This sets the encrypt_history flag to true by default. ```rust use inertia_rust::{Inertia, InertiaConfig}; let inertia = Inertia::new( InertiaConfig::builder() .encrypt_history() .build() ); ``` -------------------------------- ### Deferring a Single Prop Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/props/deferred.md Use `InertiaProp::defer` to specify a prop that should be fetched after the page has rendered. This is useful for non-critical or heavy data. ```rust use actix_web::{get, HttpRequest, Responder}; use inertia_rust::{hashmap, prop_resolver, Inertia, InertiaProp, IntoInertiaPropResult}; // let's pretend these are some ORM's models and // assume they have an `all` method. use domain::identity::models::Role; use domain::identity::models::User; use domain::identity::models::Permission; #[get("/users")] pub async fn users(req: HttpRequest) -> impl Responder { Inertia::render_with_props(&req, "Users/Index", hashmap![ 'users' => InertiaProp::data(User::all().await), 'roles' => InertiaProp::data(Role::all().await), 'permissions' => InertiaProp::defer(prop_resolver!({ Permission::all().await.into_inertia_value() })), ]).await } ``` -------------------------------- ### Conditional Hydration in React App Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/ssr.md Conditionally hydrate or create the React application based on the 'ssr' meta tag. Use `hydrateRoot` for SSR responses and `createRoot` for client-side rendered pages. ```ts import "./app.scss"; import { createInertiaApp } from "@inertiajs/react"; import { createRoot, hydrateRoot } from "react-dom/client"; createInertiaApp({ // ... setup({ el, App, props }) { const isSSR = document.head .querySelector("meta[name='ssr']")?. getAttribute("content") === "true" ?? false; if (isSSR) { hydrateRoot(el, ); return; } createRoot(el).render(); }, }); ``` -------------------------------- ### Actix-Web Middleware for Reflashing Temporary Session Data Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/advanced/actix-web-implementations.md This middleware intercepts requests and responses to manage temporary session data, such as errors and previous request URLs, ensuring they are correctly flashed or persisted across requests. It's particularly useful for handling Inertia.js redirects and responses. ```rust use actix_session::SessionExt; use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}; use actix_web::Error; use actix_web::HttpMessage; use futures_util::future::LocalBoxFuture; use inertia_rust::{actix::{is_inertia_response, SessionErrors}, InertiaSessionToReflash, InertiaTemporarySession}; use log::error; use serde_json::Map; use std::future::{ready, Ready}; pub struct ReflashTemporarySession; impl Transform for ReflashTemporarySession where S: Service, Error = Error>, S::Future: 'static, B: 'static, { type Response = ServiceResponse; type Error = Error; type InitError = (); type Transform = ReflashTemporarySessionMiddleware; type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ready(Ok(ReflashTemporarySessionMiddleware { service })) } } pub struct ReflashTemporarySessionMiddleware { service: S, } const ERRORS_KEY: &str = "_errors"; const PREV_REQ_KEY: &str = "_prev_req_url"; const CURR_REQ_KEY: &str = "_curr_req_url"; impl Service for ReflashTemporarySessionMiddleware where S: Service, Error = Error>, S::Future: 'static, B: 'static, { type Response = ServiceResponse; type Error = Error; type Future = LocalBoxFuture<'static, Result>; forward_ready!(service); fn call(&self, req: ServiceRequest) -> Self::Future { let session = req.get_session(); let errors = session.remove(ERRORS_KEY).map(|errors| { serde_json::from_str(&errors).unwrap_or_else(|err| { error!("Failed to serialize session errors: {}", err); Map::new() }) }); let before_prev_url = session .get::(PREV_REQ_KEY) .unwrap_or(None) .unwrap_or("/".into()); let prev_url = session .get::(CURR_REQ_KEY) .unwrap_or(None) .unwrap_or("/".into()); // --- let temporary_session = InertiaTemporarySession { errors: errors.clone(), prev_req_url: prev_url.clone(), }; req.extensions_mut().insert(temporary_session); let fut = self.service.call(req); Box::pin(async move { let res = fut.await?; let req = res.request(); let session = req.get_session(); // If it's not a Inertia redirect or response, it might be assets response // then, reflash everything so that assets don't affect real user's requests let (prev_url, curr_url, optional_errors) = if !is_inertia_response(&res) { (before_prev_url, prev_url, errors) } else { let inertia_session = req.extensions_mut().remove::(); // if it needs to reflash a temporary flash session, then // replace data from inertia session middleware with the same as before, // so that the further request generates the same InertiaTemporarySession, // containing the exactly same errors, previous url, and current url. // // otherwise, gets the previous request's URI and stores the current one's as the next // request "previous", moving the navigation history if let Some(InertiaSessionToReflash(inertia_session)) = inertia_session { ( before_prev_url, inertia_session.prev_req_url, inertia_session.errors, ) } else { let errors = req .extensions_mut() .remove::() .map(|SessionErrors(errors)| errors); (prev_url, req.uri().to_string(), errors) } }; if let Err(err) = session.insert(ERRORS_KEY, optional_errors.unwrap_or_default()) { error!("Failed to add errors to session: {}", err); } if let Err(err) = session.insert(PREV_REQ_KEY, prev_url) { error!("Failed to update session previous request URL: {}", err); }; if let Err(err) = session.insert(CURR_REQ_KEY, curr_url) { error!("Failed to update session current request URL: {}", err); }; Ok(res) }) } } ``` -------------------------------- ### Enable Per-Request History Encryption Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/security/history-encryption.md Control history encryption on a per-request basis. This setting overrides global configuration and middleware settings. ```rust Inertia::encrypt_history(&req, true); ``` -------------------------------- ### Add SSR Meta Tag to Root Template Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/basic/ssr.md Include a meta tag in your root template's head to indicate whether the page was server-side rendered. This value is used by the client-side application. ```html ``` -------------------------------- ### InertiaTemporarySession Struct Definition Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/advanced/flash-messages.md Defines the structure for temporary session data, including optional errors and the previous request URL. This struct is used to pass session-related information, like validation errors, to Inertia pages. ```rust use inertia_rust::{hashmap, Inertia, InertiaFacade}; use serde_json::{Map, Value}; // InertiaTemporarySession struct from Inertia Rust #[derive(Clone, Serialize)] pub struct InertiaTemporarySession { // Optional errors hashmap pub errors: Option>, // The previous request URL // useful for redirecting back with errors pub prev_req_url: String, } ``` -------------------------------- ### Clear Server-Side History State Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/security/history-encryption.md Clear the history state from the server-side before rendering a page by calling clear_history. ```rust Inertia::clear_history(&req): ``` -------------------------------- ### Resetting Mergeable Props in JavaScript Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/props/merging.md This JavaScript snippet shows how to reset mergeable props when using `router.reload`. The `results` prop is specified to be reset, causing it to behave like a standard prop. ```javascript router.reload({ reset: ['results'], //... }) ``` -------------------------------- ### Disable Per-Request History Encryption Source: https://github.com/kaiofelps/inertia-rust/blob/v2/docs/src/security/history-encryption.md Disable history encryption for a specific route by passing false to the encrypt_history function. This overrides all other encryption settings for that request. ```rust Inertia::encrypt_history(&req, false); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.