How to Programmatically Trigger htmx Events: A Complete Guide to the `htmx.trigger` API
Use the htmx.trigger(elt, eventName, detail?) API to fire any htmx-compatible event from JavaScript, which internally delegates to the triggerEvent function in src/htmx.js to ensure full compatibility with the declarative hx-trigger pipeline.
The bigskysoftware/htmx library exposes a tiny but powerful helper called htmx.trigger that enables you to programmatically dispatch events. Because this function reuses the exact same internal code path as the declarative hx-trigger attribute, any listener that responds to htmx-generated events will also receive your programmatically fired events.
How htmx.trigger Works Internally
When you invoke htmx.trigger(elt, eventName, detail?), the function executes a seven-step pipeline defined in src/htmx.js around lines 3092–3126. Understanding this flow helps you predict how extensions and error handlers will react:
- Target resolution – the supplied
elt(either a DOM element or a CSS selector string) is resolved to an actual element via the internalresolveTargetfunction. - Event construction – an
Eventobject is created withmakeEvent(eventName, detail); the detail object automatically receives the resolved element under theeltkey. - Logging – if a logger is configured, the event is logged unless the event name appears on the internal ignore list.
- Error handling – if
detail.erroris present, anhtmx:errorevent is automatically emitted regardless of the primary event type. - Dispatch – the event is dispatched on the element using
elt.dispatchEvent(event). If the event name contains capital letters, a kebab-cased version is also dispatched so listeners can subscribe to either form. - Extension hooks – any loaded htmx extensions receive the event via
extension.onEvent; returningfalseor callingevent.preventDefault()will stop further processing. - Result – the Boolean result of the dispatch (accounting for extension handling) is returned, allowing you to detect whether the event was cancelled.
This architecture ensures that programmatic triggers behave identically to user-initiated ones, including proper lifecycle management and extension integration.
Triggering Built-in htmx Lifecycle Events
You can manually fire standard htmx events like htmx:beforeRequest or htmx:afterSwap to simulate user interactions or force state updates. The following example fires the post-swap event on a button element:
// Assume there is a button with id="saveBtn"
const btn = document.getElementById('saveBtn')
// Fires the same event that would be emitted after a successful swap
htmx.trigger(btn, 'htmx:afterSwap')
Because htmx.trigger uses the same internal pipeline as declarative triggers, all standard lifecycle hooks—including request indicators, error handling, and swap animations—execute normally.
Firing Custom Events with Data Payloads
For component-to-component communication, pass a detail object as the third argument. The detail payload merges with the internal elt reference, giving listeners access to both your custom data and the target element:
// Custom event name can be any string
htmx.trigger('#myDiv', 'my:customEvent', { userId: 42, action: 'login' })
// Listener that reacts to the custom event
document.body.addEventListener('my:customEvent', function (evt) {
console.log('Custom event data:', evt.detail) // → {userId:42,action:"login",elt:#myDiv}
})
This pattern is particularly useful when building micro-frontends or when you need to signal state changes across disconnected DOM subtrees.
Advanced Usage Patterns
Using CSS Selectors Instead of DOM References
The elt parameter accepts CSS selector strings, which htmx.trigger resolves internally using the same resolveTarget logic used throughout the library:
// htmx will resolve the selector internally
htmx.trigger('.list-item:first-child', 'click')
This eliminates the need to manually query the DOM before triggering events.
Checking Event Cancellation Status
The function returns a Boolean indicating whether the event completed successfully or was halted by preventDefault() or an extension returning false:
const wasHandled = htmx.trigger('#form', 'htmx:beforeRequest')
if (!wasHandled) {
console.warn('A listener called preventDefault(); the request will not be sent')
}
Use this return value to implement fallback logic when event listeners block operations.
Triggering from Within Extensions
Extensions can emit events using the same API. The event flows through extension.onEvent hooks just like native htmx events:
// Inside a custom extension
htmx.defineExtension('myExt', {
onEvent: function(name, evt) {
if (name === 'htmx:afterSwap') {
// do something after every swap
htmx.trigger(evt.detail.elt, 'my:postSwap')
}
}
})
This enables extension authors to create custom lifecycle hooks that other extensions or page scripts can listen for.
Key Implementation Files
The following files contain the authoritative source for programmatic event triggering in htmx:
src/htmx.js(lines 3092–3126) – Contains the coretriggerEventimplementation and the publichtmx.triggeralias. This is where target resolution, event construction, and extension hook invocation occur.dist/htmx.js– The bundled/minified production version that includes the samehtmx.triggerfunctionality for browser environments.test/core/events.js– The test suite that verifies programmatic triggering behavior across releases, ensuring API stability.
By using htmx.trigger, you tap directly into this proven pipeline rather than relying on raw dispatchEvent calls that might bypass htmx-specific logic like extension handling and dual-case event naming.
Summary
htmx.trigger(elt, name, detail?)is the canonical API for firing htmx events from JavaScript, implemented insrc/htmx.js.- The function accepts either DOM elements or CSS selector strings for the target parameter.
- Event detail objects automatically include the resolved element under the
eltkey, and handlers receive both camelCase and kebab-case event names when applicable. - The Boolean return value indicates whether the event was cancelled via
preventDefault()or extension interception. - Use this method for testing, cross-component communication, and simulating user interactions while maintaining full compatibility with htmx extensions.
Frequently Asked Questions
What is the difference between htmx.trigger and native dispatchEvent?
While dispatchEvent fires a raw DOM event, htmx.trigger wraps the native call with htmx-specific logic including automatic logger integration, extension hook broadcasting via extension.onEvent, and dual-format event naming (camelCase and kebab-case). According to the source code in src/htmx.js, it also automatically populates the elt property in the event detail and handles error event emission when detail.error is present.
Can I trigger htmx requests programmatically without user interaction?
Yes. You can trigger the htmx:trigger or click events on elements with hx-get, hx-post, or similar attributes to initiate network requests. For example, htmx.trigger('#loadBtn', 'click') will cause htmx to process the element's AJAX attributes exactly as if the user had clicked it, including executing confirmation handlers and indicator CSS classes.
How do I pass data to event listeners when using htmx.trigger?
Supply a plain JavaScript object as the third argument. The internal makeEvent function merges your properties into event.detail, alongside the automatic elt reference to the target element. Listeners access this data via evt.detail. This works for both built-in htmx events and custom event names you define.
Does htmx.trigger work with kebab-case event names?
Yes. The implementation in src/htmx.js detects capital letters in the event name and automatically dispatches a kebab-cased variant. For example, triggering myCustomEvent will also fire my-custom-event, allowing listeners to subscribe to whichever naming convention they prefer while ensuring backward compatibility.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →