> htmx
htmx gives you access to AJAX, CSS transitions, WebSockets, and Server-Sent Events directly in HTML using attributes. It enables dynamic web UIs without writing JavaScript by letting any element issue HTTP requests and swap content into the DOM.
curl "https://skillshub.wtf/TerminalSkills/skills/htmx?format=md"htmx
htmx extends HTML with attributes like hx-get, hx-post, hx-swap, and hx-trigger to make any element capable of issuing HTTP requests and updating the DOM. The server returns HTML fragments, not JSON.
Installation
<!-- index.html — add htmx via CDN or npm -->
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<!-- Or: npm install htmx.org -->
Core Attributes
<!-- templates/core-demo.html — fundamental htmx attributes -->
<!-- GET request, replace inner HTML of target -->
<button hx-get="/api/articles" hx-target="#article-list" hx-swap="innerHTML">
Load Articles
</button>
<div id="article-list"></div>
<!-- POST form without page reload -->
<form hx-post="/api/articles" hx-target="#article-list" hx-swap="afterbegin">
<input name="title" placeholder="Title" required />
<textarea name="body" placeholder="Body" required></textarea>
<button type="submit">Create</button>
</form>
<!-- DELETE with confirmation -->
<button hx-delete="/api/articles/42" hx-confirm="Are you sure?" hx-target="closest article" hx-swap="outerHTML swap:500ms">
Delete
</button>
Swap Strategies
<!-- templates/swap-strategies.html — different ways to insert content -->
<!-- Replace inner content (default) -->
<div hx-get="/fragment" hx-swap="innerHTML">Replace my contents</div>
<!-- Replace entire element -->
<div hx-get="/fragment" hx-swap="outerHTML">Replace me entirely</div>
<!-- Append/prepend to list -->
<div id="list">
<button hx-get="/more" hx-target="#list" hx-swap="beforeend">Load More</button>
</div>
<!-- Swap with transition delay -->
<div hx-get="/fragment" hx-swap="innerHTML settle:300ms">With transition</div>
<!-- Out-of-band swaps (update multiple elements) -->
<!-- Server returns: -->
<!-- <div id="notification" hx-swap-oob="innerHTML">New notification!</div> -->
<!-- <div id="count" hx-swap-oob="innerHTML">43</div> -->
Triggers
<!-- templates/triggers.html — custom event triggers -->
<!-- Trigger on input change with debounce -->
<input type="search" name="q"
hx-get="/search"
hx-trigger="input changed delay:300ms"
hx-target="#results" />
<div id="results"></div>
<!-- Trigger on intersection (lazy loading) -->
<div hx-get="/more-articles"
hx-trigger="intersect once"
hx-swap="afterend">
Loading...
</div>
<!-- Trigger on custom event -->
<div hx-get="/notifications" hx-trigger="newMessage from:body">
Notifications
</div>
<!-- Polling -->
<div hx-get="/api/status" hx-trigger="every 5s">
Status: checking...
</div>
Server Responses (Python/Django Example)
# views.py — server returns HTML fragments, not JSON
from django.shortcuts import render
from django.http import HttpResponse
def article_list(request):
articles = Article.objects.filter(published=True)[:20]
return render(request, "partials/article_list.html", {"articles": articles})
def create_article(request):
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save()
return render(request, "partials/article_card.html", {"article": article})
return render(request, "partials/article_form.html", {"form": form}, status=422)
def delete_article(request, pk):
Article.objects.filter(pk=pk).delete()
return HttpResponse("") # Empty response removes element with outerHTML swap
<!-- templates/partials/article_card.html — HTML fragment returned by server -->
<article id="article-{{ article.id }}">
<h2>{{ article.title }}</h2>
<p>{{ article.body|truncatewords:30 }}</p>
<button hx-delete="/api/articles/{{ article.id }}"
hx-target="#article-{{ article.id }}"
hx-swap="outerHTML swap:300ms"
hx-confirm="Delete this article?">
Delete
</button>
</article>
Indicators
<!-- templates/indicators.html — loading indicators -->
<!-- Show spinner during request -->
<button hx-get="/slow-endpoint" hx-indicator="#spinner">
Load Data
</button>
<span id="spinner" class="htmx-indicator">Loading...</span>
<!-- CSS (htmx adds .htmx-request class during requests) -->
<style>
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline; }
.htmx-request.htmx-indicator { display: inline; }
</style>
Headers and Request Config
<!-- templates/request-config.html — request customization -->
<!-- Include extra values -->
<button hx-post="/api/vote" hx-vals='{"article_id": 42, "vote": "up"}'>
Upvote
</button>
<!-- Include values from other elements -->
<input id="search-input" name="q" />
<button hx-get="/search" hx-include="#search-input">Search</button>
<!-- Push URL to browser history -->
<a hx-get="/articles/my-article" hx-push-url="true" hx-target="#content">
My Article
</a>
Server-Sent Events
<!-- templates/sse.html — real-time updates with SSE -->
<div hx-ext="sse" sse-connect="/events/articles">
<div sse-swap="newArticle" hx-swap="afterbegin">
<!-- New articles appear here in real-time -->
</div>
</div>
# views.py — SSE endpoint
import json
from django.http import StreamingHttpResponse
def article_events(request):
def event_stream():
for article in listen_for_new_articles():
html = render_to_string("partials/article_card.html", {"article": article})
yield f"event: newArticle\ndata: {html}\n\n"
return StreamingHttpResponse(event_stream(), content_type="text/event-stream")
WebSocket
<!-- templates/ws.html — WebSocket integration -->
<div hx-ext="ws" ws-connect="/ws/chat">
<div id="messages"></div>
<form ws-send>
<input name="message" placeholder="Type a message..." />
<button type="submit">Send</button>
</form>
</div>
Boosting (Progressive Enhancement)
<!-- templates/boost.html — make regular links/forms use AJAX -->
<body hx-boost="true">
<!-- All links and forms in this body now use AJAX -->
<nav>
<a href="/articles">Articles</a> <!-- AJAX navigation -->
<a href="/about">About</a>
</nav>
<main id="content">
<!-- Content swapped here -->
</main>
</body>
Key Patterns
- Server returns HTML fragments, not JSON — this is hypermedia, not REST
- Use
hx-targetto control where responses are inserted;hx-swapcontrols how - Use
hx-triggerwith modifiers (delay,throttle,changed,once) for precise control - Use
hx-boost="true"on<body>for easy progressive enhancement of existing sites - Use
hx-swap-oobfor updating multiple page sections from a single response - Use
hx-indicatorfor loading states — htmx manages the CSS class automatically - Use
hx-push-urlto update browser URL for back-button support
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zoho
Integrate and automate Zoho products. Use when a user asks to work with Zoho CRM, Zoho Books, Zoho Desk, Zoho Projects, Zoho Mail, or Zoho Creator, build custom integrations via Zoho APIs, automate workflows with Deluge scripting, sync data between Zoho apps and external systems, manage leads and deals, automate invoicing, build custom Zoho Creator apps, set up webhooks, or manage Zoho organization settings. Covers Zoho CRM, Books, Desk, Projects, Creator, and cross-product integrations.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> zipkin
Deploy and configure Zipkin for distributed tracing and request flow visualization. Use when a user needs to set up trace collection, instrument Java/Spring or other services with Zipkin, analyze service dependencies, or configure storage backends for trace data.