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:
| Element | Default Trigger |
|---|---|
| button, a, input[type=submit] | click |
| input, textarea, select | change |
| form | submit |
| 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
| Modifier | Syntax Example | Effect |
|---|---|---|
| delay | keyup delay:500ms | Waits before firing; resets on each new event |
| throttle | mousemove throttle:200ms | Fires immediately, then ignores events for the duration |
| changed | change changed | Only fires if the value is different from last request |
| once | load once | Fires one time only |
| from: | keyup from:#other | Listens on a different element |
| target: | click target:.item | Only fires if the click happened on a matching child |
| consume | click consume | Prevents 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.
