Skip to content

Latest commit

Β 

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Veer

Veer

The Inertia.js server-side protocol superset, for Rust.

Build modern single-page apps in React, Vue, or Svelte β€” without writing a JSON API, a client-side router, or a single fetch. Your Rust handlers return typed props, and the frontend hydrates as if the page was server-rendered. Because it was.

Discord Latest Version on crates.io GitHub CI Status docs.rs MSRV 1.88 Sponsor on GitHub Support on Ko-fi

πŸ“– Table of Contents

✨ What is Inertia, and why a Rust adapter

Inertia.js is a glue layer that lets a classic server-rendered backend drive a modern SPA frontend. The server returns a page object (component name + props); the official Inertia client adapter for React/Vue/Svelte takes care of mounting the component, hydrating props, intercepting links, and making subsequent navigations into JSON XHRs.

veer is a clean-room Rust implementation of the server side of the Inertia v3 protocol. It targets axum out of the box; the protocol core is framework-agnostic, so adapters for other Rust web frameworks slot in beside it.

   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  Rust handler           β”‚  ── page object ──▢   β”‚  Inertia client (JS)    β”‚
   β”‚  inertia.render(...)    β”‚       (JSON)          β”‚  React / Vue / Svelte   β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β–²                                                  β”‚
              └─────────────  navigation XHR  β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Highlights

  • πŸ¦€ The full Inertia v3 protocol (client 3.8), checked against the Laravel adapter and the real client
  • ⚑ First-class axum adapter: one extractor, one tower layer
  • πŸ“¦ Every prop type: partial reloads, deferred, optional, once, merge, infinite scroll, big integers
  • βœ… Forms the Inertia way: validation errors, flash data, error bags, Precognition live validation, file uploads
  • πŸ–₯️ SSR through the official Node/Bun renderer, with fallback to client rendering
  • βš™οΈ Vite dev server + production manifest integration, and embedded assets for a single-binary deploy
  • πŸͺ’ End-to-end TypeScript: page props and route helpers generated from your Rust types
  • πŸ”’ CSRF protection, history encryption, and a recorder for the Inertia DevTools extension
  • 🧩 Framework-agnostic protocol core with pluggable sessions, root views and SSR clients

πŸ“¦ Installation

[dependencies]
veer = "0.3"

Or with cargo add:

cargo add veer

The default feature set includes the axum adapter. See Feature flags for everything else. Coming from an older version? Read the upgrade guide.

πŸš€ Quick Start

use axum::{routing::get, Router};
use veer::{Inertia, InertiaConfig, InertiaLayer, MinimalRootView};

#[tokio::main]
async fn main() {
    let cfg = InertiaConfig::new()
        .root_view(
            MinimalRootView::new()
                .title("Acme")
                .vite_entry("/src/main.tsx"),
        );

    let app = Router::new()
        .route("/", get(|inertia: Inertia| async move {
            inertia.render("Home", serde_json::json!({ "msg": "hello" }))
        }))
        .layer(InertiaLayer::new(cfg));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

The Inertia extractor reads the request. render(component, props) returns a builder. The layer handles the rest of the protocol β€” initial HTML on first load, JSON on XHR navigations, 409 for asset-version mismatches, 303 for redirects, partial reloads. Continue with the getting started guide.

🧭 A short tour

Load data only when the page needs it. Closures run only when the response needs their value, and they return plain Rust types. β†’ Props

inertia
    .render("Users/Index", UsersIndexProps { users })
    .lazy("stats", || async { load_stats().await })                    // on request only
    .deferred("activity", "default", || async { load_activity().await }) // after first render
    .once("plans", || async { load_plans().await })                    // one time per client
    .prop("feed", Prop::scroll(move || async move { load_feed(page).await })) // infinite scroll

Handle a form like a classic server app. Validate, redirect, and the errors and the flash message show on the next page. β†’ Forms and validation

async fn users_store(inertia: Inertia, Validated(body): Validated<NewUser>) -> impl IntoResponse {
    // Invalid input went back to the form with its errors already.
    create_user(body).await;
    inertia.redirect("/users").with_flash("success", "User created")
}

Share data with every page. β†’ Shared props

let cfg = InertiaConfig::new().share(|req| {
    let user = req.extension::<CurrentUser>().cloned(); // set by your auth middleware
    async move { json!({ "auth": { "user": user } }) }
});

Get TypeScript types from your Rust structs. β†’ TypeScript bindings

#[derive(Serialize, TS)]
#[ts(export)]
pub struct UsersIndexProps { pub users: Vec<User> }
veer::register_page!(UsersIndexProps, "Users/Index");

// The component name comes from the registration.
inertia.page(UsersIndexProps { users })
import { users, type UsersIndexProps } from "./gen";

<Link href={users.show.url({ id: user.id })}>{user.name}</Link>

πŸ“š Documentation

Guide What it covers
Getting started Install, first page, how a request flows, the frontend entry point
Props Partial reloads, lazy / deferred / once props, merging, infinite scroll, shared props, big integers
Forms and validation Validated, InertiaForm, validation errors, error bags, flash data, Precognition, file uploads
Redirects and history redirect, back, external redirects, URL fragments, history encryption
Sessions The cookie store, tower-sessions, writing your own store
Error pages 404 and application errors as Inertia pages
Testing veer::testing: page assertions for your handlers
Vite, SSR and assets ViteRootView, server-side rendering, embedded assets, <head> elements
TypeScript bindings Typed page props and route helpers generated from Rust
CSRF protection CsrfLayer and the XSRF-TOKEN convention
DevTools The recorder for the Inertia DevTools browser extension
Architecture Crate layout, protocol coverage, extension points
Upgrading Every breaking change of each version and what to do

API reference: docs.rs/veer. For the client side, use the Inertia documentation.

πŸŽ›οΈ Feature Flags

Flag Default Effect
axum on Axum extractor + tower layer + InertiaForm body extractor
multipart off File upload support (UploadedFile, MultipartStream)
ssr off HTTP SSR client (reqwest)
cookie-session off Signed-cookie session store
tower-sessions off Session store backed by tower-sessions
validator off Validated<T> extractor + IntoErrorBag impl for validator::ValidationErrors
garde off GardeValidated<T> extractor + IntoErrorBag impl for garde::Report
csrf off CSRF protection (CsrfLayer)
embed off Embedded-asset serving for single-binary deploys (EmbeddedAssets)
devtools off Recorder + read API for the Inertia DevTools browser extension
ts off End-to-end TypeScript bindings codegen (ts-rs + inventory)
testing off Test helpers (veer::testing)

Disabling a feature drops its transitive deps entirely.

πŸ§ͺ Example App

A complete end-to-end demo lives at examples/axum-react-todo/ β€” axum backend + React/Vite frontend, in CSR and SSR mode. It has a todo list with validation, flash messages and Precognition, and a showcase page for once, deferred, rescued, scroll and big-integer props with generated TypeScript types.

cd examples/axum-react-todo
just              # CSR mode β€” open http://localhost:5173
SSR=1 just dev    # SSR mode β€” open http://localhost:3000

πŸ—ΊοΈ Status & Roadmap

veer is pre-1.0. The protocol surface follows Inertia v3 as of client 3.8 / inertia-laravel 3.5; the coverage table has the detail. Planned:

  • Adapters for actix-web and rocket
  • Typed route-param inference (today: string | number; goal: read each handler's Path extractor and emit the matching TS type)

Contributions, bug reports, and protocol-conformance fixtures welcome.

πŸ™Œ Acknowledgements

The protocol is Inertia.js by Jonathan Reinink and contributors. veer is an independent server-side implementation for Rust, modeled on the Laravel adapter's behavior.

πŸ§ͺ Testing

cargo test --all-features

πŸ“‹ Changelog

Please see CHANGELOG for more information on what has changed recently.

🀝 Contributing

Issues and pull requests are welcome. You can also join our Discord server to discuss ideas and get help: Discord Invite.

πŸ”’ Security Vulnerabilities

Please report security vulnerabilities to security@climactic.co.

πŸ’– Support This Project

Veer is free and open source, built and maintained with care. If this crate has saved you development time or helped power your application, please consider supporting its continued development.

Sponsor on GitHub Β  Support on Ko-fi

🌟 Sponsors

Your logo here β€” Become a sponsor and get your logo featured in this README and on our website.

Interested in title sponsorship? Contact us at sponsors@climactic.co for premium placement and recognition.

⭐ Star History

Star History Chart

πŸ“„ License

Dual-licensed under MIT or Apache 2.0 at your option. Please see MIT and APACHE for more information.

About

The Inertia.js server-side protocol superset, for Rust.

Topics

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages