jQuery.ajax remains relevant in applications that already depend on jQuery, but adding the library only for HTTP requests is rarely necessary in modern browsers. Its jqXHR interface, global events, converters, and status handling differ from fetch, so maintenance code should follow one style consistently.
jQuery provides a set of convenient methods that wrap XMLHttpRequest with a simpler API. While the native fetch() API has largely replaced jQuery AJAX in modern projects, jQuery AJAX is still widely used in legacy codebases and WordPress themes.
// Full $.ajax() with all common options
$.ajax({
url: '/api/users',
type: 'POST', // HTTP method
data: JSON.stringify({ name: 'Alice', email: 'alice@example.com' }),
contentType: 'application/json', // request Content-Type
dataType: 'json', // expected response type (auto-parses)
headers: {
'Authorization': 'Bearer my-token'
},
timeout: 10000, // 10 second timeout
beforeSend: function (xhr) {
// Runs before the request is sent
$('#spinner').show();
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
},
success: function (data, textStatus, xhr) {
console.log('Success:', data);
console.log('Status:', textStatus); // "success"
},
error: function (xhr, textStatus, errorThrown) {
console.error('Error:', textStatus, errorThrown);
console.error('Response:', xhr.responseText);
},
complete: function (xhr, textStatus) {
// Always runs - success or error
$('#spinner').hide();
}
});
// $.get(url, data, callback, dataType)
$.get('/api/posts', { page: 1 }, function (data) {
console.log('Posts:', data);
}, 'json');
// $.post(url, data, callback, dataType)
$.post('/api/posts', { title: 'Hello', body: 'World' }, function (data) {
console.log('Created:', data);
}, 'json');
// $.getJSON - shorthand for GET with JSON response
$.getJSON('/api/users', { active: true }, function (users) {
users.forEach(u => console.log(u.name));
});
// $.load() - load HTML directly into a DOM element
$('#content').load('/partials/sidebar.html', function (response, status) {
if (status === 'error') {
console.error('Failed to load partial');
}
});
// Using Promise-style .done(), .fail(), .always()
$.get('/api/data')
.done(data => console.log('Done:', data))
.fail((xhr, status, err) => console.error('Fail:', err))
.always(() => console.log('Always runs'));
// Set defaults for ALL subsequent $.ajax() calls
$.ajaxSetup({
headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') },
dataType: 'json',
timeout: 15000
});
// Global AJAX event handlers (attach to document)
$(document)
.ajaxStart(function () {
// Fires when the FIRST AJAX request begins
$('#global-spinner').show();
})
.ajaxStop(function () {
// Fires when ALL AJAX requests have completed
$('#global-spinner').hide();
})
.ajaxError(function (event, xhr, settings, error) {
console.error(`AJAX error on ${settings.url}:`, error);
});
| Feature | jQuery $.ajax() | Native fetch() |
|---|---|---|
| Dependency | Requires jQuery (~30KB) | Built into browser |
| Browser support | IE6+ | Modern browsers (IE not supported) |
| Promise-based | Deferred (not native Promise) | Native Promise |
| Auto JSON parse | Yes (dataType: 'json') | Manual: res.json() |
| HTTP error rejection | Yes (error callback) | No - must check res.ok |
| Global events | ajaxStart/ajaxStop | Not built-in |
| Streaming | No | Yes (ReadableStream) |
// jQuery Deferred objects are "thenable" - compatible with async/await
async function loadUser(id) {
try {
// $.get() returns a jQuery Deferred which works with await
const user = await $.get(`/api/users/${id}`);
console.log('User:', user.name);
return user;
} catch (xhr) {
console.error('Failed:', xhr.status, xhr.statusText);
}
}
// Or wrap in a native Promise for full compatibility
function jqueryToPromise(jqXHR) {
return new Promise((resolve, reject) => {
jqXHR.done(resolve).fail(reject);
});
}
async function loadPost(id) {
try {
const post = await jqueryToPromise($.get(`/api/posts/${id}`));
console.log(post.title);
} catch (err) {
console.error(err);
}
}
Set dataType for the expected response and contentType for the request body; they describe different directions. processData controls whether jQuery turns data into a query string. For FormData, leave the browser to generate the multipart boundary and configure the jQuery options accordingly.
Use done, fail, and always or the Promise-like methods consistently, and inspect textStatus plus the HTTP response. Avoid global ajaxStart/ajaxError handlers for feature-specific state because unrelated requests can trigger them and create confusing spinners or messages.
Explore 500+ free tutorials across 20+ languages and frameworks.