Skip to main content

fastapi-view

A lightweight, flexible Jinja2 template rendering library for FastAPI applications, with built-in support for Inertia.js and Vite integration.

Python Version FastAPI License

Features

  • Simple View Rendering: Minimal boilerplate for rendering Jinja2 templates in FastAPI routes
  • Inertia.js Support: FastAPI Inertia.js adapter for building modern monolithic applications
  • Vite Integration: Seamless Vite asset management with HMR support in development
  • Flexible Configuration: Environment-based settings using Pydantic

Installation

pip install fastapi-view

Or using uv:

uv add fastapi-view

Quick Start

Basic View Rendering

from fastapi import FastAPI
from fastapi_view import ViewDepends

app = FastAPI()

@app.get("/")
def index(view: ViewDepends):
    return view.render("index", {"message": "Hello World"})

Set the templates directory via environment variable:

export FV_TEMPLATES_PATH=templates

Inertia.js Integration

from fastapi import FastAPI
from fastapi_view.inertia import InertiaDepends

app = FastAPI()

@app.get("/dashboard")
def dashboard(inertia: InertiaDepends):
    return inertia.render("Dashboard/Index", props={
        "user": {"name": "John Doe", "email": "john@example.com"}
    })

Configure Inertia via environment variables:

export FV_INERTIA_ROOT_TEMPLATE=app.html
export FV_INERTIA_ASSETS_VERSION=v1

Vite Asset Management

In your Jinja2 template:

<!DOCTYPE html>
<html>
  <head>
    {{ vite_hmr_client() | safe }}
    {{ vite_asset('resources/js/app.ts') | safe }}
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

Configure Vite settings:

export FV_VITE_DEV_MODE=true
export FV_VITE_DEV_SERVER_URL=http://localhost:5173
export FV_VITE_MANIFEST_PATH=public/build/manifest.json

Configuration

fastapi-view uses Pydantic settings for configuration. All settings can be configured via environment variables with the FV_ prefix.

View Settings

Environment Variable Description Required Default
FV_TEMPLATES_PATH Path to Jinja2 templates directory Yes -

Inertia Settings

Environment Variable Description Required Default
FV_INERTIA_ROOT_TEMPLATE Root template for Inertia responses No app.html
FV_INERTIA_ASSETS_VERSION Asset versioning for cache busting No None

Vite Settings

Environment Variable Description Required Default
FV_VITE_DEV_MODE Enable development mode with HMR No False
FV_VITE_DEV_SERVER_PROTOCOL Protocol for Vite dev server (http/https) No http
FV_VITE_DEV_SERVER_HOST Host for Vite dev server No localhost
FV_VITE_DEV_SERVER_PORT Port for Vite dev server No 5173
FV_VITE_WS_CLIENT_PATH WebSocket client path for HMR No @vite/client
FV_VITE_MANIFEST_PATH Path to Vite manifest file No dist/.vite/manifest.json
FV_VITE_DIST_PATH Path to build output directory No dist
FV_VITE_DIST_URI_PREFIX URI prefix for serving static assets Yes (in production mode) None
FV_VITE_STATIC_URL Base URL for static assets (alternative to uri prefix) Yes (in production mode) None

Note: In production mode (FV_VITE_DEV_MODE=False), either FV_VITE_STATIC_URL or FV_VITE_DIST_URI_PREFIX must be configured.

Advanced Usage

Shared Inertia Props

Share data within a request by calling share() on the Inertia instance:

from fastapi_view.inertia import InertiaDepends

@app.get("/dashboard")
def dashboard(inertia: InertiaDepends):
    # Share props for this request
    inertia.share("app_name", "My Application")
    inertia.share("user", get_current_user())

    return inertia.render("Dashboard/Index", props={"stats": get_stats()})

For sharing data across all routes, use a dependency or middleware:

from fastapi import Depends

def with_shared_props(inertia: InertiaDepends):
    inertia.share("app_name", "My Application")
    inertia.share("version", "1.0.0")

    return inertia

@app.get("/dashboard")
def dashboard(inertia: InertiaDepends = Depends(with_shared_props)):
    return inertia.render("Dashboard/Index", props={"stats": get_stats()})

Optional Props

Use optional props for lazy loading data:

@app.get("/users")
def users(inertia: InertiaDepends):
    return inertia.render("Users/Index", props={
        "users": lambda: fetch_users(),  # Always evaluated
        "metrics": inertia.optional(lambda: fetch_metrics())  # Only on partial reload
    })

Flash Messages

Flash messages for one-time notifications (requires SessionMiddleware):

from starlette.middleware.sessions import SessionMiddleware

app.add_middleware(SessionMiddleware, secret_key="your-secret-key")

@app.post("/login")
def login(inertia: InertiaDepends):
    # ... authentication logic ...
    inertia.flash("success", "Login successful!")
    return inertia.render("Dashboard")

Flash messages are available on the frontend under the flash prop:

<script setup>
import { usePage } from '@inertiajs/vue3'

const page = usePage()
const flash = page.props.flash

// Access flash messages
if (flash.success) {
  console.log(flash.success)
}
if (flash.error) {
  console.log(flash.error)
}
</script>

Deferred Props

Use deferred props to load expensive data after the initial page render, improving perceived performance:

@app.get("/dashboard")
def dashboard(inertia: InertiaDepends):
    return inertia.render("Dashboard/Index", props={
        # Fast data loaded immediately
        "quick_stats": get_quick_stats(),

        # Slow data deferred - loads after initial page render
        "statistics": inertia.defer(lambda: get_statistics(), group="analytics"),
        "recent_activities": inertia.defer(lambda: fetch_activities(), group="analytics"),

        # Another deferred group for separate loading
        "chart_data": inertia.defer(lambda: get_chart_data(), group="charts")
    })

On the frontend, deferred props will be undefined initially, then populated once loaded:

<template>
  <!-- Show skeleton while loading -->
  <div v-if="statistics">
    <p>Total Users: {{ statistics.total_users }}</p>
  </div>
  <div v-else class="animate-pulse">
    <div class="h-4 bg-gray-300 rounded"></div>
  </div>
</template>

<script setup>
defineProps(['statistics'])  // Will be undefined, then populated
</script>

Benefits:

  • Faster initial page load
  • Better user experience with progressive data loading
  • Group related deferred props for efficient batching

Merge Props

Use merge props to combine new data with existing client-side data during partial reloads (perfect for infinite scroll, "load more" features):

@app.get("/posts")
def posts(inertia: InertiaDepends, page: int = 1):
    per_page = 10
    offset = (page - 1) * per_page
    posts = fetch_posts(offset, per_page)

    return inertia.render("Posts/Index", props={
        "posts": inertia.merge(lambda: posts),
        "pagination": {
            "current_page": page,
            "has_more": has_more_posts(page, per_page)
        }
    })

On the frontend, use Inertia's partial reload to append new data:

<template>
  <div>
    <div v-for="post in posts" :key="post.id">
      <h3>{{ post.title }}</h3>
    </div>

    <button v-if="pagination.has_more" @click="loadMore">
      Load More
    </button>
  </div>
</template>

<script setup>
import { router } from '@inertiajs/vue3'

const props = defineProps(['posts', 'pagination'])

function loadMore() {
  router.reload({
    data: { page: props.pagination.current_page + 1 },
    only: ['posts', 'pagination']
  })
}
</script>

Advanced merge options:

# Deep merge for nested objects
inertia.merge(lambda: data).deep_merge()

# Prepend instead of append
inertia.merge(lambda: posts).prepend()

# Append/prepend at specific paths
inertia.merge(lambda: data).append(paths=["items", "results"])

# Prevent duplicates by matching on a field
inertia.merge(lambda: posts).append(match_on="id")

Benefits:

  • Efficient "load more" / infinite scroll implementations
  • Reduced data transfer on partial reloads
  • Automatic client-side data merging

Custom Response Configuration

@app.get("/custom")
def custom_view(view: ViewDepends):
    return view.render(
        "custom",
        context={"data": "value"},
        status_code=201,
        headers={"X-Custom-Header": "value"}
    )

Complete Example

Check out the full Inertia.js example application in the examples/inertia directory, which demonstrates:

  • User authentication flow
  • CRUD operations with Inertia
  • Vite + Vue 3 + TypeScript frontend
  • Session management
  • Form handling

To run the example:

cd examples/inertia
cp .env.example .env
uv sync
bun install
bun run build
uv run uvicorn app.main:app --reload

Development

Setup

git clone https://github.com/turisesonia/fastapi-view.git
cd fastapi-view
uv sync

Running Tests

uv run pytest

Code Formatting

uvx ruff format
uvx ruff check --fix

Requirements

  • Python 3.10+
  • FastAPI 0.70+
  • Jinja2 3.0+
  • Pydantic Settings 2.0+

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

Metadata

Release files for fastapi-view 0.6.3

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

Source distribution (sdist)

Source distribution for fastapi-view 0.6.3
File Size Uploaded Provenance Actions
fastapi_view-0.6.3.tar.gz 209.4 kB Without provenance Details for fastapi_view-0.6.3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-view 0.6.3
File Interpreter ABI Platform Actions
fastapi_view-0.6.3-py3-none-any.whl Python 3 none any Details for fastapi_view-0.6.3-py3-none-any.whl

Total release size: 224.6 kB

Release files / fastapi_view-0.6.3.tar.gz

Download URL fastapi_view-0.6.3.tar.gz
Size 209.4 kB
Tags Source
SHA-256 checksum
How to use checksums
61d8fcdf56df9f3a380bb9e3668d4e366d2e45f0e6d2b5f6e196d09d248a0190
BLAKE2b-256 checksum
How to use checksums
2531d83585668ed9b3223187af8cd105d60d185995239591a16d5fe2725352cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","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 / fastapi_view-0.6.3-py3-none-any.whl

Download URL fastapi_view-0.6.3-py3-none-any.whl
Size 15.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7c4599152a46fd443717548f5a7693dadf17a839e84b2d9a5a3a672968309c05
BLAKE2b-256 checksum
How to use checksums
bfa916c50a0f8a6caf7ff005c21b39b58f55678c3d6f2be361cbf31f253cd275
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","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 fastapi-view 0.6.3

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.

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