Inertia Integration

Litestar Vite provides optional integration with InertiaJS, allowing you to build modern single-page applications while keeping your server-side routing and controllers. This integration is completely optional and can be added to an existing Litestar Vite project.

For a complete example of a fullstack application using Litestar with Inertia, check out the Litestar Fullstack Inertia Template.

Installation

Install the Inertia dependencies:

pip install "litestar-vite"

For the frontend, install the Inertia client library for your framework:

# For Vue.js
npm install @inertiajs/vue3

# For React
npm install @inertiajs/react

# For Svelte
npm install @inertiajs/svelte

Basic Setup

1. Configure Inertia Plugin

Add the Inertia plugin to your Litestar application:

from litestar import Litestar
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.template.config import TemplateConfig
from litestar_vite.inertia import InertiaConfig, InertiaPlugin

app = Litestar(
    template_config=TemplateConfig(
        engine=JinjaTemplateEngine(
            directory="templates"
        )
    ),
    plugins=[
        # ... VitePlugin configuration ...
        InertiaPlugin(
            config=InertiaConfig(
                root_template="index.html",
                redirect_unauthorized_to="/login"
            )
        )
    ]
)

2. Create Root Template

Create the Inertia root template (templates/index.html):

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    {{ vite('resources/css/app.css') }}
    {{ js_routes }}
</head>
<body>
    <div id="app" data-page="{{ inertia | escape }}"></div>
    {{ vite_hmr() }}
    {{ vite('resources/js/app.js') }}
</body>
</html>

3. Initialize Frontend

Set up your frontend entry point (resources/js/app.js):

import { createInertiaApp } from '@inertiajs/vue3'
import { createApp, h } from 'vue'
import Layout from './Layout.vue'

createInertiaApp({
    resolve: name => {
        const pages = import.meta.glob('./pages/**/*.vue', { eager: true })
        const page = pages[`./pages/${name}.vue`]
        page.default.layout = page.default.layout || Layout
        return page
    },
    setup({ el, App, props, plugin }) {
        createApp({ render: () => h(App, props) })
            .use(plugin)
            .mount(el)
    }
})

Features

Route Handlers

Create Inertia-powered route handlers, but using the component parameter to specify the client side component to render.

This parameter maps to the path (minus the .vue extension) of the component to render within the resources/js/pages directory.

The return value of any Litestar route will be serialized as normal and passed to the client side component as the data.content property.

from litestar import get

@get("/", component="Home")
async def home() -> dict[str, str]:
    return {
        "title": "Welcome",
        "message": "Hello from Litestar!"
    }

Shared Data

If you would like to share additional data with your client on any given route, you can use the share function.

This data will automatically serialized to JSON and passed to the client side component in a property named the same as the key you provide to the share function.

from litestar_vite.inertia import share

@get("/")
async def handler(request) -> dict[str, str]:
    # Available in all responses
    share(request, "user", {"name": "John"})
    return {"message": "Hello"}

Lazy Loading

If you would like to conditionally return data to the client based on the request using Inertia deferred data, you can use the lazy function.

This method allows for deferred execution of methods. Under normal cases, the data or callable referenced in the lazy function will not be executed. However, if the client requests the partial data or component, the element will be executed and rendered in the response.

It works with sync and async callables as well as static values.

from litestar_vite.inertia import lazy

@get("/dashboard", component="Dashboard")
async def dashboard():
    return {
        "stats": {"visits": 100},           # Loaded immediately
        "posts": lazy("posts", get_posts),  # Loaded on demand
    }

Security

CSRF Protection

If you would like to use drop-in CSRF protection with the Inertia plugin, you can use the built in Litestar CSRF protection with the cookie_name set to csrftoken or with a header name set to x-csrftoken.

If you use any other value, be sure to refer to the configuration details in the Inertia documentation.

For more examples and best practices, refer to the Litestar Fullstack Inertia Template and the official InertiaJS documentation.