diff --git js/ajax-responder.js js/ajax-responder.js
index 01b7101..f269111 100644
--- js/ajax-responder.js
+++ js/ajax-responder.js
@@ -9,6 +9,7 @@
   Drupal.CTools = Drupal.CTools || {};
   Drupal.CTools.AJAX = Drupal.CTools.AJAX || {};
   Drupal.CTools.AJAX.commands = Drupal.CTools.AJAX.commands || {};
+  Drupal.CTools.AJAX.commandCache = Drupal.CTools.AJAX.comandCache || new Array();
 
   /**
    * Success callback for an ajax request.
@@ -27,6 +28,57 @@
   };
 
   /**
+   * Grab the response from the server and store it.
+   */
+  Drupal.CTools.AJAX.warmCache = function () {
+    if ($(this).hasClass('ctools-fetching')) {
+      return false;
+    }
+
+    var url = $(this).attr('href');
+    var object = $(this);
+    $(this).addClass('ctools-fetching');
+    try {
+      url = url.replace(/nojs/g, 'ajax');
+      $.ajax({
+        type: "POST",
+        url: url,
+        data: 'js=1',
+        global: true,
+        success: function (data) {
+          var position = Drupal.CTools.AJAX.commandCache.push(data);
+          // rel will not be seen by crawlers (they have JS off).
+          object.attr('rel', position - 1);
+        },
+        complete: function() {
+          object.removeClass('ctools-fetching');
+        },
+        dataType: 'json'
+      });
+    }
+    catch (err) {
+      $(this).removeClass('ctools-fetching');
+      return false;
+    }
+
+    return false;
+  };
+
+  /**
+   * Cachable click handler to fetch the commands out of the cache or from url.
+   */
+  Drupal.CTools.AJAX.clickAJAXCacheLink = function () {
+    var position = $(this).attr('rel');
+    if (position.length != 0 && Drupal.CTools.AJAX.commandCache[position]) {
+      Drupal.CTools.AJAX.respond(Drupal.CTools.AJAX.commandCache[position]);
+      return false;
+    }
+    else {
+      return Drupal.CTools.AJAX.clickAJAXLink.apply(this);
+    }
+  };
+
+  /**
    * Generic replacement click handler to open the modal with the destination
    * specified by the href of the link.
    */
@@ -314,4 +366,16 @@
        .addClass('ctools-use-ajax-processed')
        .change(Drupal.CTools.AJAX.changeAJAX);
   };
+
+  /**
+   * Bind links that can use the CTools commandCache.
+   */
+  Drupal.behaviors.CToolsAJAXCache = function (context) {
+    $('a.ctools-use-ajax-cache:not(.ctools-use-ajax-cache-processed)', context)
+    .addClass('ctools-use-ajax-cache-processed')
+    .click(Drupal.CTools.AJAX.clickAJAXCacheLink)
+    .each(function () {
+      Drupal.CTools.AJAX.warmCache.apply(this);
+    });
+  };
 })(jQuery);
diff --git js/modal.js js/modal.js
index 64553b9..58989e7 100644
--- js/modal.js
+++ js/modal.js
@@ -81,6 +81,14 @@
   };
 
   /**
+   * Click() function for modals that can be cached.
+   */
+  Drupal.CTools.Modal.clickAjaxCacheLink = function () {
+    Drupal.CTools.Modal.show();
+    return Drupal.CTools.AJAX.clickAJAXCacheLink.apply(this);
+  };
+
+  /**
    * Generic replacement click handler to open the modal with the destination
    * specified by the href of the link.
    */
@@ -212,6 +220,18 @@
     }
   };
 
+  /**
+   * Finds all cachable links and binds click and then warms the cache.
+   */
+  Drupal.behaviors.CToolsModalCache = function (context) {
+    $('a.ctools-use-modal-cache:not(.ctools-use-modal-cache-processed)', context)
+    .addClass('ctools-use-modal-cache-processed')
+    .click(Drupal.CTools.Modal.clickAjaxCacheLink)
+    .each(function () {
+      Drupal.CTools.AJAX.warmCache.apply(this);
+    });
+  };
+
   // The following are implementations of AJAX responder commands.
 
   /**
