XMLHttpRequest is the older browser request API and remains important in legacy code and upload-progress scenarios. open configures method and URL, event handlers observe state, and send starts the operation. New promise-based code usually reads more clearly with fetch.
XMLHttpRequest (XHR) is a built-in browser API for HTTP requests without a full-page reload. It supports progress events and several response types, not only XML. Prefer fetch for new promise-based request code unless XHR progress or an existing contract is required.
// 1. Create the XHR object
const xhr = new XMLHttpRequest();
// 2. Configure: open(method, url, async)
// method: 'GET', 'POST', 'PUT', 'DELETE', etc.
// url: the endpoint to call
// async: true (default) = non-blocking
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
// 3. Set up the response handler
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
const post = JSON.parse(xhr.responseText);
console.log('Title:', post.title);
} else {
console.error('Request failed with status:', xhr.status);
}
};
// 4. Handle network errors
xhr.onerror = function () {
console.error('Network error occurred');
};
// 5. Send the request
xhr.send();
The readyState property tracks the state of the XHR request through its lifecycle. It changes from 0 to 4 as the request progresses.
| Value | State | Description |
|---|---|---|
| 0 | UNSENT | Object created, open() not called yet. |
| 1 | OPENED | open() called, request not sent yet. |
| 2 | HEADERS_RECEIVED | send() called, response headers received. |
| 3 | LOADING | Response body is being downloaded. |
| 4 | DONE | Request complete (success or failure). |
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/users', true);
// onreadystatechange fires every time readyState changes
xhr.onreadystatechange = function () {
console.log('readyState:', xhr.readyState);
if (xhr.readyState === XMLHttpRequest.DONE) { // === 4
if (xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
console.log('Data:', data);
} else {
console.error('Error:', xhr.status, xhr.statusText);
}
}
};
xhr.send();
You can set a timeout (in milliseconds) to automatically cancel a request that takes too long. The abort() method lets you cancel a request programmatically.
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/slow-endpoint', true);
// Cancel request if it takes more than 5 seconds
xhr.timeout = 5000;
xhr.ontimeout = function () {
console.warn('Request timed out after 5 seconds');
};
// Send cookies and auth headers with cross-origin requests
xhr.withCredentials = true;
xhr.onload = function () {
if (xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
// Manually cancel the request (e.g., user navigates away)
document.getElementById('cancel-btn').addEventListener('click', () => {
xhr.abort();
console.log('Request aborted');
});
const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/upload', true);
// Track download progress
xhr.onprogress = function (event) {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
console.log(`Download: ${percent}%`);
}
};
// Track upload progress
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
document.getElementById('progress-bar').style.width = percent + '%';
}
};
xhr.onload = () => console.log('Upload complete:', xhr.status);
xhr.onerror = () => console.error('Upload failed');
const formData = new FormData(document.getElementById('upload-form'));
xhr.send(formData);
Prefer load, error, timeout, abort, and progress events over manually interpreting every readyState transition. A load event means an HTTP response arrived, not that its status represents success. Check status before parsing responseText or using the configured responseType.
Set a timeout and call abort when a request is obsolete. Register handlers before send, avoid reusing one XHR object for overlapping operations, and do not use synchronous XHR on the main thread because it blocks interaction and is restricted in modern environments.
Explore 500+ free tutorials across 20+ languages and frameworks.