What is the difference between XMLHttpRequest and fetch?
JavaScriptThe short answer
Both make HTTP requests from the browser, but fetch is the modern replacement. fetch uses promises (cleaner syntax), has a simpler API, and supports streaming. XMLHttpRequest uses callbacks, is more verbose, but supports upload progress events and request cancellation (without AbortController). Use fetch for everything unless you specifically need XHR features.
fetch — the modern way
const response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'John' }),});const data = await response.json();XMLHttpRequest — the old way
const xhr = new XMLHttpRequest();xhr.open('POST', '/api/users');xhr.setRequestHeader('Content-Type', 'application/json');xhr.onload = function () { const data = JSON.parse(xhr.responseText);};xhr.onerror = function () { console.log('Error');};xhr.send(JSON.stringify({ name: 'John' }));Key differences
| Feature | fetch | XMLHttpRequest |
|---|---|---|
| Syntax | Promise-based | Callback-based |
| Response handling | .json(), .text(), .blob() | Manual parsing |
| Error handling | .catch() or try/catch | onerror callback |
| Streaming | Yes (ReadableStream) | No |
| Upload progress | No (need XMLHttpRequest) | Yes (xhr.upload.onprogress) |
| Cancellation | AbortController | xhr.abort() |
| Cookies | Not sent by default (credentials) | Sent by default |
| HTTP errors | Does not reject on 404/500 | Triggers onload for all HTTP responses |
When XHR is still useful
The main reason to use XMLHttpRequest today is upload progress:
const xhr = new XMLHttpRequest();xhr.upload.addEventListener('progress', (event) => { const percent = (event.loaded / event.total) * 100; console.log(`${percent}% uploaded`);});fetch does not have a built-in way to track upload progress (though you can track download progress with ReadableStream).
The fetch gotcha
fetch does not reject on HTTP errors like 404 or 500:
// This does NOT throw for 404const response = await fetch('/not-found');response.ok; // falseresponse.status; // 404// You need to check manuallyif (!response.ok) { throw new Error(`HTTP ${response.status}`);}Interview Tip
Show both APIs with the same request to highlight the difference in verbosity. Mention the response.ok gotcha with fetch — this is a common interview follow-up. Knowing that XHR is still useful for upload progress shows you have real experience with file uploads.
Why interviewers ask this
This tests your knowledge of browser APIs and web development history. Interviewers want to see if you know the modern approach (fetch) and understand its limitations compared to the older API.