Skip to main content

Inertia.js Adapter for Python Web Frameworks

Tests Lint PyPI version Python Versions License: MIT

A Python adapter for using Inertia.js with FastAPI and Django.

📚 Documentation | 🚀 Quick Start | 🗺️ Roadmap

Features

  • ✅ Full Inertia.js protocol support
  • ✅ Vite integration (dev & production)
  • ✅ Auto-detection of Vite entry point from vite.config.ts/js
  • ✅ Asset versioning for cache busting
  • ✅ Validation error handling (props.errors)
  • ✅ History encryption for sensitive data
  • ✅ External redirects (OAuth, payments, etc.)
  • ✅ Partial reloads & shared data
  • ✅ Merging props (infinite scroll support)
  • ✅ View data (server-side template variables)
  • ✅ TypeScript support

Installation

# Install from PyPI using uv (recommended)
uv pip install cross-inertia

# Or using pip
pip install cross-inertia

# Or install from source
uv pip install -e .

Try the Demo

We have a full-featured cat adoption demo app in examples/fastapi/:

# Using just (recommended)
just demo-install   # Install dependencies
just demo-fastapi   # Run the demo

# Or manually
cd examples/fastapi
bun install
./run-dev.sh

Visit http://127.0.0.1:8000 to see Inertia.js + FastAPI in action!

Quick Start

1. Basic Setup

from fastapi import FastAPI
from cross_inertia.fastapi import InertiaDep

app = FastAPI()

@app.get("/")
async def home(inertia: InertiaDep):
    return inertia.render(
        "Home",
        {
            "message": "Hello from Inertia!"
        }
    )

2. Custom Configuration

If you need to customize the Inertia configuration (e.g., different template directory or Vite settings):

from fastapi import FastAPI, Request, Depends
from cross_inertia.fastapi import InertiaResponse, Inertia

# Create custom InertiaResponse instance
inertia_response = InertiaResponse(
    template_dir="my_templates",
    vite_dev_url="http://localhost:5173",
    manifest_path="dist/.vite/manifest.json",
    vite_entry="src/main.tsx",  # Optional: auto-detected from vite.config
    vite_config_path="vite.config.ts"  # Optional: defaults to vite.config.ts
)

app = FastAPI()

def get_custom_inertia(request: Request) -> Inertia:
    from cross_web import StarletteRequestAdapter
    adapter = StarletteRequestAdapter(request)
    return Inertia(request, adapter, inertia_response)

@app.get("/")
async def home(inertia: Inertia = Depends(get_custom_inertia)):
    return inertia.render("Home", {"message": "Hello!"})

Configuration Options

InertiaResponse Parameters

Parameter Type Default Description
template_dir str "templates" Directory containing your root HTML template
vite_dev_url str "http://localhost:5173" Vite dev server URL
manifest_path str "static/build/.vite/manifest.json" Path to Vite manifest file (production)
vite_entry str | None None Vite entry point (auto-detected from config if None)
vite_config_path str "vite.config.ts" Path to vite.config.ts/js for auto-detection

Auto-Detection of Vite Entry

By default, the adapter will attempt to read your vite.config.ts (or .js) file and extract the entry point from:

// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      input: "frontend/app.tsx", // ← Auto-detected
    },
  },
});

This means you don't need to specify vite_entry manually - it will match your Vite configuration automatically!

Root Template

Create a template file (default: templates/app.html):

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    {{ vite()|safe }}
  </head>
  <body>
    <script data-page="app" type="application/json">
      {{ page|safe }}
    </script>
    <div id="app"></div>
  </body>
</html>

The {{ vite() }} function will automatically include:

  • React Fast Refresh scripts (dev mode)
  • Vite client scripts (dev mode)
  • Your entry point script
  • Built CSS and JS files (production mode)

Inertia.js v3 reads this JSON script format by default:

createInertiaApp({
  // ...
});

Using Custom Entry Points

<!-- Use default entry (from config/auto-detection) -->
{{ vite()|safe }}

<!-- Use custom entry point -->
{{ vite('admin/app.js')|safe }}

Backward compatibility: The old {{ vite_tags|safe }} variable is still supported.

Validation Errors

Validation errors are automatically handled:

@app.post("/users")
async def create_user(inertia: InertiaDep):
    errors = validate_user(request_data)

    if errors:
        # Returns 200 with props.errors for Inertia requests
        return inertia.render(
            "Users/Create",
            {"user": request_data},
            errors=errors
        )

    # Create user...
    return inertia.render("Users/Show", {"user": new_user})

External Redirects

Use inertia.location() to redirect to external websites or non-Inertia pages:

@app.get("/auth/github")
async def github_oauth(inertia: InertiaDep):
    """Redirect to GitHub OAuth"""
    oauth_url = f"https://github.com/login/oauth/authorize?client_id={CLIENT_ID}"
    return inertia.location(oauth_url)

@app.get("/shelter/{id}/directions")
async def get_directions(id: int, inertia: InertiaDep):
    """Redirect to Google Maps"""
    shelter = get_shelter(id)
    maps_url = f"https://maps.google.com/?q={shelter.address}"
    return inertia.location(maps_url)

This returns a 409 Conflict response with X-Inertia-Location header, which the client automatically follows with a full page navigation.

History Encryption

Protect sensitive data in browser history by encrypting page state. This prevents users from viewing sensitive information after logging out by pressing the back button.

# Encrypt sensitive pages
@app.get("/account/transactions")
async def transactions(inertia: InertiaDep):
    inertia.encrypt_history()  # Enable encryption
    return inertia.render("Transactions", {
        "balance": user.balance,
        "transactions": user.get_transactions()
    })

# Clear encrypted history on logout
@app.post("/logout")
async def logout(inertia: InertiaDep):
    clear_user_session()
    inertia.clear_history()  # Rotate encryption keys
    return inertia.render("Login", {})

How it works:

  • Uses browser's Web Crypto API (AES-GCM encryption)
  • Encryption keys stored in sessionStorage
  • clear_history() rotates keys, making old history unreadable
  • Only works over HTTPS (except localhost)

Use cases: Banking apps, healthcare (HIPAA), admin panels, any sensitive data

Development vs Production

The adapter automatically detects whether Vite dev server is running:

  • Dev mode: Includes Vite dev server scripts and React Fast Refresh
  • Production mode: Reads from manifest.json and includes built assets

No configuration changes needed - it just works!

Feature Status

Feature FastAPI Django
Basic protocol ✅ ✅
Vite development and production ✅ ✅
Asset version mismatch ✅ ✅
Partial reloads and shared data ✅ ✅
Redirects and history encryption ✅ ✅
Merge, deferred, and once props ✅ ✅
Error bags and prefetching ✅ ✅
SSR ✅ ✅

See ROADMAP.md for detailed implementation plans and progress tracking.

Current Status

Version: v0.20.0

This adapter implements all production-critical Inertia features and is ready for production use.

Production-ready features:

  • ✅ Basic page rendering
  • ✅ Form submissions with validation
  • ✅ Navigation between pages
  • ✅ Development with Vite HMR
  • ✅ Asset version mismatch handling (409 Conflict)
  • ✅ Partial reloads for performance
  • ✅ Shared data (auth, flash messages)
  • ✅ External redirects (OAuth, payments)
  • ✅ History encryption (sensitive data protection)
  • ✅ Merging props (infinite scroll)
  • ✅ View data (server-side template variables)

Contributing

Contributions are very welcome! This adapter aims to match the Laravel adapter's feature set.

How to contribute:

  1. Check GitHub Issues for open tasks
  2. Pick a feature (look for good first issue or high-priority labels)
  3. Read the linked Inertia.js documentation
  4. Implement following existing patterns
  5. Write tests and update documentation
  6. Submit a PR!

See ROADMAP.md for the full project roadmap and milestone planning.

License

MIT

Metadata

Release files for cross-inertia 0.22.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cross-inertia 0.22.1
File Size Uploaded Provenance Actions
cross_inertia-0.22.1.tar.gz 2.6 MB Without provenance Details for cross_inertia-0.22.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cross-inertia 0.22.1
File Interpreter ABI Platform Actions
cross_inertia-0.22.1-py3-none-any.whl Python 3 none any Details for cross_inertia-0.22.1-py3-none-any.whl

Total release size: 2.6 MB

Release files / cross_inertia-0.22.1.tar.gz

Download URL cross_inertia-0.22.1.tar.gz
Size 2.6 MB
Tags Source
SHA-256 checksum
How to use checksums
d6dfbe63cc1749c294645b709e4ef09b599cf4c0e655537941150fbebc706b2a
BLAKE2b-256 checksum
How to use checksums
6a0efdd1f176e1400d03f233a273d126d13e96adb68d20f73c88fa2c5ef91a8a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / cross_inertia-0.22.1-py3-none-any.whl

Download URL cross_inertia-0.22.1-py3-none-any.whl
Size 60.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
77e11e649a9702800e1ebf595ce023fed470a211ba1557825252ead5534c07d6
BLAKE2b-256 checksum
How to use checksums
82ad9e49554363ea857fa209ad8b5b471ae969c63bd37e04e221e67e5c8a0ee3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for cross-inertia 0.22.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.22.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page