HTMX hx-trigger

Every HTMX request needs something to start it — a user action, a timer, or a page event. The hx-trigger attribute defines exactly what that starting signal is. Mastering hx-trigger lets you fire requests on any interaction: clicks, key presses, mouse movements, form changes, timers, and even custom JavaScript events.

Default Triggers by Element

HTMX picks a sensible default trigger for each element type when you omit hx-trigger:

ElementDefault Trigger
button, a, input[type=submit]click
input, textarea, selectchange
formsubmit
All others (div, span, etc.)click

Basic Syntax

<button hx-get="/data" hx-trigger="click">Load Data</button>

This is the same as omitting hx-trigger on a button — HTMX defaults to click anyway. But writing it explicitly makes your intent clear.

Common Trigger Events

hx-trigger="click"       → fires on mouse click
hx-trigger="change"      → fires when input value changes
hx-trigger="keyup"       → fires on every key release
hx-trigger="submit"      → fires on form submit
hx-trigger="mouseover"   → fires when mouse enters the element
hx-trigger="load"        → fires when the element loads into the DOM
hx-trigger="revealed"    → fires when element scrolls into view

Trigger Modifiers

You can refine any trigger with modifiers separated by spaces:

delay — Wait Before Firing

<input
  type="text"
  name="q"
  hx-get="/search"
  hx-trigger="keyup delay:400ms"
  hx-target="#results">

The request fires 400ms after the user stops typing. If they type again before 400ms pass, the timer resets. This prevents a flood of requests with every key press.

  Diagram:
  User types: "l" → "la" → "lap" → "lapt" → "lapto" → "laptop"

  WITHOUT delay:  6 requests fired
  WITH delay:400ms:  1 request fired (after user pauses)

changed — Only Fire When Value Changed

<input
  type="text"
  name="email"
  hx-post="/validate-email"
  hx-trigger="change changed"
  hx-target="#email-error">

The changed modifier makes HTMX compare the current value to the last sent value. If the value did not change (for example, the user clicked elsewhere without editing), no request fires.

once — Fire Only the First Time

<div hx-get="/welcome-offer" hx-trigger="load once" hx-target="this">
  Loading offer...
</div>

After the first trigger fires, HTMX removes the listener. The request never fires again — even if the element is clicked, reloaded, or hovered.

throttle — Limit Request Rate

<div
  hx-get="/mouse-position"
  hx-trigger="mousemove throttle:200ms"
  hx-target="#coords">
</div>

Unlike delay, throttle fires immediately on the first event and then ignores further events for 200ms. After 200ms, it is ready to fire again. This caps the request rate during rapid, continuous events like mouse movement.

Polling: The "every" Trigger

HTMX includes a special polling syntax that fires requests on a repeating interval:

<div hx-get="/live-score" hx-trigger="every 3s" hx-target="this">
  Score: Loading...
</div>

HTMX sends a GET request to /live-score every 3 seconds and replaces the div's content with the latest score. This creates a live-updating display without WebSockets or Server-Sent Events.

  Timeline:
  0s  → Request 1 → Server returns "Score: 2–1"
  3s  → Request 2 → Server returns "Score: 2–2"
  6s  → Request 3 → Server returns "Score: 3–2"
  9s  → ...

Triggering From Another Element

Use the from: modifier to listen for an event on a different element:

<input id="global-search" type="text" name="q" placeholder="Search...">

<div
  hx-get="/search"
  hx-trigger="keyup delay:400ms from:#global-search"
  hx-target="#search-results">
</div>

<div id="search-results"></div>

The div fires the GET request whenever the user types in #global-search. The trigger element and the HTMX element are separate — a powerful pattern for decoupled components.

Multiple Triggers

You can specify several triggers for the same element by separating them with commas:

<button
  hx-get="/refresh"
  hx-trigger="click, keyup[key=='Enter'] from:body"
  hx-target="#feed">
  Refresh Feed
</button>

The request fires when the button is clicked OR when the Enter key is pressed anywhere on the page.

Keyboard Event Filters

You can filter keyboard events to fire only on a specific key:

<input
  type="text"
  name="search"
  hx-get="/search"
  hx-trigger="keyup[key=='Enter']"
  hx-target="#results">

The request fires only when the user presses the Enter key inside the input — not on every keystroke.

The "revealed" Trigger for Scroll-Based Loading

<div
  hx-get="/more-posts"
  hx-trigger="revealed"
  hx-swap="beforeend"
  hx-target="#post-list">
  Loading more...
</div>

HTMX watches the element. The moment it scrolls into the visible area of the browser, the request fires. This is the simplest way to build infinite scroll — covered in detail in the Infinite Scroll topic.

Full Trigger Modifier Reference

ModifierSyntax ExampleEffect
delaykeyup delay:500msWaits before firing; resets on each new event
throttlemousemove throttle:200msFires immediately, then ignores events for the duration
changedchange changedOnly fires if the value is different from last request
onceload onceFires one time only
from:keyup from:#otherListens on a different element
target:click target:.itemOnly fires if the click happened on a matching child
consumeclick consumePrevents the event from bubbling up after HTMX handles it

Key Takeaway

The hx-trigger attribute gives you fine-grained control over when HTMX fires requests. Use standard DOM events for basic interactions, add modifiers like delay and throttle to manage frequency, use every for polling, and use from: to listen on remote elements. The right trigger makes your app feel responsive and efficient at the same time.

Leave a Comment

Your email address will not be published. Required fields are marked *