Skip to content

HTMX Triggers — Complete Guide with Examples

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you'll learn about HTMX triggers. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

HTMX triggers define what event initiates an HTTP request, supporting standard DOM events, custom events, polling intervals, and trigger modifiers like debounce, throttle, and delay.

What You'll Learn

By the end of this tutorial, you'll configure click, load, input, every, and custom triggers, use modifiers like delay and throttle, combine multiple triggers, and filter events with CSS selectors.

Why It Matters

Choosing the right trigger makes your UI feel responsive. A search that fires on every keystroke overwhelms the server. A search that fires 300ms after typing stops feels snappy. Polling every 5 seconds keeps dashboards fresh without excessive requests.

Real-World Use

Durga Antivirus Pro's real-time dashboard uses multiple triggers: every 5 seconds for auto-refresh, mouseenter for tooltip previews, load for initial data, and custom events for inter-component communication.

Where This Fits in Your Learning Path

flowchart LR
    A["HTMX Targets"] --> B["**HTMX Triggers**"]
    B --> C["Swapping & Transitions"]
    C --> D["Synchronization"]
    D --> E["Advanced HTMX"]
    style B fill:#3b82f6,stroke:#2563eb,color:#fff
    style A fill:#e2e8f0,stroke:#94a3b8
    style E fill:#e2e8f0,stroke:#94a3b8

Standard Triggers

HTMX uses standard DOM events as triggers.

<!-- Click (default for buttons, anchors, form submits) -->
<button hx-get="/api/click">Click me</button>

<!-- Double click -->
<div hx-get="/api/double-click" hx-trigger="dblclick">Double-click me</div>

<!-- Focus -->
<input hx-get="/api/autocomplete" hx-trigger="focus" hx-target="#suggestions">

<!-- Blur (lost focus) -->
<input hx-post="/api/validate" hx-trigger="blur" hx-target="#validation">

Expected output: Each element fires its request on the specified DOM event.

The "load" Trigger

Fires the request immediately when the element is loaded into the DOM.

<!-- Lazy load content on page load -->
<div hx-get="/api/sidebar" hx-trigger="load">
  Loading sidebar...
</div>

<!-- Lazy load when an element is added dynamically -->
<div hx-get="/api/comments" hx-trigger="load" hx-target="#comments">
  Loading comments...
</div>
<div id="comments"></div>

Expected output: The request fires as soon as the element is parsed. The element shows "Loading..." until the response arrives.

The "every" Trigger (Polling)

Creates a polling interval that fires the request repeatedly.

<!-- Poll every 5 seconds -->
<div hx-get="/api/status" hx-trigger="every 5s">
  Status updated every 5 seconds
</div>

<!-- Poll with different interval -->
<div hx-get="/api/clock" hx-trigger="every 1s" hx-target="#clock">
  <span id="clock"></span>
</div>

<!-- Polling that stops on certain conditions -->
<div hx-get="/api/job-status" hx-trigger="every 2s" hx-target="#job-result">
  Checking job status...
</div>

Expected output: The request fires repeatedly at the specified interval. The clock updates every second, status every 5 seconds.

The "input" and "change" Triggers

Input triggers respond to user input events.

<!-- Fire on every input change (with debounce) -->
<input hx-get="/api/search"
       hx-trigger="input changed delay:300ms"
       hx-target="#results"
       placeholder="Type to search">

<!-- Fire only when value actually changes (not same as previous) -->
<input hx-get="/api/validate"
       hx-trigger="change"
       hx-target="#validation-message">

<!-- Input with throttle (limit frequency) -->
<input hx-get="/api/live-preview"
       hx-trigger="input throttle:1s"
       hx-target="#preview">

Expected output: The search debounces (waits 300ms after last keystroke). The change trigger fires only when the user blurs with a different value.

Trigger Modifiers

Modifiers refine trigger behavior.

<!-- once: fire only one time -->
<button hx-get="/api/one-time" hx-trigger="click once">
  Can only click once
</button>

<!-- delay: wait before firing -->
<button hx-get="/api/delayed" hx-trigger="click delay:500ms">
  Waits 500ms before firing
</button>

<!-- throttle: limit frequency -->
<button hx-post="/api/rapid-click"
        hx-trigger="click throttle:2s">
  Max once per 2 seconds
</button>

<!-- from: listen on a different element -->
<button hx-get="/api/proxy"
        hx-trigger="click from:#submit-btn"
        hx-target="#result">
  Triggered by #submit-btn
</button>

Expected output: once prevents double-clicks. delay adds a wait. throttle limits frequency. from listens on a different element.

Custom Events

HTMX listens to custom DOM events as triggers.

<div hx-get="/api/data-refresh"
     hx-trigger="data-updated from:body">
  Listens for custom data-updated event
</div>

<button onclick="document.dispatchEvent(new CustomEvent('data-updated'))">
  Trigger refresh from anywhere
</button>

<!-- Filtered custom event -->
<div hx-get="/api/notification"
     hx-trigger="notify[type=='success']">
  Only fires for success notifications
</div>

Expected output: The first div reacts to the custom 'data-updated' event dispatched anywhere on the page.

Combining Multiple Triggers

Separate multiple triggers with commas.

<!-- Fire on click OR load -->
<button hx-get="/api/data" hx-trigger="click, load">
  Loads on click and on page load
</button>

<!-- Input with both change and search -->
<input hx-get="/api/search"
       hx-trigger="input changed delay:300ms, search"
       hx-target="#results">

<!-- Polling with manual refresh -->
<div hx-get="/api/dashboard"
     hx-trigger="every 30s, click from:#refresh-btn">
  Dashboard auto-refresh
</div>

Expected output: Multiple triggers mean the request fires on any of the specified conditions.

Common Mistakes

1. Using "change" instead of "input" for real-time updates

change fires on blur, not on every keystroke. Use "input changed delay:300ms" for live search.

2. Forgetting the "changed" keyword with input

"input" without "changed" fires on every keystroke including the same value. "input changed" fires only when the value differs.

3. Polling too frequently

Every 1s polling on many elements can overwhelm the server. Use 5s or longer for most use cases.

4. Not using "once" for one-time actions

Delete buttons without once can fire multiple times if clicked rapidly, causing server errors.

5. Using "from" with a non-existent selector

The from modifier waits for events on a specific element. If the element doesn't exist, the trigger never fires.

Practice Questions

  1. What is the default trigger for buttons? Click. Buttons fire the request on click by default.

  2. How do you create a debounced search? Use hx-trigger="input changed delay:300ms" on the search input.

  3. What does the "once" modifier do? The request fires only one time, then the trigger is removed.

  4. How do you poll every 10 seconds? Use hx-trigger="every 10s" on the element.

  5. What is the "from" modifier used for? It listens for the trigger event on a different element, not the one with the HTMX attribute.

Challenge

Build a dashboard with multiple triggers: initial data loads on page load, auto-refreshes every 30 seconds, has a manual refresh button, and handles a custom "data-updated" event from other components.

FAQ

Can I use keyboard events as triggers?

Yes. Use keydown, keyup, or keypress as the trigger value. Filter by key using CSS selector filters.

What is the difference between delay and throttle?

delay resets the timer on each event (debounce). throttle fires at most once per interval regardless of events.

Can triggers be nested?

Triggers are per-element. Use event bubbling or the from modifier for nested trigger patterns.

How do I stop polling?

Use hx-trigger='every 5s' with a condition. Return HX-Trigger response header to stop polling from the server.

Can I use touch events?

Yes. Use touchstart, touchend, or custom touch events as triggers.


Mini Project

Build a real-time monitoring dashboard. Use multiple triggers: load for initial data fetch, every 5s for auto-refresh, mouseenter for detail tooltips, and a custom event for inter-widget communication.

<div hx-get="/api/dashboard/header" hx-trigger="load" hx-target="#header">
  <div id="header">Loading header...</div>
</div>

<div hx-get="/api/dashboard/metrics"
     hx-trigger="every 5s, data-refreshed from:body"
     hx-target="#metrics">
  <div id="metrics">Loading metrics...</div>
</div>

<div hx-get="/api/dashboard/alerts"
     hx-trigger="every 10s, click from:#refresh-alerts"
     hx-target="#alerts">
  <div id="alerts">
    <span hx-get="/api/alerts/tooltip"
          hx-trigger="mouseenter"
          hx-target="next .tooltip">
      Alerts
    </span>
    <div class="tooltip"></div>
  </div>
  <button id="refresh-alerts">Refresh Now</button>
</div>

<button onclick="document.dispatchEvent(new CustomEvent('data-refreshed'))">
  Force Refresh All
</button>

What's Next

Master swapping strategies:

Tutorial What You'll Learn
HTMX Swapping Swap strategies and CSS transition animations
HTMX Synchronization Coordinate multiple HTMX requests

Related topics: DOM events reference, debounce and throttle patterns.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro