How does AJAX work?
The Short Answer
AJAX (Asynchronous JavaScript and XML) is a technique for making HTTP requests from the browser without reloading the page. It lets you fetch data from a server, send form submissions, and update parts of the page dynamically — all in the background while the user continues interacting with the page. Despite the name, modern AJAX almost always uses JSON instead of XML. The technique was revolutionary because before AJAX, every server interaction required a full page reload.
Before AJAX — Full Page Reloads
Before AJAX, web applications worked in a request-response cycle where every user action that needed server data caused a full page reload. Click a link? New page. Submit a form? Page reloads. Check for new messages? Refresh the page. This made web apps feel slow and clunky compared to desktop applications. AJAX changed this by allowing the browser to communicate with the server in the background.
- User performs an action (clicks button, submits form, scrolls)
- JavaScript creates an HTTP request in the background
- Request is sent to the server asynchronously (page stays interactive)
- Server processes the request and sends back data (JSON, HTML, XML)
- JavaScript receives the response and updates only the relevant part of the page
- No page reload — seamless user experience
AJAX with XMLHttpRequest (Original)
The original AJAX implementation used XMLHttpRequest (XHR). You create a request object, configure it with the HTTP method and URL, set up a callback for when the response arrives, and send it. The readyState property tracks the request lifecycle (0 = unsent, 4 = done), and you check the status for HTTP response codes. This is verbose but shows exactly what AJAX does under the hood.
// Classic AJAX with XMLHttpRequest
function loadUserData(userId) {
const xhr = new XMLHttpRequest();
// Configure: method, URL, async (true)
xhr.open('GET', `/api/users/${userId}`, true);
// Set up response handler
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) { // Request complete
if (xhr.status === 200) {
const user = JSON.parse(xhr.responseText);
document.getElementById('username').textContent = user.name;
document.getElementById('email').textContent = user.email;
} else {
console.error('Request failed:', xhr.status);
}
}
};
// Send the request
xhr.send();
}
// AJAX POST — sending data to the server
function submitComment(postId, comment) {
const xhr = new XMLHttpRequest();
xhr.open('POST', `/api/posts/${postId}/comments`, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status === 201) {
const newComment = JSON.parse(xhr.responseText);
appendCommentToDOM(newComment); // Update UI without reload
}
};
xhr.send(JSON.stringify({ text: comment }));
}
The pattern is always the same: create request → configure → set callback → send. The callback fires when the response arrives, and you update the DOM with the new data. The page never reloads — only the relevant section changes.
Modern AJAX with Fetch
Today, the Fetch API is the standard way to make AJAX requests. It uses Promises instead of callbacks, works with async/await, and has a cleaner API. The concept is identical — asynchronous HTTP requests without page reloads — but the developer experience is dramatically better. Most modern applications use Fetch directly or through libraries like Axios.
// Modern AJAX with Fetch + async/await
async function loadUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const user = await response.json();
// Update only the relevant part of the page
document.getElementById('username').textContent = user.name;
document.getElementById('email').textContent = user.email;
} catch (error) {
document.getElementById('error').textContent = 'Failed to load user';
}
}
// AJAX POST with Fetch
async function submitComment(postId, comment) {
const response = await fetch(`/api/posts/${postId}/comments`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: comment }),
});
if (response.ok) {
const newComment = await response.json();
appendCommentToDOM(newComment);
}
}
// Real-world example: search-as-you-type
const searchInput = document.getElementById('search');
let debounceTimer;
searchInput.addEventListener('input', (event) => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
const query = event.target.value;
if (query.length < 2) return;
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
const results = await response.json();
renderSearchResults(results); // Update dropdown without page reload
}, 300);
});
The search-as-you-type example shows AJAX at its best — the user types, requests fire in the background, and results appear instantly without any page navigation. This pattern powers autocomplete, live search, infinite scroll, and real-time updates across the web.
Common AJAX Patterns
- Lazy loading:
- Load content only when needed (scroll, tab click, expand)
- Reduces initial page weight and speeds up first paint
- Polling:
- Repeatedly fetch data at intervals (setInterval + fetch)
- Simple but inefficient — replaced by WebSockets for real-time
- Infinite scroll:
- Detect scroll position → fetch next page of results
- Append new items to the list without navigation
- Form submission:
- Submit via AJAX → show success/error inline
- No page reload, better UX, can show validation errors instantly
- Optimistic updates:
- Update UI immediately, send request in background
- Revert if request fails — feels instant to the user
AJAX vs Modern Alternatives
| Technology | Direction | Use case |
|---|---|---|
| AJAX (Fetch/XHR) | Client → Server (request/response) | Standard data fetching, form submissions, CRUD operations |
| WebSockets | Bidirectional (persistent connection) | Real-time chat, live updates, multiplayer games |
| Server-Sent Events | Server → Client (one-way stream) | Live feeds, notifications, stock tickers |
| GraphQL | Client → Server (flexible queries) | Complex data requirements, avoiding over/under-fetching |
| React Server Components | Server → Client (streaming HTML) | Next.js — server renders components, streams to client |
Why Interviewers Ask This
AJAX is foundational web knowledge. Interviewers ask this to verify you understand how client-server communication works in the browser, know the difference between synchronous page loads and asynchronous requests, can explain the evolution from XHR to Fetch to modern data-fetching libraries, understand common patterns (polling, optimistic updates, debounced search), and know when AJAX is appropriate versus WebSockets or SSE. It's also a gateway to discussing error handling, loading states, and race conditions in async code.
Quick Revision Cheat Sheet
What AJAX does: Makes HTTP requests in the background — updates page without reload
Original tool: XMLHttpRequest (XHR) — event-based, verbose, still used for upload progress
Modern tool: Fetch API — Promise-based, clean, works with async/await
Key benefit: Partial page updates — only change what's needed, no full reload
Common patterns: Search-as-you-type, infinite scroll, form submission, lazy loading
Not AJAX: WebSockets (persistent bidirectional), SSE (server push) — different protocols