HTMX

HTMX integration for hypermedia-driven applications with minimal JavaScript.

Quick Start

litestar assets init --template htmx

Project Structure

my-app/
├── app.py              # Litestar backend
├── package.json
├── vite.config.ts
├── templates/
│   ├── index.html      # Main page
│   └── partials/       # HTMX partials
│       └── items.html
└── src/
    ├── main.ts         # Entry (minimal)
    └── style.css

Backend Setup

from pathlib import Path
from litestar import Litestar, get
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.response import Template
from litestar.template.config import TemplateConfig
from litestar_vite import ViteConfig, VitePlugin
from litestar_vite.config import PathConfig

@get("/")
async def index() -> Template:
    return Template(template_name="index.html")

@get("/items")
async def get_items() -> Template:
    items = [
        {"id": 1, "name": "Item 1"},
        {"id": 2, "name": "Item 2"},
    ]
    return Template(
        template_name="partials/items.html",
        context={"items": items},
    )

vite = VitePlugin(
    config=ViteConfig(
        dev_mode=True,
        paths=PathConfig(
            bundle_dir=Path("public"),
            resource_dir=Path("src"),
        ),
    ),
)

app = Litestar(
    plugins=[vite],
    route_handlers=[index, get_items],
    template_config=TemplateConfig(
        directory=Path("templates"),
        engine=JinjaTemplateEngine,
    ),
)

Main Template

<!DOCTYPE html>
<html>
<head>
    <script src="https://unpkg.com/htmx.org@1.9.10"></script>
    {{ vite_hmr() }}
    {{ vite('src/main.ts') }}
</head>
<body>
    <h1>HTMX + Litestar</h1>

    <button hx-get="/items" hx-target="#items">
        Load Items
    </button>

    <div id="items">
        <!-- Items loaded here -->
    </div>
</body>
</html>

Partial Template

{# templates/partials/items.html #}
<ul>
{% for item in items %}
    <li>{{ item.name }}</li>
{% endfor %}
</ul>

HTMX Patterns

Inline Editing:

<div hx-get="/edit/{{ item.id }}"
     hx-trigger="click"
     hx-swap="outerHTML">
    {{ item.name }}
</div>

Form Submission:

<form hx-post="/items"
      hx-target="#items"
      hx-swap="beforeend">
    <input name="name" required>
    <button type="submit">Add</button>
</form>

Delete with Confirmation:

<button hx-delete="/items/{{ item.id }}"
        hx-confirm="Delete this item?"
        hx-target="closest li"
        hx-swap="outerHTML">
    Delete
</button>

Why HTMX?

  • Minimal JavaScript: Most interactivity via HTML attributes

  • Server-rendered: Full HTML responses, great for SEO

  • Progressive enhancement: Works without JS (degrades gracefully)

  • Simple mental model: Request → HTML response → DOM update

See Also