Triggers, forms, and feedback

Use trigger modifiers

Debounce input, suppress duplicate values, limit frequency, filter events, or allow a trigger only once.

Search-as-you-type is the textbook case for trigger modifiers. Without them, every keyup fires a request:

<input type="search" name="q"
  hx-get="/search"
  hx-trigger="keyup changed delay:500ms, search"
  hx-target="#results"
  hx-sync="this:replace">

changed suppresses requests when the value is identical to the last one sent. delay:500ms debounces: each new keyup resets the timer, so typing “tasks” sends one request after you pause, not five.

The search event also handles clearing the native search field with the × button.

Debouncing does not cancel a request already in flight. hx-sync="this:replace" aborts the older search when a newer one starts. That stops a slow old response from overwriting fresh results. I see that bug often on fast typists with slow servers.

Other modifiers solve different problems:

  • throttle:500ms allows at most one request per interval
  • once accepts only the first matching event
  • from:body listens on another element
  • target:<selector> filters events by their original target
  • queue:first, queue:last, or queue:all controls events arriving during a request

Use the smallest set that expresses a real timing requirement. More modifiers mean harder debugging.

The once modifier is handy for analytics or “load on first reveal” patterns where repeat events should not refetch. throttle differs from delay: throttle caps frequency, delay waits for quiet time. Pick the one that matches user behavior.

Enable Network throttling, type quickly in a search field, and confirm the final #results content always matches the current input. If stale results appear, add or tighten hx-sync. Log the trigger string in htmx:beforeRequest while debugging. It shows exactly which clause fired.

Lesson completed