Skip to content

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

uv add litestar-vite
pip install litestar-vite
pdm add litestar-vite
poetry add litestar-vite

Install litestar-vite-plugin and your Inertia.js adapter:

npm install -D litestar-vite-plugin
npm install @inertiajs/react react react-dom
pnpm add -D litestar-vite-plugin
pnpm add @inertiajs/react react react-dom
bun add -d litestar-vite-plugin
bun add @inertiajs/react react react-dom

Configure Litestar

Set mode="hybrid" and configure InertiaConfig(root_template="index.html"). When mode="hybrid" is active, VitePlugin automatically activates InertiaPlugin and InertiaMiddleware:

docs/examples/getting_started/inertia_app.py
"""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>:

index.html
<!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:

resources/main.tsx
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:

litestar run --reload
  • On initial visit to GET /, Litestar renders index.html with the initial PageProps JSON and Vite asset scripts.
  • On subsequent <Link href="..."> clicks, Inertia sends X-Inertia: true and swaps only the JSON PageProps payload.
  • See the Inertia.js Guide for deferred props (defer), infinite scroll (merge), shared session data (share), and SSR.