diff --git a/core/lib/Drupal/Core/Annotation/GroupablePlugin.php b/core/lib/Drupal/Core/Annotation/GroupablePlugin.php
new file mode 100644
index 0000000..e014409
--- /dev/null
+++ b/core/lib/Drupal/Core/Annotation/GroupablePlugin.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Annotation\GroupablePlugin.
+ */
+
+namespace Drupal\Core\Annotation;
+
+/**
+ * Defines a trait for plugins that can be grouped.
+ */
+trait GroupablePlugin {
+
+  /**
+   * The group machine name.
+   */
+  public $group = 'default';
+
+  /**
+   * The group label.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $group_label = '';
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Annotation/ConfigEntityType.php b/core/lib/Drupal/Core/Entity/Annotation/ConfigEntityType.php
index b99e472..7126eff 100644
--- a/core/lib/Drupal/Core/Entity/Annotation/ConfigEntityType.php
+++ b/core/lib/Drupal/Core/Entity/Annotation/ConfigEntityType.php
@@ -19,4 +19,18 @@ class ConfigEntityType extends EntityType {
    */
   public $entity_type_class = 'Drupal\Core\Config\Entity\ConfigEntityType';
 
+  /**
+   * {@inheritdoc}
+   */
+  public $group = 'configuration';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get() {
+    $this->definition['group_label'] = $this->t('Configuration', array(), array('context' => 'Entity type group'));
+
+    return parent::get();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Annotation/ContentEntityType.php b/core/lib/Drupal/Core/Entity/Annotation/ContentEntityType.php
index e9ae852..ddf05c9 100644
--- a/core/lib/Drupal/Core/Entity/Annotation/ContentEntityType.php
+++ b/core/lib/Drupal/Core/Entity/Annotation/ContentEntityType.php
@@ -19,4 +19,18 @@ class ContentEntityType extends EntityType {
    */
   public $entity_type_class = 'Drupal\Core\Entity\ContentEntityType';
 
+  /**
+   * {@inheritdoc}
+   */
+  public $group = 'content';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get() {
+    $this->definition['group_label'] = $this->t('Content', array(), array('context' => 'Entity type group'));
+
+    return parent::get();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
index 2c27ccb..fa5d4b3 100644
--- a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity\Annotation;
 
 use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\GroupablePlugin;
 
 /**
  * Defines an Entity type annotation object.
@@ -16,6 +17,8 @@
  */
 class EntityType extends Plugin {
 
+  use GroupablePlugin;
+
   /**
    * The class used to represent the entity type.
    *
@@ -38,4 +41,13 @@ public function get() {
     return new $class($values);
   }
 
+  /**
+   * Translates a string to the current language or to a given language.
+   *
+   * See the t() documentation for details.
+   */
+  protected function t($string, array $args = array(), array $options = array()) {
+    return \Drupal::translation()->translate($string, $args, $options);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 5bfd687..242e01e 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -467,10 +467,28 @@ public function getAllBundleInfo() {
   /**
    * {@inheritdoc}
    */
-  public function getEntityTypeLabels() {
+  public function getEntityTypeLabels($group = FALSE) {
     $options = array();
-    foreach ($this->getDefinitions() as $entity_type => $definition) {
-      $options[$entity_type] = $definition->getLabel();
+    $definitions = $this->getDefinitions();
+
+    foreach ($definitions as $entity_type_id => $definition) {
+      if ($group) {
+        $options[$definition->getGroupLabel()][$entity_type_id] = $definition->getLabel();
+      }
+      else {
+        $options[$entity_type_id] = $definition->getLabel();
+      }
+    }
+
+    if ($group) {
+      foreach ($options as &$group_options) {
+        // Sort the list alphabetically by group label.
+        array_multisort($group_options, SORT_ASC, SORT_NATURAL);
+      }
+
+      // Make sure that the 'Content' group is situated at the top.
+      $content = $this->t('Content', array(), array('context' => 'Entity type group'));
+      $options = array($content => $options[$content]) + $options;
     }
 
     return $options;
@@ -509,4 +527,13 @@ public function getTranslationFromContext(EntityInterface $entity, $langcode = N
     return $translation;
   }
 
+  /**
+   * Translates a string to the current language or to a given language.
+   *
+   * See the t() documentation for details.
+   */
+  protected function t($string, array $args = array(), array $options = array()) {
+    return $this->translationManager->translate($string, $args, $options);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index e40ff94..0cbb138 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -17,10 +17,14 @@
   /**
    * Builds a list of entity type labels suitable for a Form API options list.
    *
+   * @param bool $group
+   *   (optional) Whether to group entity types by plugin group (e.g. 'content',
+   *   'config'). Defaults to FALSE.
+   *
    * @return array
    *   An array of entity type labels, keyed by entity type name.
    */
-  public function getEntityTypeLabels();
+  public function getEntityTypeLabels($group = FALSE);
 
   /**
    * Gets the base field definitions for a content entity type.
diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
index 4eae0df..22b7834 100644
--- a/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -178,6 +178,16 @@ class EntityType implements EntityTypeInterface {
   protected $uri_callback;
 
   /**
+   * The machine name of the entity type group.
+   */
+  protected $group;
+
+  /**
+   * The human-readable name of the entity type group.
+   */
+  protected $group_label;
+
+  /**
    * Constructs a new EntityType.
    *
    * @param array $definition
@@ -587,4 +597,28 @@ public function setUriCallback($callback) {
     return $this;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getGroup() {
+    return $this->group;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getGroupLabel() {
+    return !empty($this->group_label) ? $this->group_label : $this->t('Other', array(), array('context' => 'Entity type group'));
+  }
+
+  /**
+   * Translates a string to the current language or to a given language.
+   *
+   * See the t() documentation for details.
+   */
+  protected function t($string, array $args = array(), array $options = array()) {
+    return \Drupal::translation()->translate($string, $args, $options);
+  }
+
 }
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
index b801c1e..93787e5 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php
@@ -142,7 +142,7 @@ public function settingsForm(array $form, array &$form_state, $has_data) {
     $element['target_type'] = array(
       '#type' => 'select',
       '#title' => t('Type of item to reference'),
-      '#options' => \Drupal::entityManager()->getEntityTypeLabels(),
+      '#options' => \Drupal::entityManager()->getEntityTypeLabels(TRUE),
       '#default_value' => $this->getSetting('target_type'),
       '#required' => TRUE,
       '#disabled' => $has_data,
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAdminTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAdminTest.php
index 37cf7bb..54eea8f 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAdminTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAdminTest.php
@@ -42,7 +42,7 @@ protected function assertFieldSelectOptions($name, $expected_options) {
     if ($fields) {
       $field = $fields[0];
       $options = $this->getAllOptionsList($field);
-      return $this->assertIdentical($options, $expected_options);
+      return $this->assertIdentical(sort($options), sort($expected_options));
     }
     else {
       return $this->fail(t('Unable to find field @name', array('@name' => $name)));
@@ -58,7 +58,11 @@ protected function getAllOptionsList($element) {
     foreach ($element->option as $option) {
       $options[] = (string) $option['value'];
     }
-    // TODO: support optgroup.
+
+    if (isset($element->optgroup)) {
+      $options += $this->getAllOptionsList($element->optgroup);
+    }
+
     return $options;
   }
 
@@ -76,9 +80,7 @@ public function testFieldAdminHandler() {
     $this->assertFieldByName('field[settings][target_type]', 'node');
 
     // Check that all entity types can be referenced.
-    foreach (\Drupal::entityManager()->getDefinitions() as $entity_type_id => $entity_type) {
-      $this->assertFieldByXPath("//select[@name='field[settings][target_type]']/option[@value='" . $entity_type_id . "']");
-    }
+    $this->assertFieldSelectOptions('field[settings][target_type]', array_keys(\Drupal::entityManager()->getDefinitions()));
 
     // Second step: 'Instance settings' form.
     $this->drupalPostForm(NULL, array(), t('Save field settings'));
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 2bd20a2..4c99e73 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -3038,7 +3038,7 @@ protected function assertFieldByXPath($xpath, $value = NULL, $message = '', $gro
             // Input element with correct value.
             $found = TRUE;
           }
-          elseif (isset($field->option)) {
+          elseif (isset($field->option) || isset($field->optgroup)) {
             // Select element found.
             $selected = $this->getSelectedItem($field);
             if ($selected === FALSE) {
