# How to Make AJAX Requests Manually with htmx: A Complete Guide to htmx.ajax()

> Learn to make AJAX requests manually with htmx using htmx.ajax(). Trigger full rendering, events, and swaps programmatically without needing hx attributes.

- Repository: [Big Sky Software/htmx](https://github.com/bigskysoftware/htmx)
- Tags: how-to-guide
- Published: 2026-08-30

---

**Use the `htmx.ajax()` method to programmatically issue HTTP requests that trigger htmx’s full rendering pipeline, including events, swap strategies, and out-of-band updates, without requiring declarative `hx-*` attributes.**

The **bigskysoftware/htmx** library provides a low-level JavaScript API for imperative AJAX calls that integrates seamlessly with its declarative HTML-driven workflow. When you need to trigger server requests from JavaScript logic—whether in response to complex user interactions, third-party callbacks, or dynamic state changes—the `htmx.ajax()` helper exposes the same request machinery that powers `hx-get` or `hx-post` attributes.

## Understanding the htmx.ajax() API Architecture

In [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js), the public `htmx.ajax` method is bound at [line 307](https://github.com/bigskysoftware/htmx/blob/master/src/htmx.js#L307) as a thin wrapper around the internal `ajaxHelper` function defined at line 4059:

```js
htmx.ajax = ajaxHelper      // <-- public entry point

```

This architectural choice means programmatic calls reuse htmx’s core `XMLHttpRequest` infrastructure, including runtime configuration for `withCredentials`, global headers, and timeout handling. The helper automatically routes responses through htmx’s internal rendering engine, invoking functions like `swapOuterHTML`, `swapInnerHTML`, and `makeFragment` that power the declarative attribute API.

## Core Capabilities of Programmatic Requests

### Event System Integration

Calls to `htmx.ajax()` trigger the complete htmx event lifecycle, including `htmx:configRequest`, `htmx:beforeRequest`, `htmx:afterOnLoad`, and error events. This ensures extensions and global listeners behave identically regardless of whether the request originated from an HTML attribute or JavaScript code.

### Swap Mechanisms and OOB Support

The method supports all standard swap styles (`innerHTML`, `outerHTML`, `beforebegin`, `afterend`, etc.) and processes **out-of-band (OOB)** swaps automatically. When the server response includes elements marked with `hx-swap-oob`, htmx updates those targets while simultaneously handling the primary swap target specified in your JavaScript call.

### Promise-Based Return Value

`htmx.ajax()` returns a **Promise** that resolves with the raw `XMLHttpRequest` object. This allows you to inspect `status`, `responseText`, and `responseJSON` properties after the DOM update completes, enabling chaining logic that depends on both the HTTP response and the resulting UI state.

## Practical Implementation Examples

### GET Request with Target Selection

Issue a simple GET request and inject the response into a specific DOM element using the default `innerHTML` swap strategy:

```html
<div id="result">Waiting for data…</div>

<script>
  // Load `/info` into #result using the default `innerHTML` swap
  htmx.ajax('GET', '/info', { target: '#result' })
       .then(xhr => console.log('Request finished', xhr.status));
</script>

```

### POST Request with JSON Payload

Send structured data to your backend with custom headers and handle the 201 Created response:

```html
<button id="saveBtn">Save</button>
<div id="msg"></div>

<script>
  document.getElementById('saveBtn').addEventListener('click', () => {
    const data = { name: 'Alice', age: 30 };
    htmx.ajax('POST', '/users', {
      target: '#msg',
      swap: 'innerHTML',
      headers: { 'Content-Type': 'application/json' },
      values: data
    }).then(xhr => {
      if (xhr.status === 201) {
        console.log('User created');
      }
    });
  });
</script>

```

### Executing Code After DOM Updates

Use the Promise resolution to run logic after htmx has completed the swap operation:

```js
htmx.ajax('GET', '/partial')
    .then(xhr => {
      // The swap has already happened at this point
      const newContent = document.querySelector('#partialResult');
      console.log('New content length:', newContent.innerHTML.length);
    });

```

### Manual Response Handling Without Swapping

Prevent automatic DOM updates by setting `swap: 'none'`, then manually process the raw response:

```js
htmx.ajax('GET', '/api/data', { swap: 'none' })
    .then(xhr => {
      const json = JSON.parse(xhr.responseText);
      console.log('Fetched data:', json);
      // Manual DOM manipulation logic here
    });

```

## Summary

- **htmx.ajax()** exposes the internal `ajaxHelper` function from [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js) (line 4059), bound to the public API at line 307, providing imperative access to htmx’s request pipeline.
- The method triggers standard htmx events (`htmx:configRequest`, `htmx:afterOnLoad`, etc.) and supports **OOB swaps**, **history integration**, and **indicator elements** exactly like declarative attributes.
- It returns a **Promise** resolving to the `XMLHttpRequest` object, allowing inspection of HTTP status and response data after the swap completes.
- You can disable automatic swapping with `swap: 'none'` and handle responses manually while still benefiting from htmx’s request configuration and error handling.

## Frequently Asked Questions

### What is the difference between htmx.ajax() and the native fetch() API?

While `fetch()` provides generic HTTP capabilities, `htmx.ajax()` integrates with htmx’s entire ecosystem—it triggers the same events as `hx-get` or `hx-post`, respects global configuration like `withCredentials` and request headers, and processes responses through htmx’s swap engine including OOB updates. Use `htmx.ajax()` when you need htmx-specific functionality; use `fetch()` for standalone data retrieval that doesn’t require htmx’s DOM manipulation pipeline.

### Can I use htmx.ajax() without specifying a target element?

Yes. If you omit the `target` option, htmx will not perform a default swap, but you can still process the response manually via the returned Promise or use `swap: 'none'` to prevent any DOM changes while maintaining access to htmx’s request configuration and event system. This is useful for fire-and-forget requests or when handling the response entirely in JavaScript logic.

### Does htmx.ajax() trigger htmx events that my extensions listen for?

Absolutely. Because `htmx.ajax()` reuses the same internal machinery as attribute-driven requests, it fires `htmx:beforeRequest`, `htmx:configRequest`, `htmx:beforeSwap`, `htmx:afterSwap`, and error events. Extensions registered via `htmx.defineExtension()` will intercept and process these requests exactly as they would standard htmx interactions.

### How do I handle network or HTTP errors with htmx.ajax()?

The Promise returned by `htmx.ajax()` resolves regardless of HTTP status codes (2xx or 4xx/5xx), so you must check `xhr.status` inside the `.then()` handler. For network-level failures (timeouts, connection errors), the Promise rejects and you can attach a `.catch()` handler. Alternatively, listen for the `htmx:responseError` or `htmx:sendError` events globally to centralize error handling across both programmatic and declarative requests.