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.
What this skill does
# 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 ```html <!-- index.html — add htmx via CDN or npm --> <script src="https://unpkg.com/[email protected]"></script> <!-- Or: npm install htmx.org --> ``` ## Core Attributes ```html <!-- 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 ```html <!-- 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 ```html <!-- 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) ```python # 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 ``` ```html <!-- 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 ```html <!-- 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 ```html <!-- 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 ```html <!-- 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> ``` ```python # 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 ```html <!-- 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) ```html <!-- 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-target` to control where responses are inserted; `hx-swap` controls how - Use `hx-trigger` with modifiers (`delay`, `throttle`, `changed`, `once`) for precise control - Use `hx-boost="true"` on `<body>` for easy progressive enhancement of existing sites - Use `hx-swap-oob` for updating multiple page sections from a single response - Use `hx-indicator` for loading states — htmx manages the CSS class automatically - Use `hx-push-url` to update browser URL for back-button support
Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.