Skip to content
FLAVIO COPES
flaviocopes.com
2026

XMLHttpRequest (XHR)

By

Learn how XMLHttpRequest works, why Fetch is the default for new code, and when XHR is still useful for upload progress.

~~~

Introduction

XMLHttpRequest, usually called XHR, lets JavaScript make HTTP requests without reloading the page.

XHR is a legacy API, but it remains widely supported. The Fetch API is the better default for new code. XHR remains useful when you need upload progress events or maintain older code that already uses it.

XHR was invented at Microsoft in the 1990s. Apps like Gmail and Google Maps later showed what pages could do without full reloads.

Libraries like jQuery became popular partly because they hid early browser differences behind a simpler API.

An example XHR request

The following code creates an XHR object and makes an asynchronous GET request:

const xhr = new XMLHttpRequest()

xhr.addEventListener('load', () => {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log(xhr.responseText)
  } else {
    console.error(`HTTP error: ${xhr.status}`)
  }
})

xhr.addEventListener('error', () => {
  console.error('Network error')
})

xhr.open('GET', '/file.txt')
xhr.send()

Additional open() parameters

In the example above, we passed the method and URL to open().

XHR supports other HTTP methods, including POST, PUT, PATCH, and DELETE.

The third parameter controls whether the request is asynchronous:

xhr.open('GET', '/file.txt', true)

It defaults to true. Do not set it to false in page code. Synchronous XHR is deprecated because it blocks the main thread and can make the page freeze.

onreadystatechange

Older XHR code often uses readystatechange. The event fires several times as the request moves through its states.

The states are

For new XHR code, load, error, abort, and timeout events are usually clearer.

Aborting an XHR request

An XHR request can be aborted by calling the abort() method on the xhr object.

Comparison with jQuery

With jQuery these lines can be translated to:

$.get('https://yoursite.com', (data) => {
  console.log(data)
}).fail((err) => {
  console.error(err)
})

Comparison with Fetch

With the Fetch API this is the equivalent code:

fetch('/file.txt')
  .then((response) => {
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`)
    }
    return response.text()
  })
  .then((text) => console.log(text))
  .catch((error) => console.error(error))

Cross Domain Requests

XHR follows the same-origin policy. To read a response from another origin, that server must allow your origin using CORS (Cross-Origin Resource Sharing).

Uploading files using XHR

XHR can report upload progress through its upload object. This is one reason it is still used:

xhr.upload.addEventListener('progress', (event) => {
  if (event.lengthComputable) {
    const percent = Math.round((event.loaded / event.total) * 100)
    console.log(`${percent}% uploaded`)
  }
})

See the MDN XHR progress-event guide and my tutorial on how to upload files using XHR.

Tagged: Web Platform ยท All topics
~~~

Related posts about platform: