diff --git a/src/Form/SubformHelperTrait.php b/src/Form/SubformHelperTrait.php
new file mode 100644
index 0000000..10f177d
--- /dev/null
+++ b/src/Form/SubformHelperTrait.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\plugin\Form;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformStateInterface;
+
+/**
+ * Provides helpers for building subforms.
+ *
+ * @internal
+ */
+trait SubformHelperTrait {
+
+  protected function assertSubformState(FormStateInterface $form_state) {
+    if (!($form_state instanceof SubformStateInterface)) {
+      $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+      trigger_error(sprintf('%s::%s() SHOULD receive %s on line %d, but %s was given. More information is available at https://www.drupal.org/node/2774077.', $trace[1]['class'], $trace[1]['function'], SubformStateInterface::class, $trace[1]['line'], get_class($form_state)), E_USER_DEPRECATED);
+    }
+  }
+
+}
diff --git a/src/Plugin/Field/FieldWidget/PluginSelector.php b/src/Plugin/Field/FieldWidget/PluginSelector.php
index 12f753b..65c311f 100644
--- a/src/Plugin/Field/FieldWidget/PluginSelector.php
+++ b/src/Plugin/Field/FieldWidget/PluginSelector.php
@@ -6,6 +6,7 @@ use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
 
 /**
  * Provides a plugin selector field widget.
@@ -23,7 +24,7 @@ class PluginSelector extends WidgetBase {
   /**
    * {@inheritdoc}
    */
-  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$parent_form, FormStateInterface $complete_form_state) {
     /** @var \Drupal\plugin\Plugin\Field\FieldType\PluginCollectionItemInterface $item */
     $item = $items[$delta];
     /** @var \Drupal\plugin\PluginType\PluginTypeInterface $plugin_type */
@@ -38,7 +39,6 @@ class PluginSelector extends WidgetBase {
       '#process' => [[get_class(), 'processFormElement']],
       '#selected_plugin' => $items->isEmpty() ? NULL : $items->get($delta)->getContainedPluginInstance(),
     ];
-    $element['plugin_selector'] = static::getPluginSelector($form_state, $element)->buildSelectorForm([], $form_state);
 
     return $element;
   }
@@ -46,7 +46,7 @@ class PluginSelector extends WidgetBase {
   /**
    * Implements a #process callback.
    */
-  public static function processFormElement(array &$element, FormStateInterface $form_state, array &$form) {
+  public static function processFormElement(array &$element, FormStateInterface $complete_form_state, array &$complete_form) {
     // Store #array_parents in the form state, so we can get the elements from
     // the complete form array by using only the form state.
     $element['array_parents'] = [
@@ -54,27 +54,36 @@ class PluginSelector extends WidgetBase {
       '#value' => $element['#array_parents'],
     ];
 
+    $plugin_selector = static::getPluginSelector($complete_form_state, $element);
+    $plugin_selector_form = [];
+    $plugin_selector_form_state = SubformState::createForSubform($plugin_selector_form, $complete_form_state->getCompleteForm(), $complete_form_state);
+    $element['plugin_selector'] = $plugin_selector->buildSelectorForm($plugin_selector_form, $plugin_selector_form_state);
+
     return $element;
   }
 
   /**
    * Implements an #element_validate callback.
    */
-  public static function validateFormElement(array &$element, FormStateInterface $form_state, array &$form) {
-    $plugin_selector = static::getPluginSelector($form_state, $element);
-    $plugin_selector->validateSelectorForm($element['plugin_selector'], $form_state);
+  public static function validateFormElement(array &$element, FormStateInterface $complete_form_state, array &$complete_form) {
+    $plugin_selector = static::getPluginSelector($complete_form_state, $element);
+    $plugin_selector_form = $element['plugin_selector'];
+    $plugin_selector_form_state = SubformState::createForSubform($plugin_selector_form, $complete_form, $complete_form_state);
+    $plugin_selector->validateSelectorForm($plugin_selector_form, $plugin_selector_form_state);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+  public function massageFormValues(array $values, array $parent_form, FormStateInterface $complete_form_state) {
     $massaged_values = [];
 
     foreach ($values as $delta => $item_values) {
-      $element = NestedArray::getValue($form, array_slice($item_values['array_parents'], count($form['#array_parents'])));
-      $plugin_selector = static::getPluginSelector($form_state, $element);
-      $plugin_selector->submitSelectorForm($element['plugin_selector'], $form_state);
+      $element = NestedArray::getValue($parent_form, array_slice($item_values['array_parents'], count($parent_form['#array_parents'])));
+      $plugin_selector = static::getPluginSelector($complete_form_state, $element);
+      $plugin_selector_form = $element['plugin_selector'];
+      $plugin_selector_form_state = SubformState::createForSubform($plugin_selector_form, $complete_form_state->getCompleteForm(), $complete_form_state);
+      $plugin_selector->submitSelectorForm($plugin_selector_form, $plugin_selector_form_state);
       $massaged_values[$delta] = [
         'plugin_instance' => $plugin_selector->getSelectedPlugin(),
       ];
@@ -86,18 +95,18 @@ class PluginSelector extends WidgetBase {
   /**
    * Gets the plugin selector for a field item's elements.
    *
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   * @param \Drupal\Core\Form\FormStateInterface $complete_form_state
    * @param mixed[] $element
    *   The field widget's form elements.
    *
    * @return \Drupal\plugin\Plugin\Plugin\PluginSelector\PluginSelectorInterface
    */
-  protected static function getPluginSelector(FormStateInterface $form_state, array $element) {
+  protected static function getPluginSelector(FormStateInterface $complete_form_state, array $element) {
     /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
     $field_definition = $element['#field_definition'];
     $form_state_key = sprintf('plugin_selector:%s:%d', $field_definition->getName(), $element['#delta']);
-    if ($form_state->has($form_state_key)) {
-      $plugin_selector = $form_state->get($form_state_key);
+    if ($complete_form_state->has($form_state_key)) {
+      $plugin_selector = $complete_form_state->get($form_state_key);
     }
     else {
       /** @var \Drupal\plugin\PluginType\PluginTypeManagerInterface $plugin_type_manager */
@@ -117,7 +126,7 @@ class PluginSelector extends WidgetBase {
       if ($element['#selected_plugin']) {
         $plugin_selector->setSelectedPlugin($element['#selected_plugin']);
       }
-      $form_state->set($form_state_key, $plugin_selector);
+      $complete_form_state->set($form_state_key, $plugin_selector);
     }
 
     return $plugin_selector;
diff --git a/src/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBase.php b/src/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBase.php
index c0a6bcb..0c2ff94 100644
--- a/src/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBase.php
+++ b/src/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBase.php
@@ -9,6 +9,7 @@ use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\ReplaceCommand;
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Plugin\PluginFormInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
@@ -58,11 +59,13 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
   /**
    * {@inheritdoc}
    */
-  public function buildSelectorForm(array $form, FormStateInterface $form_state) {
-    $form = parent::buildSelectorForm($form, $form_state);
+  public function buildSelectorForm(array $plugin_selector_form, FormStateInterface $plugin_selector_form_state) {
+    $this->assertSubformState($plugin_selector_form_state);
+    $plugin_selector_form = parent::buildSelectorForm($plugin_selector_form, $plugin_selector_form_state);
+    $plugin_selector_form['#tree'] = TRUE;
 
     $available_plugins = [];
-    $cacheability_metadata = CacheableMetadata::createFromRenderArray($form);
+    $cacheability_metadata = CacheableMetadata::createFromRenderArray($plugin_selector_form);
     foreach (array_keys($this->selectablePluginDiscovery->getDefinitions()) as $plugin_id) {
       $available_plugin = $this->selectablePluginFactory->createInstance($plugin_id);
       if ($available_plugin instanceof PluginInspectionInterface) {
@@ -70,21 +73,20 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
         $cacheability_metadata = $cacheability_metadata->merge(CacheableMetadata::createFromObject($available_plugin));
       }
     }
-    $cacheability_metadata->applyTo($form);
+    $cacheability_metadata->applyTo($plugin_selector_form);
 
-    $plugin_selector_form_state_key = static::setPluginSelector($form_state, $this);
-    $form['container'] = array(
+    $plugin_selector_form_state_key = static::setPluginSelector($plugin_selector_form_state, $this);
+    $plugin_selector_form['container'] = array(
       '#attributes' => array(
         'class' => array('plugin-selector-' . Html::getClass($this->getPluginId())),
       ),
       '#available_plugins' => $available_plugins,
       '#plugin_selector_form_state_key' => $plugin_selector_form_state_key,
       '#process' => [[get_class(), 'processBuildSelectorForm']],
-      '#tree' => TRUE,
       '#type' => 'container',
     );
 
-    return $form;
+    return $plugin_selector_form;
   }
 
   /**
@@ -124,27 +126,29 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
   /**
    * Implements a Form API #process callback.
    */
-  public static function processBuildSelectorForm(array $element, FormStateInterface $form_state, array $form) {
+  public static function processBuildSelectorForm(array $plugin_selector_form, FormStateInterface $complete_form_state, array $complete_form) {
     /** @var static $plugin_selector */
-    $plugin_selector = static::getPluginSelector($form_state, $element['#plugin_selector_form_state_key']);
+    $plugin_selector = static::getPluginSelector($complete_form_state, $plugin_selector_form['#plugin_selector_form_state_key']);
+
+    $plugin_selector_form_state = SubformState::createForSubform($plugin_selector_form, $complete_form, $complete_form_state);
 
-    if (count($element['#available_plugins']) == 0) {
-      return $plugin_selector->buildNoAvailablePlugins($element, $form_state);
+    if (count($plugin_selector_form['#available_plugins']) == 0) {
+      return $plugin_selector->buildNoAvailablePlugins($plugin_selector_form, $plugin_selector_form_state);
     }
-    elseif (count($element['#available_plugins']) == 1 && !$plugin_selector->getSelectorVisibilityForSingleAvailability()) {
-      return $plugin_selector->buildOneAvailablePlugin($element, $form_state);
+    elseif (count($plugin_selector_form['#available_plugins']) == 1 && !$plugin_selector->getSelectorVisibilityForSingleAvailability()) {
+      return $plugin_selector->buildOneAvailablePlugin($plugin_selector_form, $plugin_selector_form_state);
     }
     else {
-      return $plugin_selector->buildMultipleAvailablePlugins($element, $form_state);
+      return $plugin_selector->buildMultipleAvailablePlugins($plugin_selector_form, $plugin_selector_form_state);
     }
   }
 
   /**
    * {@inheritdoc}
    */
-  public function validateSelectorForm(array &$form, FormStateInterface $form_state) {
-    $values = $form_state->getValues();
-    $plugin_id = NestedArray::getValue($values, array_merge($form['container']['#parents'], array('select', 'container', 'plugin_id')));
+  public function validateSelectorForm(array &$plugin_selector_form, FormStateInterface $plugin_selector_form_state) {
+    $this->assertSubformState($plugin_selector_form_state);
+    $plugin_id = $plugin_selector_form_state->getValue(['container', 'select', 'container', 'plugin_id']);
     $selected_plugin = $this->getSelectedPlugin();
     if (!$selected_plugin && $plugin_id || $selected_plugin && $plugin_id != $selected_plugin->getPluginId()) {
       // Keep track of all previously selected plugins so their configuration
@@ -162,39 +166,44 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
       // If a (different) plugin was chosen and its form must be displayed,
       // rebuild the form.
       if ($this->getCollectPluginConfiguration() && $this->getSelectedPlugin() instanceof PluginFormInterface) {
-        $form_state->setRebuild();
+        $plugin_selector_form_state->setRebuild();
       }
     }
     // If no (different) plugin was chosen, delegate validation to the plugin.
     elseif ($this->getCollectPluginConfiguration() && $selected_plugin instanceof PluginFormInterface) {
-      $selected_plugin->validateConfigurationForm($form['container']['plugin_form'], $form_state);
+      $selected_plugin_form = &$plugin_selector_form['container']['plugin_form'];
+      $selected_plugin_form_state = SubformState::createForSubform($selected_plugin_form, $plugin_selector_form, $plugin_selector_form_state);
+      $selected_plugin->validateConfigurationForm($selected_plugin_form, $selected_plugin_form_state);
     }
   }
 
   /**
    * {@inheritdoc}
    */
-  public function submitSelectorForm(array &$form, FormStateInterface $form_state) {
-    $selectedPlugin = $this->getSelectedPlugin();
-    if ($this->getCollectPluginConfiguration() && $selectedPlugin instanceof PluginFormInterface) {
-      $selectedPlugin->submitConfigurationForm($form['container']['plugin_form'], $form_state);
+  public function submitSelectorForm(array &$plugin_selector_form, FormStateInterface $plugin_selector_form_state) {
+    $this->assertSubformState($plugin_selector_form_state);
+    $selected_plugin = $this->getSelectedPlugin();
+    if ($this->getCollectPluginConfiguration() && $selected_plugin instanceof PluginFormInterface) {
+      $selected_plugin_form = &$plugin_selector_form['container']['plugin_form'];
+      $selected_plugin_form_state = SubformState::createForSubform($selected_plugin_form, $plugin_selector_form, $plugin_selector_form_state);
+      $selected_plugin->submitConfigurationForm($selected_plugin_form, $selected_plugin_form_state);
     }
   }
 
   /**
    * Implements form API's #submit.
    */
-  public static function rebuildForm(array $form, FormStateInterface $form_state) {
-    $form_state->setRebuild();
+  public static function rebuildForm(array $complete_form, FormStateInterface $complete_form_state) {
+    $complete_form_state->setRebuild();
   }
 
   /**
    * Implements form AJAX callback.
    */
-  public static function ajaxRebuildForm(array &$form, FormStateInterface $form_state) {
-    $triggering_element = $form_state->getTriggeringElement();
+  public static function ajaxRebuildForm(array &$complete_form, FormStateInterface $complete_form_state) {
+    $triggering_element = $complete_form_state->getTriggeringElement();
     $form_parents = array_slice($triggering_element['#array_parents'], 0, -3);
-    $root_element = NestedArray::getValue($form, $form_parents);
+    $root_element = NestedArray::getValue($complete_form, $form_parents);
 
     $response = new AjaxResponse();
     $response->addCommand(new ReplaceCommand(sprintf('[data-drupal-selector="%s"]', $root_element['plugin_form']['#attributes']['data-drupal-selector']), $root_element['plugin_form']));
@@ -205,30 +214,32 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
   /**
    * Builds the plugin configuration form elements.
    *
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   * @param mixed[] $plugin_selector_form
+   *   The plugin selector form.
+   * @param \Drupal\Core\Form\FormStateInterface $plugin_selector_form_state
    *
    * @return array
    */
-  protected function buildPluginForm(FormStateInterface $form_state) {
-    $element = array(
+  protected function buildPluginForm(array $plugin_selector_form, FormStateInterface $plugin_selector_form_state) {
+    $selected_plugin_form = [
       '#attributes' => array(
         'class' => [Html::getClass(sprintf('plugin-selector-%s-plugin-form', $this->getPluginId()))],
       ),
       '#type' => 'container',
-    );
-
-    $selectedPlugin = $this->getSelectedPlugin();
-    if ($this->getCollectPluginConfiguration() && $selectedPlugin instanceof PluginFormInterface) {
-      $element += $selectedPlugin->buildConfigurationForm([], $form_state);
+    ];
+    $selected_plugin = $this->getSelectedPlugin();
+    if ($this->getCollectPluginConfiguration() && $selected_plugin instanceof PluginFormInterface) {
+      $selected_plugin_form_state = SubformState::createForSubform($selected_plugin_form, $plugin_selector_form, $plugin_selector_form_state);
+      $selected_plugin_form = $selected_plugin->buildConfigurationForm($selected_plugin_form, $selected_plugin_form_state);
     }
 
-    return $element;
+    return $selected_plugin_form;
   }
 
   /**
    * Builds the form elements for when there are no available plugins.
    */
-  public function buildNoAvailablePlugins(array $element, FormStateInterface $form_state) {
+  public function buildNoAvailablePlugins(array $element, FormStateInterface $plugin_selector_form_state) {
     $element['select']['container'] = array(
       '#type' => 'container',
     );
@@ -248,8 +259,8 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
   /**
    * Builds the form elements for one plugin.
    */
-  public function buildOneAvailablePlugin(array $element, FormStateInterface $form_state) {
-    $plugin = reset($element['#available_plugins']);
+  public function buildOneAvailablePlugin(array $plugin_selector_form, FormStateInterface $plugin_selector_form_state) {
+    $plugin = reset($plugin_selector_form['#available_plugins']);
 
     // Use the only available plugin if no other was configured before, or the
     // configured plugin is not available.
@@ -257,40 +268,40 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
       $this->setSelectedPlugin($plugin);
     }
 
-    $element['select']['message'] = array(
+    $plugin_selector_form['select']['message'] = array(
       '#title' => $this->getLabel(),
       '#type' => 'item',
     );
-    $element['select']['container'] = array(
+    $plugin_selector_form['select']['container'] = array(
       '#type' => 'container',
     );
-    $element['select']['container']['plugin_id'] = array(
+    $plugin_selector_form['select']['container']['plugin_id'] = array(
       '#type' => 'value',
       '#value' => $this->getSelectedPlugin()->getPluginId(),
     );
-    $element['plugin_form'] = $this->buildPluginForm($form_state);
+    $plugin_selector_form['plugin_form'] = $this->buildPluginForm($plugin_selector_form, $plugin_selector_form_state);
 
-    return $element;
+    return $plugin_selector_form;
   }
 
   /**
    * Builds the form elements for multiple plugins.
    */
-  public function buildMultipleAvailablePlugins(array $element, FormStateInterface $form_state) {
-    $plugins = $element['#available_plugins'];
+  protected function buildMultipleAvailablePlugins(array $plugin_selector_form, FormStateInterface $plugin_selector_form_state) {
+    $plugins = $plugin_selector_form['#available_plugins'];
 
-    $element['select'] = $this->buildSelector($element, $form_state, $plugins);
-    $element['plugin_form'] = $this->buildPluginForm($form_state);
+    $plugin_selector_form['select'] = $this->buildSelector($plugin_selector_form, $plugin_selector_form_state, $plugins);
+    $plugin_selector_form['plugin_form'] = $this->buildPluginForm($plugin_selector_form, $plugin_selector_form_state);
 
-    return $element;
+    return $plugin_selector_form;
   }
 
   /**
    * Builds the form elements for the actual plugin selector.
    *
-   * @param array $root_element
-   *   The plugin's root element.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   * @param array $plugin_selector_form
+   *   The plugin selector form.
+   * @param \Drupal\Core\Form\FormStateInterface $plugin_selector_form_state
    *   The form's state.
    * @param \Drupal\Component\Plugin\PluginInspectionInterface[] $plugins
    *   The available plugins.
@@ -298,7 +309,7 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
    * @return array
    *   The selector's form elements.
    */
-  protected function buildSelector(array $root_element, FormStateInterface $form_state, array $plugins) {
+  protected function buildSelector(array $plugin_selector_form, FormStateInterface $plugin_selector_form_state, array $plugins) {
     $build['container'] = array(
       '#attributes' => array(
         'class' => array('plugin-selector-' . Html::getClass($this->getPluginId() . '-selector')),
@@ -308,7 +319,7 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
     $build['container']['plugin_id'] = array(
       '#markup' => 'This element must be overridden to provide the plugin ID.',
     );
-    $root_element_parents = $root_element['#parents'];
+    $root_element_parents = $plugin_selector_form['#parents'];
     // Compute the button's name based on its position in the form, but we
     // cannot use "][" to indicate nesting as we would usually do, because then
     // \Drupal\Core\Form\FormBuilder::buttonWasClicked() cannot recognize the
@@ -322,7 +333,7 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
       '#attributes' => array(
         'class' => array('js-hide')
       ),
-      '#limit_validation_errors' => array(array_merge($root_element['#parents'], array('select', 'plugin_id'))),
+      '#limit_validation_errors' => array(array_merge($plugin_selector_form['#parents'], array('select', 'plugin_id'))),
       '#name' => $change_button_name,
       '#submit' => array(array(get_class(), 'rebuildForm')),
       '#type' => 'submit',
diff --git a/src/Plugin/Plugin/PluginSelector/PluginSelectorBase.php b/src/Plugin/Plugin/PluginSelector/PluginSelectorBase.php
index 3661076..b9a405f 100644
--- a/src/Plugin/Plugin/PluginSelector/PluginSelectorBase.php
+++ b/src/Plugin/Plugin/PluginSelector/PluginSelectorBase.php
@@ -9,6 +9,7 @@ use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Plugin\PluginBase;
 use Drupal\plugin\DefaultPluginResolver\DefaultPluginResolverInterface;
+use Drupal\plugin\Form\SubformHelperTrait;
 use Drupal\plugin\PluginDiscovery\TypedDefinitionEnsuringPluginDiscoveryDecorator;
 use Drupal\plugin\PluginType\PluginTypeInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -22,6 +23,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  */
 abstract class PluginSelectorBase extends PluginBase implements PluginSelectorInterface, ContainerFactoryPluginInterface {
 
+  use SubformHelperTrait;
+
   /**
    * The default plugin resolver.
    *
@@ -291,7 +294,8 @@ abstract class PluginSelectorBase extends PluginBase implements PluginSelectorIn
   /**
    * {@inheritdoc}
    */
-  public function buildSelectorForm(array $form, FormStateInterface $form_state) {
+  public function buildSelectorForm(array $plugin_selector_form, FormStateInterface $plugin_selector_form_state) {
+    $this->assertSubformState($plugin_selector_form_state);
     $this->validateSelectablePluginType();
 
     return [];
diff --git a/src/Plugin/Plugin/PluginSelector/PluginSelectorInterface.php b/src/Plugin/Plugin/PluginSelector/PluginSelectorInterface.php
index f553f4c..64754c5 100644
--- a/src/Plugin/Plugin/PluginSelector/PluginSelectorInterface.php
+++ b/src/Plugin/Plugin/PluginSelector/PluginSelectorInterface.php
@@ -181,11 +181,12 @@ interface PluginSelectorInterface extends PluginInspectionInterface, Configurabl
   /**
    * Builds the selector form.
    *
-   * @param mixed[] $form
+   * @param mixed[] $plugin_selector_form
    *   Any suggested form elements to build upon. May be ignored.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The form state for $form and the return value. This often is not the
-   *   complete (global) form state.
+   * @param \Drupal\Core\Form\SubformStateInterface|\Drupal\Core\Form\FormStateInterface $plugin_selector_form_state
+   *   The form state for $plugin_selector_form and the return value. This often
+   *   is not the complete (global) form state and SHOULD be
+   *   \Drupal\Core\Form\SubformStateInterface (added in Drupal 8.2.0)
    *
    * @return mixed[]
    *   The form structure.
@@ -194,28 +195,30 @@ interface PluginSelectorInterface extends PluginInspectionInterface, Configurabl
    *   Thrown if the plugin type was not set using
    *   self::setSelectablePluginType().
    */
-  public function buildSelectorForm(array $form, FormStateInterface $form_state);
+  public function buildSelectorForm(array $plugin_selector_form, FormStateInterface $plugin_selector_form_state);
 
   /**
    * Validates the selector form.
    *
-   * @param mixed[] $form
+   * @param mixed[] $plugin_selector_form
    *   The selector form as built by static::buildSelectorForm().
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The form state for $form. This often is not the complete (global) form
-   *   state.
+   * @param \Drupal\Core\Form\SubformStateInterface|\Drupal\Core\Form\FormStateInterface $plugin_selector_form_state
+   *   The form state for $plugin_selector_form. This often is not the complete
+   *   (global) form state and SHOULD be \Drupal\Core\Form\SubformStateInterface
+   *   (added in Drupal 8.2.0).
    */
-  public function validateSelectorForm(array &$form, FormStateInterface $form_state);
+  public function validateSelectorForm(array &$plugin_selector_form, FormStateInterface $plugin_selector_form_state);
 
   /**
    * Submits the selector form.
    *
-   * @param mixed[] $form
+   * @param mixed[] $plugin_selector_form
    *   The selector form as built by static::buildSelectorForm().
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The form state for $form. This often is not the complete (global) form
-   *   state.
+   * @param \Drupal\Core\Form\SubformStateInterface|\Drupal\Core\Form\FormStateInterface $plugin_selector_form_state
+   *   The form state for $plugin_selector_form. This often is not the complete
+   *   (global) form state and SHOULD be \Drupal\Core\Form\SubformStateInterface
+   *   (added in Drupal 8.2.0)
    */
-  public function submitSelectorForm(array &$form, FormStateInterface $form_state);
+  public function submitSelectorForm(array &$plugin_selector_form, FormStateInterface $plugin_selector_form_state);
 
 }
diff --git a/src/Plugin/Plugin/PluginSelector/Radios.php b/src/Plugin/Plugin/PluginSelector/Radios.php
index 094b387..756b8d6 100644
--- a/src/Plugin/Plugin/PluginSelector/Radios.php
+++ b/src/Plugin/Plugin/PluginSelector/Radios.php
@@ -18,20 +18,21 @@ class Radios extends AdvancedPluginSelectorBase {
   /**
    * {@inheritdoc}
    */
-  public function buildSelectorForm(array $form, FormStateInterface $form_state) {
-    $form = parent::buildSelectorForm($form, $form_state);
-    $form['clear'] = array(
+  public function buildSelectorForm(array $plugin_selector_form, FormStateInterface $plugin_selector_form_state) {
+    $this->assertSubformState($plugin_selector_form_state);
+    $plugin_selector_form = parent::buildSelectorForm($plugin_selector_form, $plugin_selector_form_state);
+    $plugin_selector_form['clear'] = array(
       '#markup' => '<div style="clear: both;"></div>',
     );
 
-    return $form;
+    return $plugin_selector_form;
   }
 
   /**
    * {@inheritdoc}
    */
-  protected function buildSelector(array $root_element, FormStateInterface $form_state, array $plugins) {
-    $element = parent::buildSelector($root_element, $form_state, $plugins);
+  protected function buildSelector(array $plugin_selector_form, FormStateInterface $plugin_selector_form_state, array $plugins) {
+    $element = parent::buildSelector($plugin_selector_form, $plugin_selector_form_state, $plugins);
     /** @var \Drupal\Component\Plugin\PluginInspectionInterface[] $plugins */
     $plugin_options = [];
     foreach ($plugins as $plugin) {
diff --git a/tests/modules/plugin_test_helper/src/AdvancedPluginSelectorBasePluginSelectorForm.php b/tests/modules/plugin_test_helper/src/AdvancedPluginSelectorBasePluginSelectorForm.php
index 5575b7b..a8db435 100644
--- a/tests/modules/plugin_test_helper/src/AdvancedPluginSelectorBasePluginSelectorForm.php
+++ b/tests/modules/plugin_test_helper/src/AdvancedPluginSelectorBasePluginSelectorForm.php
@@ -6,6 +6,7 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
 use Drupal\plugin\Plugin\Plugin\PluginSelector\PluginSelectorManagerInterface;
 use Drupal\plugin\PluginDiscovery\LimitedPluginDiscoveryDecorator;
 use Drupal\plugin\PluginManager\PluginManagerDecorator;
@@ -79,13 +80,15 @@ class AdvancedPluginSelectorBasePluginSelectorForm implements ContainerInjection
       $form_state->set('plugin_selector', $plugin_selector);
     }
 
-    $form['plugin'] = $plugin_selector->buildSelectorForm([], $form_state);
+    $plugin_selector_form = [];
+    $plugin_selector_form_state = SubformState::createForSubform($plugin_selector_form, $form, $form_state);
+    $form['plugin'] = $plugin_selector->buildSelectorForm($plugin_selector_form, $plugin_selector_form_state);
     // Nest the selector in a tree if that's required.
     if ($tree) {
       $form['tree'] = array(
         '#tree' => TRUE,
+        'plugin' => $form['plugin'],
       );
-      $form['tree']['plugin'] = $form['plugin'];
       unset($form['plugin']);
     }
     $form['actions'] = array(
@@ -105,8 +108,9 @@ class AdvancedPluginSelectorBasePluginSelectorForm implements ContainerInjection
   public function validateForm(array &$form, FormStateInterface $form_state) {
     /** @var \Drupal\plugin\Plugin\Plugin\PluginSelector\PluginSelectorInterface $plugin_selector */
     $plugin_selector = $form_state->get('plugin_selector');
-    $plugin_form = isset($form['tree']) ? $form['tree']['plugin'] : $form['plugin'];
-    $plugin_selector->validateSelectorForm($plugin_form, $form_state);
+    $plugin_selector_form = isset($form['tree']) ? $form['tree']['plugin'] : $form['plugin'];
+    $plugin_selector_form_state = SubformState::createForSubform($plugin_selector_form, $form, $form_state);
+    $plugin_selector->validateSelectorForm($plugin_selector_form, $plugin_selector_form_state);
   }
 
   /**
@@ -115,8 +119,9 @@ class AdvancedPluginSelectorBasePluginSelectorForm implements ContainerInjection
   public function submitForm(array &$form, FormStateInterface $form_state) {
     /** @var \Drupal\plugin\Plugin\Plugin\PluginSelector\PluginSelectorInterface $plugin_selector */
     $plugin_selector = $form_state->get('plugin_selector');
-    $plugin_form = isset($form['tree']) ? $form['tree']['plugin'] : $form['plugin'];
-    $plugin_selector->submitSelectorForm($plugin_form, $form_state);
+    $plugin_selector_form = isset($form['tree']) ? $form['tree']['plugin'] : $form['plugin'];
+    $plugin_selector_form_state = SubformState::createForSubform($plugin_selector_form, $form, $form_state);
+    $plugin_selector->submitSelectorForm($plugin_selector_form, $plugin_selector_form_state);
     \Drupal::state()->set($this->getFormId(), $plugin_selector->getSelectedPlugin());
   }
 }
diff --git a/tests/modules/plugin_test_helper/src/Plugin/PluginTestHelper/MockConfigurablePlugin.php b/tests/modules/plugin_test_helper/src/Plugin/PluginTestHelper/MockConfigurablePlugin.php
index 85000d7..e480809 100644
--- a/tests/modules/plugin_test_helper/src/Plugin/PluginTestHelper/MockConfigurablePlugin.php
+++ b/tests/modules/plugin_test_helper/src/Plugin/PluginTestHelper/MockConfigurablePlugin.php
@@ -3,7 +3,6 @@
 namespace Drupal\plugin_test_helper\Plugin\PluginTestHelper;
 
 use Drupal\Component\Plugin\ConfigurablePluginInterface;
-use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Plugin\PluginBase;
 use Drupal\Core\Plugin\PluginFormInterface;
@@ -80,8 +79,7 @@ class MockConfigurablePlugin extends PluginBase implements ConfigurablePluginInt
    * {@inheritdoc}
    */
   public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
-    $values = NestedArray::getValue($form_state->getValues(), $form['#parents']);
-    $this->configuration['foo'] = $values['foo'];
+    $this->configuration['foo'] = $form_state->getValue('foo');
   }
 
 }
diff --git a/tests/src/Functional/Plugin/Plugin/PluginSelector/RadiosTest.php b/tests/src/Functional/Plugin/Plugin/PluginSelector/RadiosTest.php
index 52490b5..69ae18d 100644
--- a/tests/src/Functional/Plugin/Plugin/PluginSelector/RadiosTest.php
+++ b/tests/src/Functional/Plugin/Plugin/PluginSelector/RadiosTest.php
@@ -34,9 +34,9 @@ class RadiosTest extends BrowserTestBase {
    * @param bool $tree
    *   Whether to test the element with #tree = TRUE or not.
    */
-  public function doTestElement($tree) {
-    $name_prefix = $tree ? 'tree[plugin][container]' : 'container';
-    $change_button_name = $tree ? 'tree__plugin__container__select__container__change' : 'container__select__container__change';
+  protected function doTestElement($tree) {
+    $name_prefix = $tree ? 'tree[plugin][container]' : 'plugin[container]';
+    $change_button_name = $tree ? 'tree__plugin__container__select__container__change' : 'plugin__container__select__container__change';
 
     // Test the presence of default elements without available plugins.
     $path = $this->buildFormPath(['none'], $tree);
diff --git a/tests/src/Functional/Plugin/Plugin/PluginSelector/SelectListTest.php b/tests/src/Functional/Plugin/Plugin/PluginSelector/SelectListTest.php
index 2e9c7ef..c71cfac 100644
--- a/tests/src/Functional/Plugin/Plugin/PluginSelector/SelectListTest.php
+++ b/tests/src/Functional/Plugin/Plugin/PluginSelector/SelectListTest.php
@@ -34,9 +34,9 @@ class SelectListTest extends BrowserTestBase {
    * @param bool $tree
    *   Whether to test the element with #tree = TRUE or not.
    */
-  public function doTestElement($tree) {
-    $name_prefix = $tree ? 'tree[plugin][container]' : 'container';
-    $change_button_name = $tree ? 'tree__plugin__container__select__container__change' : 'container__select__container__change';
+  protected function doTestElement($tree) {
+    $name_prefix = $tree ? 'tree[plugin][container]' : 'plugin[container]';
+    $change_button_name = $tree ? 'tree__plugin__container__select__container__change' : 'plugin__container__select__container__change';
 
     // Test the presence of default elements without available plugins.
     $path = $this->buildFormPath(['none'], $tree);
diff --git a/tests/src/Unit/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBaseTest.php b/tests/src/Unit/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBaseTest.php
index 3535c33..9cbd69d 100644
--- a/tests/src/Unit/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBaseTest.php
+++ b/tests/src/Unit/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBaseTest.php
@@ -9,6 +9,7 @@ use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Form\SubformState;
+use Drupal\Core\Form\SubformStateInterface;
 use Drupal\Core\Plugin\PluginFormInterface;
 use Drupal\plugin\Plugin\Plugin\PluginSelector\AdvancedPluginSelectorBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -78,7 +79,7 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
    * @covers ::buildPluginForm
    */
   public function testBuildPluginForm() {
-    $form_state = $this->getMock(FormStateInterface::class);
+    $plugin_form_state = $this->getMock(SubformStateInterface::class);
 
     $plugin_form = array(
       '#foo' => $this->randomMachineName(),
@@ -87,19 +88,20 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
     $plugin = $this->getMockForAbstractClass(AdvancedPluginSelectorBaseUnitTestPluginFormPluginInterface::class);
     $plugin->expects($this->once())
       ->method('buildConfigurationForm')
-      ->with([], $form_state)
+      ->with($this->isType('array'), $this->isInstanceOf(FormStateInterface::class))
       ->willReturn($plugin_form);
 
 
     $method = new \ReflectionMethod($this->sut, 'buildPluginForm');
     $method->setAccessible(TRUE);
 
-    $build = $method->invoke($this->sut, $form_state);
+    $plugin_selector_form = [];
+    $build = $method->invoke($this->sut, $plugin_selector_form, $plugin_form_state);
     $this->assertSame('container', $build['#type']);
 
     $this->sut->setSelectedPlugin($plugin);
-    $build = $method->invoke($this->sut, $form_state);
-    $this->assertSame('container', $build['#type']);
+    $plugin_selector_form = [];
+    $build = $method->invoke($this->sut, $plugin_selector_form, $plugin_form_state);
     $this->assertSame($plugin_form['#foo'], $build['#foo']);
   }
 
@@ -116,11 +118,13 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
     $method = new \ReflectionMethod($this->sut, 'buildPluginForm');
     $method->setAccessible(TRUE);
 
-    $build = $method->invoke($this->sut, $form_state);
+    $plugin_selector_form = [];
+    $build = $method->invoke($this->sut, $plugin_selector_form, $form_state);
     $this->assertSame('container', $build['#type']);
 
     $this->sut->setSelectedPlugin($plugin);
-    $build = $method->invoke($this->sut, $form_state);
+    $plugin_selector_form = [];
+    $build = $method->invoke($this->sut, $plugin_selector_form, $form_state);
     $this->assertSame('container', $build['#type']);
   }
 
@@ -129,17 +133,18 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
    * @covers ::setPluginSelector
    */
   public function testBuildSelectorFormWithoutAvailablePlugins() {
-    $form = [];
-    $form_state = $this->getMock(FormStateInterface::class);
+    $plugin_selector_form = [];
+    $plugin_selector_form_state = $this->getMock(SubformStateInterface::class);
 
     $this->selectablePluginManager->expects($this->any())
       ->method('getDefinitions')
       ->willReturn([]);
 
-    $build = $this->sut->buildSelectorForm($form, $form_state);
+    $build = $this->sut->buildSelectorForm($plugin_selector_form, $plugin_selector_form_state);
     unset($build['container']['#plugin_selector_form_state_key']);
 
     $expected_build = array(
+      '#tree' => TRUE,
       '#cache' => [
         'contexts' => [],
         'tags' => [],
@@ -156,7 +161,6 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
             'processBuildSelectorForm'
           ]
         ],
-        '#tree' => TRUE,
         '#type' => 'container',
       ),
     );
@@ -168,8 +172,8 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
    * @covers ::setPluginSelector
    */
   public function testBuildSelectorFormWithOneAvailablePlugin() {
-    $form = [];
-    $form_state = $this->getMock(FormStateInterface::class);
+    $plugin_selector_form = [];
+    $plugin_selector_form_state = $this->getMock(SubformStateInterface::class);
 
     $plugin_id = $this->randomMachineName();
     $plugin = $this->getMock(PluginInspectionInterface::class);
@@ -188,10 +192,11 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
       ->method('getDefinitions')
       ->willReturn($plugin_definitions);
 
-    $build = $this->sut->buildSelectorForm($form, $form_state);
+    $build = $this->sut->buildSelectorForm($plugin_selector_form, $plugin_selector_form_state);
     unset($build['container']['#plugin_selector_form_state_key']);
 
     $expected_build = array(
+      '#tree' => TRUE,
       '#cache' => [
         'contexts' => [],
         'tags' => [],
@@ -208,7 +213,6 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
             'processBuildSelectorForm'
           ]
         ],
-        '#tree' => TRUE,
         '#type' => 'container',
       ),
     );
@@ -242,8 +246,8 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
    * @covers ::setPluginSelector
    */
   public function testBuildSelectorFormWithMultipleAvailablePlugins() {
-    $form = [];
-    $form_state = $this->getMock(FormStateInterface::class);
+    $plugin_selector_form = [];
+    $plugin_selector_form_state = $this->getMock(SubformStateInterface::class);
     $cache_contexts = [$this->randomMachineName()];
     $cache_tags = [$this->randomMachineName()];
 
@@ -273,10 +277,11 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
       ->method('getDefinitions')
       ->willReturn($plugin_definitions);
 
-    $build = $this->sut->buildSelectorForm($form, $form_state);
+    $build = $this->sut->buildSelectorForm($plugin_selector_form, $plugin_selector_form_state);
     unset($build['container']['#plugin_selector_form_state_key']);
 
     $expected_build = array(
+      '#tree' => TRUE,
       '#cache' => [
         'contexts' => $cache_contexts,
         'tags' => $cache_tags,
@@ -293,7 +298,6 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
             'processBuildSelectorForm'
           ]
         ],
-        '#tree' => TRUE,
         '#type' => 'container',
       ),
     );
@@ -304,23 +308,23 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
    * @covers ::submitSelectorForm
    */
   public function testSubmitSelectorForm() {
-    $form = array(
+    $plugin_selector_form = array(
       'container' => array(
         'plugin_form' => array(
           $this->randomMachineName() => [],
         ),
       ),
     );
-    $form_state = $this->getMock(FormStateInterface::class);
+    $plugin_selector_form_state = $this->getMock(SubformStateInterface::class);
 
     $plugin = $this->getMockForAbstractClass(AdvancedPluginSelectorBaseUnitTestPluginFormPluginInterface::class);
     $plugin->expects($this->once())
       ->method('submitConfigurationForm')
-      ->with($form['container']['plugin_form'], $form_state);
+      ->with($plugin_selector_form['container']['plugin_form'], $this->isInstanceOf(FormStateInterface::class));
 
-    $this->sut->submitSelectorForm($form, $form_state);
+    $this->sut->submitSelectorForm($plugin_selector_form, $plugin_selector_form_state);
     $this->sut->setSelectedPlugin($plugin);
-    $this->sut->submitSelectorForm($form, $form_state);
+    $this->sut->submitSelectorForm($plugin_selector_form, $plugin_selector_form_state);
   }
 
   /**
@@ -330,7 +334,8 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
     $plugin_id_a = $this->randomMachineName();
     $plugin_id_b = $this->randomMachineName();
 
-    $form = array(
+    $selector_form = array(
+      '#parents' => [],
       'container' => array(
         '#parents' => array('foo', 'bar', 'container'),
         'plugin_form' => array(
@@ -360,97 +365,73 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
 
     // The plugin is set for the first time. The plugin form must not be
     // validated, as there is no input for it yet.
-    $form_state = $this->getMock(FormStateInterface::class);
-    $form_state->expects($this->atLeastOnce())
-      ->method('getValues')
-      ->willReturn(array(
-        'foo' => array(
-          'bar' => array(
-            'container' => array(
-              'select' => array(
-                'container' => array(
-                  'plugin_id' => $plugin_id_a,
-                ),
-              ),
-            ),
-          ),
-        ),
-      ));
-    $form_state->expects($this->once())
-      ->method('setRebuild');
-    $this->sut->validateSelectorForm($form, $form_state);
+    $form_state = new FormState();
+    $form_state->setValues([
+      'container' => [
+        'select' => [
+          'container' => [
+            'plugin_id' => $plugin_id_a,
+          ],
+        ],
+      ],
+    ]);
+    $selector_form_state = SubformState::createForSubform($selector_form, $selector_form, $form_state);
+    $this->sut->validateSelectorForm($selector_form, $selector_form_state);
+    $this->assertTrue($selector_form_state->isRebuilding());
     $this->assertSame($plugin_a, $this->sut->getSelectedPlugin());
 
     // The form is validated, but the plugin remains unchanged, and as such
     // should validate its own form as well.
-    $form_state = $this->getMock(FormStateInterface::class);
-    $form_state->expects($this->atLeastOnce())
-      ->method('getValues')
-      ->willReturn(array(
-        'foo' => array(
-          'bar' => array(
-            'container' => array(
-              'select' => array(
-                'container' => array(
-                  'plugin_id' => $plugin_id_a,
-                ),
-              ),
-            ),
-          ),
-        ),
-      ));
-    $form_state->expects($this->never())
-      ->method('setRebuild');
+    $form_state = new FormState();
+    $form_state->setValues([
+      'container' => [
+        'select' => [
+          'container' => [
+            'plugin_id' => $plugin_id_a,
+          ],
+        ],
+      ],
+    ]);
+    $selector_form_state = SubformState::createForSubform($selector_form, $selector_form, $form_state);
     $plugin_a->expects($this->once())
       ->method('validateConfigurationForm')
-      ->with($form['container']['plugin_form'], $form_state);
-    $this->sut->validateSelectorForm($form, $form_state);
+      ->with($selector_form['container']['plugin_form'], $this->isInstanceOf(FormStateInterface::class));
+    $this->sut->validateSelectorForm($selector_form, $selector_form_state);
+    $this->assertFalse($selector_form_state->isRebuilding());
     $this->assertSame($plugin_a, $this->sut->getSelectedPlugin());
 
     // The plugin has changed. The plugin form must not be validated, as there
     // is no input for it yet.
-    $form_state = $this->getMock(FormStateInterface::class);
-    $form_state->expects($this->atLeastOnce())
-      ->method('getValues')
-      ->willReturn(array(
-        'foo' => array(
-          'bar' => array(
-            'container' => array(
-              'select' => array(
-                'container' => array(
-                  'plugin_id' => $plugin_id_b,
-                ),
-              ),
-            ),
-          ),
-        ),
-      ));
-    $form_state->expects($this->once())
-      ->method('setRebuild');
-    $this->sut->validateSelectorForm($form, $form_state);
+    $form_state = new FormState();
+    $form_state->setValues([
+      'container' => [
+        'select' => [
+          'container' => [
+            'plugin_id' => $plugin_id_b,
+          ],
+        ],
+      ],
+    ]);
+    $selector_form_state = SubformState::createForSubform($selector_form, $selector_form, $form_state);
+    $this->sut->validateSelectorForm($selector_form, $selector_form_state);
+    $this->assertTrue($selector_form_state->isRebuilding());
     $this->assertSame($plugin_b, $this->sut->getSelectedPlugin());
 
     // Change the plugin ID back to the original. No new plugin may be
     // instantiated, nor must the plugin form be validated.
-    $form_state = $this->getMock(FormStateInterface::class);
-    $form_state->expects($this->atLeastOnce())
-      ->method('getValues')
-      ->willReturn(array(
-        'foo' => array(
-          'bar' => array(
-            'container' => array(
-              'select' => array(
-                'container' => array(
-                  'plugin_id' => $plugin_id_a,
-                ),
-              ),
-            ),
-          ),
-        ),
-      ));
-    $form_state->expects($this->once())
-      ->method('setRebuild');
-    $this->sut->validateSelectorForm($form, $form_state);
+    $form_state = new FormState();
+    $form_state->setValues([
+      'container' => [
+        'select' => [
+          'container' => [
+            'plugin_id' => $plugin_id_a,
+          ],
+        ],
+      ],
+    ]);
+    $selector_form_state = SubformState::createForSubform($selector_form, $selector_form, $form_state);
+    $this->sut->validateSelectorForm($selector_form, $selector_form_state);
+    $this->assertTrue($selector_form_state->isRebuilding());
     $this->assertSame($plugin_a, $this->sut->getSelectedPlugin());
   }
 
@@ -502,10 +483,10 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
    * @covers ::buildOneAvailablePlugin
    */
   public function testBuildOneAvailablePlugin() {
-    $plugin_id = $this->randomMachineName();
+    $plugin_id = 'foo_bar:plugin_id';
 
     $plugin_form = array(
-      '#type' => $this->randomMachineName(),
+      '#type' => 'plugin_form',
     );
 
     $plugin = $this->getMockForAbstractClass(AdvancedPluginSelectorBaseUnitTestPluginFormPluginInterface::class);
@@ -522,35 +503,13 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
     $form_state = $this->getMock(FormStateInterface::class);
     $form = [];
 
-    $label = $this->randomMachineName();
+    $label = 'Foo Bar';
 
     $this->sut->setLabel($label);
-
-    $expected_build = array(
-      '#available_plugins' => array($plugin),
-      'select' => array(
-        'message' => [
-          '#title' => $label,
-          '#type' => 'item',
-        ],
-        'container' => array(
-          '#type' => 'container',
-          'plugin_id' => array(
-            '#type' => 'value',
-            '#value' => $plugin_id,
-          ),
-        ),
-      ),
-      'plugin_form' => array(
-          '#attributes' => array(
-            'class' => array('plugin-selector-' . Html::getId($this->pluginId) . '-plugin-form'),
-          ),
-          '#type' => 'container',
-        ) + $plugin_form,
-    );
     $build = $this->sut->buildOneAvailablePlugin($element, $form_state, $form);
     unset($build['plugin_form']['#id']);
-    $this->assertSame($expected_build, $build);
+    $this->assertSame($build['#available_plugins'], [$plugin]);
+    $this->assertSame($build['plugin_form'], $plugin_form);
   }
 
   /**
@@ -587,7 +546,7 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
     $plugin_selector->setSelectablePluginType($this->selectablePluginType);
     $plugin_selector->expects($this->once())
       ->method('buildPluginForm')
-      ->with($form_state)
+      ->with($this->isType('array'), $form_state)
       ->willReturn($plugin_form);
     $plugin_selector->expects($this->once())
       ->method('buildSelector')
@@ -600,7 +559,10 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
       'select' => $selector,
       'plugin_form' => $plugin_form,
     );
-    $this->assertEquals($expected_build, $plugin_selector->buildMultipleAvailablePlugins($element, $form_state, $form));
+
+    $method = new \ReflectionMethod(get_class($plugin_selector), 'buildMultipleAvailablePlugins');
+    $method->setAccessible(TRUE);
+    $this->assertEquals($expected_build, $method->invoke($plugin_selector, $element, $form_state, $form));
   }
 
   /**
diff --git a/tests/src/Unit/Plugin/Plugin/PluginSelector/PluginSelectorBaseTest.php b/tests/src/Unit/Plugin/Plugin/PluginSelector/PluginSelectorBaseTest.php
index a1740c3..3531e54 100644
--- a/tests/src/Unit/Plugin/Plugin/PluginSelector/PluginSelectorBaseTest.php
+++ b/tests/src/Unit/Plugin/Plugin/PluginSelector/PluginSelectorBaseTest.php
@@ -6,6 +6,7 @@ use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Plugin\Factory\FactoryInterface;
 use Drupal\Component\Plugin\PluginInspectionInterface;
 use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\SubformStateInterface;
 use Drupal\plugin\Plugin\Plugin\PluginSelector\PluginSelectorBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -167,12 +168,12 @@ class PluginSelectorBaseTest extends PluginSelectorBaseTestBase {
   public function testBuildSelectorForm() {
     $this->sut->setSelectablePluginType($this->selectablePluginType);
 
-    $form = [];
-    $form_state = new FormState();
+    $plugin_selector_form = [];
+    $plugin_selector_form_state = $this->getMock(SubformStateInterface::class);
 
-    $form = $this->sut->buildSelectorForm($form, $form_state);
+    $plugin_selector_form = $this->sut->buildSelectorForm($plugin_selector_form, $plugin_selector_form_state);
 
-    $this->assertInternalType('array', $form);
+    $this->assertInternalType('array', $plugin_selector_form);
   }
 
 }
diff --git a/tests/src/Unit/Plugin/Plugin/PluginSelector/RadiosTest.php b/tests/src/Unit/Plugin/Plugin/PluginSelector/RadiosTest.php
index 9a48782..449754c 100644
--- a/tests/src/Unit/Plugin/Plugin/PluginSelector/RadiosTest.php
+++ b/tests/src/Unit/Plugin/Plugin/PluginSelector/RadiosTest.php
@@ -4,6 +4,7 @@ namespace Drupal\Tests\plugin\Unit\Plugin\Plugin\PluginSelector;
 
 use Drupal\Component\Plugin\PluginInspectionInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformStateInterface;
 use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
 use Drupal\plugin\Plugin\Plugin\PluginSelector\AdvancedPluginSelectorBase;
 use Drupal\plugin\Plugin\Plugin\PluginSelector\Radios;
@@ -57,14 +58,14 @@ class RadiosTest extends PluginSelectorBaseTestBase {
    * @covers ::buildSelectorForm
    */
   public function testBuildSelectorFormWithoutAvailablePlugins() {
-    $form = [];
-    $form_state = $this->getMock(FormStateInterface::class);
+    $plugin_selector_form = [];
+    $plugin_selector_form_state = $this->getMock(SubformStateInterface::class);
 
     $this->selectablePluginManager->expects($this->any())
       ->method('getDefinitions')
       ->willReturn([]);
 
-    $build = $this->sut->buildSelectorForm($form, $form_state);
+    $build = $this->sut->buildSelectorForm($plugin_selector_form, $plugin_selector_form_state);
 
     $this->assertArrayHasKey('clear', $build);
   }
