Agent skill
htmx-frontend
Install this agent skill to your Project
npx add-skill https://github.com/pyramidheadshark/claude-scaffold/tree/main/.claude/skills/htmx-frontend
SKILL.md
HTMX Frontend
When to Load This Skill
Load when working with: Jinja2 templates, HTMX attributes, static files, server-side rendering, admin panels, dashboards integrated into FastAPI.
Architecture Decision
HTMX frontend lives inside the FastAPI application — no separate frontend service, no build step, no npm. This is intentional and correct for our use cases (internal tools, admin panels, dashboards with moderate load).
For high-traffic public frontends, consider a separate service. For everything else, co-location in FastAPI is simpler, easier to deploy, and has zero JS toolchain overhead.
Router Separation
JSON API routes and HTMX page routes live in separate routers and must never mix:
api/
├── routers/
│ ├── items.py # JSON API — returns Pydantic models
│ └── health.py
└── pages/
├── dashboard.py # HTMX pages — returns TemplateResponse
├── admin.py
└── chat.py
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="src/project_name/templates")
router = APIRouter()
@router.get("/", response_class=HTMLResponse)
async def dashboard(request: Request) -> HTMLResponse:
return templates.TemplateResponse(
"dashboard.html",
{"request": request, "title": "Dashboard"},
)
@router.get("/items/list", response_class=HTMLResponse)
async def items_list(request: Request, service=Depends(get_item_service)) -> HTMLResponse:
items = await service.list_all()
return templates.TemplateResponse(
"partials/items_list.html",
{"request": request, "items": items},
)
Template Structure
src/{project_name}/
├── templates/
│ ├── base.html # base layout
│ ├── dashboard.html
│ ├── admin.html
│ └── partials/ # HTMX partial responses
│ ├── items_list.html
│ ├── chat_message.html
│ └── toast.html
└── static/
├── htmx.min.js # pinned version, served locally
├── css/
│ └── styles.css
└── js/
└── app.js # minimal custom JS only
Pin HTMX version locally — do not use CDN in production.
Current stable: htmx.org@2.0.x.
Base Template Pattern
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{{ title }}{% endblock %}</title>
<link rel="stylesheet" href="/static/css/styles.css">
<script src="/static/htmx.min.js"></script>
</head>
<body hx-boost="true">
<main id="content">
{% block content %}{% endblock %}
</main>
<div id="toast-container" aria-live="polite"></div>
</body>
</html>
Core HTMX Patterns
Simple list refresh
<div id="items-list" hx-get="/pages/items/list" hx-trigger="load">
Loading...
</div>
<button
hx-post="/api/v1/items"
hx-target="#items-list"
hx-swap="outerHTML"
hx-include="[name='item-form']">
Add Item
</button>
Chat / streaming pattern
<div id="chat-messages"></div>
<form
hx-post="/pages/chat/send"
hx-target="#chat-messages"
hx-swap="beforeend"
hx-on::after-request="this.reset()">
<input type="text" name="message" placeholder="Ask a question..." autofocus>
<button type="submit">Send</button>
</form>
Partial response for partials/chat_message.html:
<div class="message message--{{ role }}">
<p>{{ content }}</p>
<time>{{ timestamp }}</time>
</div>
Loading indicator
<button
hx-post="/api/v1/process"
hx-indicator="#spinner">
Process
<span id="spinner" class="htmx-indicator">⏳</span>
</button>
Toast notifications
from fastapi.responses import HTMLResponse
def toast_response(message: str, variant: str = "success") -> HTMLResponse:
html = f'<div class="toast toast--{variant}" hx-swap-oob="beforeend:#toast-container">{message}</div>'
return HTMLResponse(content=html)
Static Files Setup
from fastapi.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="src/project_name/static"), name="static")
Performance Considerations
HTMX + FastAPI handles ~500 concurrent users comfortably on a single 2-core VM. For higher load:
- Add
nginxas reverse proxy indocker-compose.ymlwith static file caching - Enable Jinja2 template caching (
auto_reload=Falsein production) - Use
hx-boost="true"on<body>for SPA-like navigation without full reloads
If concurrent users exceed ~2000, evaluate separating the frontend service.
.env Keys Required
None specific to HTMX — uses the same FastAPI app configuration.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
critical-analysis
prompt-engineering
claude-api-patterns
github-actions
python-project-standards
test-first-patterns
Didn't find tool you were looking for?