DEV Community

Alex Spinov
Alex Spinov

Posted on

htmx Has a Free API: Add AJAX, WebSockets, and SSE to HTML Without JavaScript

htmx lets you build modern interactive web applications using HTML attributes instead of JavaScript. It adds AJAX requests, CSS transitions, WebSockets, and Server-Sent Events directly in HTML.

Why htmx Matters

React, Vue, and Svelte require complex JavaScript build toolchains. htmx gives you the same interactivity with zero JavaScript — just HTML attributes.

What you get for free:

  • AJAX requests from any HTML element
  • Partial page updates (swap HTML fragments)
  • CSS transitions and animations
  • WebSocket and SSE support
  • Form validation and error handling
  • History management (back/forward)
  • 14KB gzipped, zero dependencies

Quick Start

<script src="https://unpkg.com/htmx.org@2.0.0"></script>

<!-- Click button, load content into div -->
<button hx-get="/api/users" hx-target="#user-list" hx-swap="innerHTML">
  Load Users
</button>
<div id="user-list"></div>

<!-- Submit form without page reload -->
<form hx-post="/api/users" hx-target="#result" hx-swap="innerHTML">
  <input name="name" placeholder="Name" required />
  <input name="email" type="email" placeholder="Email" required />
  <button type="submit">Create User</button>
</form>
<div id="result"></div>
Enter fullscreen mode Exit fullscreen mode

Common Patterns

<!-- Delete with confirmation -->
<button hx-delete="/api/users/123"
        hx-confirm="Delete this user?"
        hx-target="closest tr"
        hx-swap="outerHTML swap:0.5s">
  Delete
</button>

<!-- Infinite scroll -->
<div hx-get="/api/posts?page=2"
     hx-trigger="revealed"
     hx-swap="afterend">
  Loading more...
</div>

<!-- Search with debounce -->
<input type="search"
       name="q"
       hx-get="/api/search"
       hx-trigger="keyup changed delay:300ms"
       hx-target="#results" />
<div id="results"></div>

<!-- Active search indicator -->
<input hx-get="/api/search"
       hx-trigger="keyup changed delay:300ms"
       hx-indicator="#spinner" />
<span id="spinner" class="htmx-indicator">Searching...</span>
Enter fullscreen mode Exit fullscreen mode

Server-Sent Events

<div hx-ext="sse" sse-connect="/api/events">
  <div sse-swap="notification">
    <!-- New notifications appear here in real-time -->
  </div>
  <div sse-swap="score-update">
    <!-- Live score updates -->
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

WebSockets

<div hx-ext="ws" ws-connect="/chat">
  <div id="messages"></div>
  <form ws-send>
    <input name="message" />
    <button>Send</button>
  </form>
</div>
Enter fullscreen mode Exit fullscreen mode

Backend (any language works)

# Flask example — just return HTML fragments
from flask import Flask, render_template_string

app = Flask(__name__)

@app.route("/api/users")
def get_users():
    users = db.query(User).all()
    return render_template_string("""
        {% for user in users %}
        <tr>
          <td>{{ user.name }}</td>
          <td>{{ user.email }}</td>
          <td>
            <button hx-delete="/api/users/{{ user.id }}"
                    hx-target="closest tr"
                    hx-swap="outerHTML">Delete</button>
          </td>
        </tr>
        {% endfor %}
    """, users=users)
Enter fullscreen mode Exit fullscreen mode

Links


Building interactive web apps? Check out my developer tools on Apify or email spinov001@gmail.com for custom solutions.

Top comments (0)