htmx: Build Interactive Web Apps with HTML · Hypermedia Foundations

Triggers and Timing

Build a notifications badge that polls, a section that loads lazily, and a search box that waits for the user to pause.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

Three small features on a dashboard header: a notifications badge that refreshes every few seconds, a slow section that loads after the page appears, and a username check that runs only when the user stops typing.

The idea

hx-trigger says when to send a request. Without it, inputs use change, forms use submit, and everything else uses click. You can use any DOM event plus:

Syntax Meaning
input changed delay:400ms debounce: wait for a pause, skip unchanged values
click throttle:2s at most one request per 2 seconds
load as soon as the element is on the page
revealed / intersect once when the element scrolls into view
every 5s polling
keyup[key=='Enter'] an event with a filter
myEvent from:body an event fired anywhere on the page

The code

Polling, lazy loading and a debounced checkTry it yourself
<p>Notifications: <strong hx-get="/notifications/count" hx-trigger="load, every 3s">0</strong></p>

<div hx-get="/dashboard/stats" hx-trigger="load">Loading statistics...</div>

<label>Choose a username
  <input name="username" hx-get="/username/check" hx-trigger="input changed delay:400ms" hx-target="#name-msg">
</label>
<span id="name-msg"></span>

<!-- The pretend server: on a real site this part is your backend (Python, PHP, Node, Go...) -->
<script>
  let unread = 0;
  server.get("/notifications/count", () => String(unread += Math.random() < 0.5 ? 1 : 0));
  server.get("/dashboard/stats", () => "<p>Orders today: 42 · Revenue: $1,980</p>", { delay: 1500 });
  server.get("/username/check", (req) => {
    const name = (req.query.username || "").trim();
    if (name.length < 3) return "At least 3 characters.";
    return ["admin", "root", "ada"].includes(name.toLowerCase()) ? `"${server.escape(name)}" is taken.` : "Available!";
  });
</script>

Type quickly in the username box and watch the log: one request per pause, not one per key.

Common mistakes

  • hx-trigger="keyup" on a search box without delay: a request per keystroke, and responses can arrive out of order.
  • Polling too often. Every open tab sends requests; choose the slowest interval users accept, or use SSE.
  • Using load for content that's needed immediately; render it in the page instead, and keep load for slow parts.

Key points

  • Pick the trigger that matches the user's intent; debounce typing.
  • load and revealed make slow content lazy.
  • every polls; the server can stop it with status 286.

Further reading: hx-trigger · Tutorial: Triggers

Practice questions

What is the difference between delay:500ms and throttle:500ms?

Answer

delay waits until events stop for 500 ms and sends once (debounce); throttle sends at most once every 500 ms while events keep coming.

Why add the changed modifier to a search input's trigger?

Answer

So no request is sent when the event fires but the value is the same (for example arrow keys or re-typing the same text).

Which trigger makes a slow dashboard section load after the rest of the page appears?

Answer

load: the request fires when htmx processes the element, so the page shows first and the section fills in.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?