Quick Start: Inertia.js¶
In Inertia Hybrid mode (mode="hybrid"), Litestar owns server-side routing, authentication, and data loading while React, Vue, or Svelte renders each page component. Route handlers declare component="Dashboard/Index" and return a Python dict or msgspec.Struct directly — no client REST or GraphQL boilerplate required.
Install Dependencies¶
Install litestar-vite-plugin and your Inertia.js adapter:
Configure Litestar¶
Set mode="hybrid" and configure InertiaConfig(root_template="index.html"). When mode="hybrid" is active, VitePlugin automatically activates InertiaPlugin and InertiaMiddleware:
"""Inertia.js v2 Hybrid Mode quick-start example with Litestar and Vite."""
from __future__ import annotations
from pathlib import Path
from typing import Any
from litestar import Litestar, get
from msgspec import Struct
from litestar_vite import InertiaConfig, PathConfig, RuntimeConfig, ViteConfig, VitePlugin
class ProjectItem(Struct, frozen=True, gc=False):
"""Project summary rendered inside an Inertia page component."""
id: int
name: str
status: str
@get("/", component="Dashboard/Index", sync_to_thread=False)
def dashboard() -> dict[str, Any]:
"""Return Inertia page props for the ``Dashboard/Index`` component."""
return {
"projects": [
ProjectItem(id=1, name="Litestar API", status="healthy"),
ProjectItem(id=2, name="Vite Asset Pipeline", status="building"),
]
}
def create_app() -> Litestar:
"""Create a Litestar application configured for Inertia v2 hybrid mode."""
vite_config = ViteConfig(
mode="hybrid",
dev_mode=True,
paths=PathConfig(root=Path(), resource_dir=Path("resources"), bundle_dir=Path("public/build")),
inertia=InertiaConfig(root_template="index.html"),
runtime=RuntimeConfig(start_dev_server=False),
)
return Litestar(route_handlers=[dashboard], plugins=[VitePlugin(config=vite_config)])
def main() -> None:
"""Verify the Inertia hybrid application constructs cleanly."""
app = create_app()
plugin = app.plugins.get(VitePlugin)
assert plugin.config.mode == "hybrid"
assert ProjectItem(id=1, name="Litestar API", status="healthy").status == "healthy"
__all__ = ("ProjectItem", "create_app", "dashboard", "main")
Create the Root HTML Shell and Client Entrypoint¶
In mode="hybrid", index.html needs only <div id="app"></div> and your module entrypoint. By default (InertiaConfig(use_script_element=True)), Litestar Vite injects <script type="application/json" data-page="app">{...}</script> right before <div id="app"></div>:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Litestar + Inertia.js</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/resources/main.tsx"></script>
</body>
</html>
Initialize Inertia in resources/main.tsx using resolvePageComponent from litestar-vite-plugin/inertia-helpers and enable useScriptElementForInitialPage: true:
import { createInertiaApp } from "@inertiajs/react";
import { createRoot } from "react-dom/client";
import { resolvePageComponent } from "litestar-vite-plugin/inertia-helpers";
createInertiaApp({
defaults: {
future: {
useScriptElementForInitialPage: true,
},
},
resolve: (name: string) =>
resolvePageComponent(
`./pages/${name}.tsx`,
import.meta.glob("./pages/**/*.tsx"),
),
setup({ el, App, props }: { el: HTMLElement; App: any; props: any }) {
createRoot(el).render(<App {...props} />);
},
});
Run and Navigate¶
Start Litestar and Vite together:
- On initial visit to
GET /, Litestar rendersindex.htmlwith the initialPagePropsJSON and Vite asset scripts. - On subsequent
<Link href="...">clicks, Inertia sendsX-Inertia: trueand swaps only the JSONPagePropspayload. - See the Inertia.js Guide for deferred props (
defer), infinite scroll (merge), shared session data (share), and SSR.