diff --git a/panels.install b/panels.install
index 6e83d44..0e38f37 100644
--- a/panels.install
+++ b/panels.install
@@ -47,7 +47,27 @@ function panels_requirements_install() {
 function panels_schema() {
   // This should always point to our 'current' schema. This makes it relatively easy
   // to keep a record of schema as we make changes to it.
-  return panels_schema_4();
+  return panels_schema_5();
+}
+
+function panels_schema_5() {
+  $schema = panels_schema_4();
+
+  $schema['panels_display']['fields']['uuid'] = array(
+    'type' => 'char',
+    'length' => '36',
+  );
+  $schema['panels_display']['export']['key'] = 'uuid';
+  $schema['panels_display']['export']['key name'] = 'UUID';
+
+  $schema['panels_pane']['fields']['uuid'] = array(
+    'type' => 'char',
+    'length' => '36',
+  );
+  $schema['panels_pane']['export']['key'] = 'uuid';
+  $schema['panels_pane']['export']['key name'] = 'UUID';
+
+  return $schema;
 }
 
 function panels_schema_4() {
@@ -375,3 +395,72 @@ function panels_update_7301() {
 
   return t('panels_pane.lock field already existed, update skipped.');
 }
+
+/**
+ * Adding universally unique identifiers to panels.
+ */
+function panels_update_7302() {
+  // Load the schema.
+  $schema = panels_schema_5();
+  $msg = array();
+
+  // Add the uuid column to the pane table.
+  $table = 'panels_pane';
+  $field = 'uuid';
+  // Due to a previous failure, the column may already exist:
+  if (!db_field_exists($table, $field)) {
+    $spec = $schema[$table]['fields'][$field];
+    db_add_field($table, $field, $spec);
+    $msg[] = t('Added panels_pane.uuid column.');
+  }
+
+  // Add the uuid column to the display table.
+  $table = 'panels_display';
+  $field = 'uuid';
+  // Due to a previous failure, the column may already exist:
+  if (!db_field_exists($table, $field)) {
+    $spec = $schema[$table]['fields'][$field];
+    db_add_field($table, $field, $spec);
+    $msg[] = t('Added panels_display.uuid column.');
+  }
+
+  if (empty($msg)) {
+    $msg[] = t('UUID column already present in the panels_display & panels_pane tables.');
+  }
+
+  // Update all DB-based panes & displays to ensure that they all contain a UUID.
+  $display_dids = db_select('panels_display')
+    ->fields('panels_display', array('did'))
+    ->condition(db_or()
+      ->condition('uuid', '')
+      ->isNull('uuid')
+    )
+    ->execute()
+    ->fetchCol();
+
+  // Check the panes as well, for paranoia.
+  $pane_dids = db_select('panels_pane')
+    ->distinct()
+    ->fields('panels_pane', array('did'))
+    ->condition(db_or()
+      ->condition('uuid', '')
+      ->isNull('uuid')
+    )
+    ->execute()
+    ->fetchCol();
+
+  $dids = array_unique(array_merge($display_dids, $pane_dids));
+
+  if ($displays = panels_load_displays($dids)) {
+    foreach ($displays as $display) {
+      // A display save also triggers pane saves.
+      panels_save_display($display);
+    }
+    $msg[] = t('Generated UUIDs for database-based panel displays and panes.');
+  }
+  else {
+    $msg[] = t('No database-based panel displays or panes for which to generate UUIDs.');
+  }
+
+  return implode("\n", $msg);
+}
diff --git a/panels.module b/panels.module
index 5a78b8c..207ff45 100644
--- a/panels.module
+++ b/panels.module
@@ -652,8 +652,10 @@ class panels_display {
       $pane->panel = $location;
     }
 
-    // Get a temporary pid for this pane.
-    $pane->pid = "new-" . $this->next_new_pid();
+    // Generate a permanent uuid for this pane, and use
+    // it as a temporary pid.
+    $pane->uuid = ctools_uuid_generate();
+    $pane->pid = 'new-' . $pane->uuid;
 
     // Add the pane to the approprate spots.
     $this->content[$pane->pid] = &$pane;
@@ -667,23 +669,10 @@ class panels_display {
 
   function clone_pane($pid) {
     $pane = clone $this->content[$pid];
+    $pane->uuid = ctools_uuid_generate();
     return $pane;
   }
 
-  function next_new_pid() {
-    // We don't use static vars to record the next new pid because
-    // temporary pids can last for years in exports and in caching
-    // during editing.
-    $id = array(0);
-    foreach (array_keys($this->content) as $pid) {
-      if (!is_numeric($pid)) {
-        $id[] = substr($pid, 4);
-      }
-    }
-    $next_id = max($id);
-    return ++$next_id;
-  }
-
   /**
    * Get the title from a display.
    *
@@ -867,10 +856,13 @@ function panels_load_display($did) {
  *
  * @ingroup mainapi
  *
- * Note a new $display only receives a real did once it is run through this function.
- * Until then, it uses a string placeholder, 'new', in place of a real did. The same
- * applies to all new panes (whether on a new $display or not); in addition,
- * panes have sequential numbers appended, of the form 'new-1', 'new-2', etc.
+ * Note that a new $display only receives a real did once it is run through
+ * this function, and likewise for the pid of any new pane.
+ *
+ * Until then, a new display uses a string placeholder, 'new', in place of
+ * a real did, and a new pane (whether on a new $display or not) appends a
+ * universally-unique identifier (which is stored permanently in the 'uuid'
+ * field). This format is also used in place of the real pid for exports.
  *
  * @param object $display instanceof panels_display \n
  *  The display object to be saved. Passed by reference so the caller need not use
@@ -880,6 +872,9 @@ function panels_load_display($did) {
  */
 function panels_save_display(&$display) {
   $update = (isset($display->did) && is_numeric($display->did)) ? array('did') : array();
+  if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
+    $display->uuid = ctools_uuid_generate();
+  }
   drupal_write_record('panels_display', $display, $update);
 
   $pids = array();
@@ -910,6 +905,11 @@ function panels_save_display(&$display) {
       $pane->did = $display->did;
 
       $old_pid = $pane->pid;
+
+      if (empty($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
+        $pane->uuid = ctools_uuid_generate();
+      }
+
       drupal_write_record('panels_pane', $pane, is_numeric($pid) ? array('pid') : array());
 
       if ($pane->pid != $old_pid) {
@@ -987,9 +987,11 @@ function panels_delete_display($display) {
  *
  * This function is primarily intended as a mechanism for cloning displays.
  * It generates an exact replica (in code) of the provided $display, with
- * the exception that it replaces all ids (dids and pids) with 'new-*' values.
- * Only once panels_save_display() is called on the code version of $display will
- * the exported display written to the database and permanently saved.
+ * the exception that it replaces all ids (dids and pids) with place-holder
+ * values (consisting of the display or pane's uuid, with a 'new-' prefix).
+ *
+ * Only once panels_save_display() is called on the code version of $display
+ * will the exported display be written to the database and permanently saved.
  *
  * @see panels_page_export() or _panels_page_fetch_display() for sample implementations.
  *
@@ -1011,10 +1013,12 @@ function panels_delete_display($display) {
  */
 function panels_export_display($display, $prefix = '') {
   ctools_include('export');
+  if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
+    $display->uuid = ctools_uuid_generate();
+  }
+  $display->did = 'new-' . $display->uuid;
   $output = ctools_export_object('panels_display', $display, $prefix);
 
-  $pid_counter = &drupal_static(__FUNCTION__, 0);
-
   // Initialize empty properties.
   $output .= $prefix . '$display->content = array()' . ";\n";
   $output .= $prefix . '$display->panels = array()' . ";\n";
@@ -1024,7 +1028,12 @@ function panels_export_display($display, $prefix = '') {
   if (!empty($display->content)) {
     $region_counters = array();
     foreach ($display->content as $pane) {
-      $pid = 'new-' . ++$pid_counter;
+
+      if (empty($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
+        $pane->uuid = ctools_uuid_generate();
+      }
+      $pid = 'new-' . $pane->uuid;
+
       if ($pane->pid == $display->title_pane) {
         $title_pid = $pid;
       }
diff --git a/plugins/task_handlers/panel_context.inc b/plugins/task_handlers/panel_context.inc
index 3d3f308..f0171bc 100644
--- a/plugins/task_handlers/panel_context.inc
+++ b/plugins/task_handlers/panel_context.inc
@@ -386,7 +386,10 @@ function panels_panel_context_export(&$handler, $indent) {
       unset($handler->conf[$item]);
     }
   }
-  $display->did = 'new';
+  $display = (object) array(
+    'did' => 'new',
+    'uuid' => ctools_uuid_generate(),
+  );
   $handler->conf['display'] = $display;
 }
 
