$.ajaxTransport() is jQuery’s lowest-level Ajax extension point — a factory that returns custom { send, abort } objects to handle how bytes leave and enter the browser. This tutorial covers the factory pattern, returning undefined to fall through, the official image transport, a mock JSON transport without HTTP, cancelling with abort(), when standard $.ajax() is enough, and how transports fit after prefilters in the pipeline.
01
Transport factory
Per-request instance
02
send / abort
Transmission API
03
completeCallback
status + responses
04
dataType scope
Like prefilters
05
Last resort
After prefilters
06
Since 1.5
Core Ajax API
Fundamentals
Introduction
Most Ajax tutorials stop at $.ajax({ url, dataType: "json" }) — and for everyday REST APIs, that is exactly where you should stop. jQuery already ships with transports for text, HTML, JSON, XML, and script over XMLHttpRequest. But when you need to load images through an Image object instead of XHR, simulate responses in unit tests, or talk to a non-HTTP data source, jQuery exposes jQuery.ajaxTransport( dataType, handler ).
⚠️
Advanced API — Use as a Last Resort
jQuery’s documentation states that transports are the most advanced way to enhance $.ajax(). Reach for $.ajaxPrefilter() and converters first. Register a transport only when built-in XHR handling cannot satisfy your dataType or protocol — custom image loading, in-memory mocks, or proprietary formats.
A transport is not a global singleton. Each request gets its own instance because concurrent Ajax calls need isolated timers, DOM nodes, or sockets. You register a factory function that returns either a transport object { send, abort } or undefined to let jQuery try the next registered transport. The examples below walk from the skeleton pattern through jQuery’s official image transport, a beginner-friendly mock JSON demo, cancellable delayed responses, and when plain dataType: "json" beats custom code.
Concept
Understanding jQuery.ajaxTransport()
jQuery.ajaxTransport() does not send a network request by itself. It registers a handler jQuery invokes during transport selection — after $.ajaxSetup() merge and $.ajaxPrefilter() handlers, but before bytes move. The handler receives merged options, pristine originalOptions, and the per-request jqXHR object.
If your handler can handle the request (matching dataType, method, async flag, or custom URL scheme), return an object with two methods. send( headers, completeCallback ) starts transmission and eventually calls completeCallback( status, statusText, responses, headers ). abort() cancels an in-flight operation. If you cannot handle the request, return undefined — jQuery falls through to the default XMLHttpRequest transport or the next registered factory.
💡
Beginner Tip
Think of transports as “how bytes travel.” Prefilters tweak what gets sent (URL, headers). Converters tweak how responses transform between types. Transports replace the wire entirely — use them sparingly, and always return a fresh object per request from your factory.
Foundation
📝 Syntax
The official jQuery API accepts one signature:
1. Register a transport factory — jQuery.ajaxTransport( dataType, handler )
jQuery
jQuery.ajaxTransport( dataType, handler )
dataType is a string identifying which requests invoke this factory — for example "image" or "mockjson". handler is a function function( options, originalOptions, jqXHR ) that returns a transport object or undefined.
2. Factory skeleton — fall through or return transport
jQuery
$.ajaxTransport( dataType, function( options, originalOptions, jqXHR ) {
if ( /* transportCanHandleRequest */ ) {
return {
send: function( headers, completeCallback ) {
// Start transmission — call completeCallback when done
},
abort: function() {
// Cancel in-flight work
}
};
}
// undefined → jQuery tries the next transport
});
📋 ajaxTransport vs ajaxPrefilter vs XMLHttpRequest vs converters
Four layers of jQuery’s Ajax stack — from early option tweaks to raw byte transmission and response transformation.
ajaxTransport
send / abort
Replaces how bytes are transmitted for a dataType — Image loading, mocks, custom protocols; factory returns fresh instance per request; last resort
ajaxPrefilter
modify options
Runs before transport selection — tweak url, headers, cache, abort duplicates; cannot replace the wire; preferred over transports for cross-cutting tweaks
XMLHttpRequest
default wire
jQuery’s built-in transport for text, html, json, xml, script — handles HTTP GET/POST over XHR; what 99% of apps use without custom code
converters
type transform
Maps response formats after transmission — text to json, text to xml; extend via $.ajaxSetup converters before reaching for a custom transport
Try prefilters and converters first. Register $.ajaxTransport() only when you must replace transmission itself — not when you merely need different headers or JSON parsing. For standard REST JSON, the default XMLHttpRequest transport plus built-in converters already do the job.
Hands-On
Examples Gallery
Five progressive examples from the factory skeleton through jQuery’s official image transport, an in-memory mock JSON transport, cancellable delayed responses, and a best-practice reminder that plain dataType: "json" needs no custom transport. Each links to a Try-it lab where you can edit and run the code in the browser.
📚 Core Patterns
Understand the factory skeleton and jQuery’s documented image transport before building custom dataTypes.
Example 1 — Factory Skeleton — Return undefined or a Transport Object
The minimal pattern from jQuery’s docs: inspect merged options, return a transport when you can handle the request, otherwise fall through so jQuery uses XMLHttpRequest.
jQuery
$.ajaxTransport( "demo", function( options, originalOptions, jqXHR ) {
// Only handle GET requests to URLs starting with "demo://"
if ( options.type === "GET" && options.url.indexOf( "demo://" ) === 0 ) {
return {
send: function( headers, completeCallback ) {
var payload = options.url.replace( "demo://", "" );
completeCallback( 200, "success", { text: "Demo says: " + payload } );
},
abort: function() {
// Nothing in-flight for this instant mock
}
};
}
// return undefined → jQuery tries the next transport (XHR)
} );
$( "#run-demo" ).on( "click", function() {
$.ajax({
url: "demo://Hello transport",
dataType: "demo"
}).done( function( text ) {
$( "#log" ).text( text );
} );
} );
Click #run-demo → factory matches demo:// URL + dataType "demo"
send() calls completeCallback immediately with status 200
#log shows "Demo says: Hello transport" — no HTTP request made
How It Works
When the URL does not match or dataType differs, the factory returns undefined and jQuery never calls your send. When it matches, a fresh transport object handles this one request. That per-request isolation is why jQuery requires a factory, not a shared transport singleton.
Example 2 — Official Image Transport Pattern — Picsum Photo
Adapted from jQuery’s documented minimal image transport: load images via new Image() instead of XMLHttpRequest when dataType: "image".
Click #load-img → Image transport loads https://picsum.photos/200
onload fires → completeCallback(200, "success", { image: imgElement })
.done() receives the Image node → #preview shows the photo
How It Works
The factory checks s.type === "GET" and s.async — synchronous image loads are skipped so jQuery can fall through. send sets image.src; success and error handlers call callback with an image key in responses. abort detaches handlers so a cancelled request cannot fire a late callback.
Example 3 — Custom mockjson Transport — Fake JSON Without HTTP
A beginner-friendly demo transport that returns hard-coded JSON instantly — useful for prototypes and understanding how responses feeds converters.
Click #fetch-mock → mockjson transport parses mockjson://user/42
completeCallback passes both text and json in responses
.done() receives parsed object → #user shows "Mock User 42 — user42@example.test"
How It Works
Supplying both text and json in responses lets jQuery’s converter pipeline deliver a parsed object to .done(). No network tab entry appears because no HTTP occurs. In production tests you might gate this transport behind a debug flag; never ship mock transports to users unintentionally.
📈 Abort & Best Practices
Cancellable delayed transports and knowing when built-in JSON handling beats custom code.
Example 4 — abort() — Delayed Mock Transport the User Can Cancel
Simulate a slow request with setTimeout, then prove that calling jqXHR.abort() invokes your transport’s abort and prevents the late callback.
Click #start-slow → 3s timer starts in send()
Click #cancel-slow within 3s → abort() clears timer → .fail() → "Request aborted"
Wait full 3s without cancel → #status shows "Slow response arrived"
How It Works
jQuery wires jqXHR.abort() to your transport’s abort method. Clear every async resource — timers, XHR, WebSocket, Image handlers — and null out references so a late callback cannot fire after cancel. The per-request factory ensures two slow requests each get their own timer.
Example 5 — When NOT to Use — Standard dataType: "json" Is Enough
Best practice: for ordinary REST JSON over HTTP, skip custom transports entirely. jQuery’s built-in XMLHttpRequest transport and text json converter already parse responses.
jQuery
// No $.ajaxTransport needed — this is the right tool for HTTP JSON
$( "#load-json" ).on( "click", function() {
$.ajax({
url: "https://jsonplaceholder.typicode.com/users/1",
dataType: "json"
}).done( function( user ) {
$( "#result" ).text( "Standard JSON: " + user.name + " (" + user.email + ")" );
} ).fail( function() {
$( "#result" ).text( "Request failed — no custom transport required to debug" );
} );
} );
// Anti-pattern (do not do this for normal APIs):
// $.ajaxTransport("json", function() { ... reimplement XHR ... });
Click #load-json → default XHR transport fetches JSONPlaceholder
Built-in converter parses JSON → .done() receives user object
Zero custom transport code — simpler, tested, maintainable
How It Works
Custom transports add complexity and bypass jQuery’s battle-tested XHR path. Register one only when the built-in wire cannot work — custom protocols, Image-based loading, or in-memory mocks. For JSON APIs, pair $.ajaxPrefilter() for auth headers with plain dataType: "json" instead.
Applications
🚀 Common Use Cases
Image preloading — official pattern loads images via Image when dataType: "image" — lighter than XHR for pixel data.
Custom URL schemes — handle app:// or mockjson:// URLs that never hit the network.
Unit test mocks — return fake responses synchronously or after setTimeout without stubbing global XHR.
Non-HTTP protocols — WebSocket, IndexedDB, or File API reads wrapped as Ajax for uniform .done() / .fail() chains.
Binary or proprietary formats — populate responses with raw ArrayBuffer before converters run.
Cancellable long polls — implement abort() to tear down connections when the user navigates away.
Progressive enhancement fallback — register a transport for legacy environments where XHR cannot handle a specific dataType.
🧠 ajaxTransport in the Ajax Pipeline
1
$.ajaxSetup() merge
Global defaults from the last $.ajaxSetup() merge into caller settings — per-call keys override setup values.
ajaxSetup defaults
2
ajaxPrefilter handlers
Registered prefilters modify merged options — headers, cache, abort-on-retry — before transport selection begins.
ajaxPrefilter
3
Transport selection
jQuery invokes registered transport factories for matching dataType. First factory returning { send, abort } wins; undefined falls through.
ajaxTransport factory
4
send() transmits
Transport send( headers, completeCallback ) moves bytes — XHR, Image, mock timer, or custom protocol.
send → wire
5
completeCallback fires
Transport reports status, statusText, and responses object — raw formats before jQuery converts them.
completeCallback
6
👉
Converters & callbacks
jQuery runs converters (text → json, etc.), then per-call .done() / .fail() and global ajaxSuccess events.
Important
📝 Notes
Last resort — jQuery docs rank transports after prefilters and converters — use only when those APIs are insufficient.
$.ajaxTransport() was added in jQuery 1.5 alongside $.ajaxPrefilter(); it returns undefined.
Each request requires its own transport instance — register a factory function, never a shared singleton object.
Return undefined from the factory when your transport cannot handle the request — jQuery tries the next transport (usually XHR).
completeCallback( status, statusText, responses, headers ) — responses keys must match formats your converters expect.
abort() must cancel all async work and prevent late completeCallback invocations after jqXHR.abort().
dataType-scoped registration works like prefilters — $.ajaxTransport("image", fn) runs only for dataType: "image" requests.
For standard HTTP JSON, HTML, or script, the built-in XMLHttpRequest transport needs no custom registration.
Compatibility
Browser Support
jQuery.ajaxTransport() is part of jQuery’s core Ajax module — not a native DOM API. It works wherever jQuery’s Ajax stack runs: all browsers supported by your jQuery version, including legacy IE with supported jQuery builds. Custom transports inherit the same environment constraints as the APIs you use inside send() — Image, XHR, fetch polyfills, etc.
✓ jQuery 1.5+
jQuery.ajaxTransport() method
Supported in jQuery 1.x, 2.x, and 3.x across all modern browsers and IE with supported jQuery builds. ajaxTransport arrived in jQuery 1.5 as the lowest-level Ajax extension point alongside prefilters. Most apps never register a custom transport because built-in XMLHttpRequest handling covers text, html, json, xml, and script.
100%With jQuery
Google ChromeAll versions · Desktop & Mobile
Full support
Mozilla FirefoxAll versions · Desktop & Mobile
Full support
Apple SafariAll versions · macOS & iOS
Full support
Microsoft EdgeAll versions · Chromium & Legacy
Full support
Internet ExplorerIE 6+ · Legacy environments
Full support
OperaAll modern versions
Full support
$.ajaxTransport()Universal
Bottom line: Technically available in any jQuery 1.5+ project, but architecturally rare — reserve for custom protocols, image loading, or test mocks. Prefer prefilters for headers and converters for type transforms before writing a transport.
Wrap Up
Conclusion
jQuery.ajaxTransport() registers a factory that returns per-request { send, abort } objects to control how Ajax data is transmitted. Use it when prefilters and converters cannot solve your problem — image loading, custom URL schemes, or in-memory mocks. Return undefined to fall through to XMLHttpRequest, call completeCallback with a rich responses object, and implement abort() for cancellable work.
For everyday JSON APIs, use $.get() or $.ajax({ dataType: "json" }) instead of a custom transport. Continue with the jQuery.get() tutorial for the readable GET shorthand, then explore ajaxStart for page-wide loading indicators.
Try $.ajaxPrefilter() and converters before registering a transport
Return a fresh transport object from the factory for every request
Return undefined when your transport cannot handle the call
Implement abort() to clear timers, XHR, and event handlers
Populate responses with keys matching your dataType and converters
❌ Don’t
Reimplement XMLHttpRequest for ordinary HTTP JSON — use dataType: "json"
Register a shared singleton transport object across concurrent requests
Leave late callbacks firing after abort() — null references in abort
Ship mock transports to production without a feature flag or test guard
Reach for transports when a prefilter header tweak would suffice
Summary
Key Takeaways
Knowledge Unlocked
Six things to remember about $.ajaxTransport()
Factory pattern, send/abort, last resort.
6
Core concepts
fact01
Factory pattern
Fresh per request
Instance
send02
send / abort
Transmission API
Wire
cb03
completeCallback
status + responses
Report
fall04
undefined fallthrough
Next transport
XHR
last05
Last resort
After prefilters
Advanced
json06
JSON needs none
Built-in XHR
Simple
❓ Frequently Asked Questions
jQuery.ajaxTransport() registers a factory function that jQuery calls when selecting how to transmit an Ajax request for a given dataType. The factory receives merged options, originalOptions, and jqXHR. If it can handle the request, it returns a transport object with send and abort methods; otherwise it returns undefined and jQuery tries the next transport. Transports handle the actual byte transmission — the lowest-level Ajax extension point.
send(headers, completeCallback) starts transmission. headers is a key-value object of request headers jQuery assembled; your transport sends bytes (or simulates them) and eventually calls completeCallback when finished. abort() cancels an in-flight request — clear timers, abort XHR, detach Image load handlers, or ignore late callbacks so completeCallback never fires after cancel.
completeCallback(status, statusText, responses, headers). status is an HTTP-style code like 200 or 404. statusText is a short label such as success or error. responses is an object mapping dataType keys to raw response values — for example { image: imgElement } or { text: "...", json: {...} }. headers is an optional string of response headers (like XMLHttpRequest.getAllResponseHeaders()) when your transport has them.
ajaxPrefilter runs early to modify request settings — headers, url, cache — before transport selection. ajaxTransport runs later and replaces how bytes are sent and received for matching dataTypes. Use prefilters for cross-cutting option tweaks; use transports only when no built-in transport or converter can handle your custom dataType or protocol.
Use ajaxTransport as a last resort when prefilters and converters are insufficient: custom protocols, non-HTTP data sources, loading images via Image instead of XHR, in-memory mocks for tests, or proprietary binary formats. For ordinary JSON, HTML, or script requests, standard $.ajax({ dataType: "json" }) already works — do not register a transport.
Each Ajax request needs its own transport instance with isolated state — timers, DOM nodes, sockets. You cannot register one shared transport object because concurrent requests would clobber each other's send/abort state. $.ajaxTransport() therefore accepts a factory: a function that returns a fresh { send, abort } object per request when it can handle that call.
Did you know?
jQuery added $.ajaxTransport() in version 1.5 — the same release as $.ajaxPrefilter() and the modern deferred-based Ajax pipeline. The official documentation includes a complete minimal image transport using new Image() because browsers load images more naturally through the Image API than through XMLHttpRequest. jQuery’s own source registers default converters for text json, text html, and text xml — which is why most developers never need a custom transport unless they introduce an entirely new dataType like mockjson or image.