jQuery ajaxTransport() Method

Beginner
⏱️ 13 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
Core Ajax API

What You’ll Learn

$.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

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.

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.

📝 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
});

3. completeCallback signature

jQuery
function( status, statusText, responses, headers ) {}

Return value & registration behavior

  • $.ajaxTransport() returns undefined — it only registers the factory; it does not perform a request.
  • The factory runs during transport selection — after setup merge and prefilters, before beforeSend fires on the wire.
  • Return undefined from the factory when your transport cannot handle the request — jQuery tries the next registered transport.
  • Each request needs its own transport instance — the factory must return a new object every time, never a shared singleton.
  • responses maps dataType keys to raw values — e.g. { image: imgElement } or { text: "...", json: {...} } for converters downstream.
  • Like prefilters, registration can be scoped to a dataType string — only matching requests invoke your factory.

⚡ Quick Reference

GoalCode
Register a transport factory$.ajaxTransport("image", factoryFn)
Fall through to default XHRreturn undefined from factory when unsupported
Start transmissionsend: function(headers, completeCallback) { ... }
Report success to jQuerycompleteCallback(200, "success", { text: data })
Report failurecompleteCallback(404, "error", { text: "" })
Cancel in-flight requestabort: function() { clearTimeout(id); xhr.abort(); }
Use custom dataType$.ajax({ url: "...", dataType: "mockjson" })
Official image patternnew Image(); image.src = options.url;
When NOT needed$.ajax({ dataType: "json" }) — built-in XHR suffices

📋 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.

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 );
  } );
} );
Try It Yourself

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".

jQuery
$.ajaxTransport( "image", function( s ) {
  if ( s.type === "GET" && s.async ) {
    var image;
    return {
      send: function( _, callback ) {
        image = new Image();
        function done( status ) {
          if ( image ) {
            var statusText = ( status === 200 ) ? "success" : "error",
                tmp = image;
            image = image.onreadystatechange = image.onerror = image.onload = null;
            callback( status, statusText, { image: tmp } );
          }
        }
        image.onreadystatechange = image.onload = function() {
          done( 200 );
        };
        image.onerror = function() {
          done( 404 );
        };
        image.src = s.url;
      },
      abort: function() {
        if ( image ) {
          image = image.onreadystatechange = image.onerror = image.onload = null;
        }
      }
    };
  }
} );

$( "#load-img" ).on( "click", function() {
  $.ajax({
    url: "https://picsum.photos/200",
    dataType: "image"
  }).done( function( img ) {
    $( "#preview" ).empty().append( img );
  } );
} );
Try It Yourself

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.

jQuery
$.ajaxTransport( "mockjson", function( options ) {
  if ( options.url.indexOf( "mockjson://" ) === 0 ) {
    return {
      send: function( headers, completeCallback ) {
        var id = options.url.replace( "mockjson://user/", "" );
        var fakeUser = {
          id: parseInt( id, 10 ) || 1,
          name: "Mock User " + id,
          email: "user" + id + "@example.test"
        };
        var jsonText = JSON.stringify( fakeUser );
        completeCallback( 200, "success", {
          text: jsonText,
          json: fakeUser
        } );
      },
      abort: function() {}
    };
  }
} );

$( "#fetch-mock" ).on( "click", function() {
  $.ajax({
    url: "mockjson://user/42",
    dataType: "mockjson"
  }).done( function( user ) {
    $( "#user" ).text( user.name + " — " + user.email );
  } );
} );
Try It Yourself

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.

jQuery
$.ajaxTransport( "slowmock", function( options ) {
  if ( options.url.indexOf( "slowmock://" ) === 0 ) {
    var timerId;
    return {
      send: function( headers, completeCallback ) {
        timerId = setTimeout( function() {
          timerId = null;
          completeCallback( 200, "success", { text: "Slow response arrived" } );
        }, 3000 );
      },
      abort: function() {
        if ( timerId ) {
          clearTimeout( timerId );
          timerId = null;
        }
      }
    };
  }
} );

var pending;

$( "#start-slow" ).on( "click", function() {
  pending = $.ajax({
    url: "slowmock://wait",
    dataType: "slowmock"
  }).done( function( text ) {
    $( "#status" ).text( text );
  }).fail( function( _jqXHR, textStatus ) {
    $( "#status" ).text( "Request " + textStatus );
  } );
} );

$( "#cancel-slow" ).on( "click", function() {
  if ( pending ) {
    pending.abort();
    pending = null;
  }
} );
Try It Yourself

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 ... });
Try It Yourself

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.

🚀 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.

📝 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.

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 Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All 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.

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.

💡 Best Practices

✅ Do

  • 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

Key Takeaways

Knowledge Unlocked

Six things to remember about $.ajaxTransport()

Factory pattern, send/abort, last resort.

6
Core concepts
send 02

send / abort

Transmission API

Wire
cb 03

completeCallback

status + responses

Report
fall 04

undefined fallthrough

Next transport

XHR
last 05

Last resort

After prefilters

Advanced
json 06

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.

Next: jQuery.get() Method

Learn the readable GET shorthand — load JSON, HTML, and query-filtered lists in one line.

jQuery.get() tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful