diff --git a/src/Plugin/Field/FieldWidget/PluginSelector.php b/src/Plugin/Field/FieldWidget/PluginSelector.php
index c347f35..cf8293f 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 */
@@ -115,7 +124,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 3233ef6..aef4f5d 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;
@@ -113,18 +114,20 @@ 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']);
 
-    if (count($element['#available_plugins']) == 0) {
-      return $plugin_selector->buildNoAvailablePlugins($element, $form_state);
+    $plugin_selector_form_state = SubformState::createForSubform($plugin_selector_form, $complete_form, $complete_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) {
-      return $plugin_selector->buildOneAvailablePlugin($element, $form_state);
+    elseif (count($plugin_selector_form['#available_plugins']) == 1) {
+      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);
     }
   }
 
@@ -132,8 +135,7 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
    * {@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')));
+    $plugin_id = $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
@@ -156,7 +158,9 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
     }
     // 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 = &$form['container']['plugin_form'];
+      $selected_plugin_form_state = SubformState::createForSubform($selected_plugin_form, $form, $form_state);
+      $selected_plugin->validateConfigurationForm($selected_plugin_form, $selected_plugin_form_state);
     }
   }
 
@@ -164,26 +168,28 @@ abstract class AdvancedPluginSelectorBase extends PluginSelectorBase implements
    * {@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);
+    $selected_plugin = $this->getSelectedPlugin();
+    if ($this->getCollectPluginConfiguration() && $selected_plugin instanceof PluginFormInterface) {
+      $selected_plugin_form = &$form['container']['plugin_form'];
+      $selected_plugin_form_state = SubformState::createForSubform($selected_plugin_form, $form, $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']));
@@ -194,29 +200,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',
     );
@@ -236,8 +245,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.
@@ -245,40 +254,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'];
+  public 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.
@@ -286,7 +295,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')),
@@ -296,7 +305,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
@@ -310,7 +319,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/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/Unit/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBaseTest.php b/tests/src/Unit/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBaseTest.php
index b659d5e..5765530 100644
--- a/tests/src/Unit/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBaseTest.php
+++ b/tests/src/Unit/Plugin/Plugin/PluginSelector/AdvancedPluginSelectorBaseTest.php
@@ -86,7 +86,7 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
     $plugin = $this->getMockForAbstractClass(AdvancedPluginSelectorBaseUnitTestPluginFormPluginInterface::class);
     $plugin->expects($this->once())
       ->method('buildConfigurationForm')
-      ->with([], $form_state)
+      ->with([], $this->isInstanceOf(FormStateInterface::class))
       ->willReturn($plugin_form);
 
 
@@ -315,7 +315,7 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
     $plugin = $this->getMockForAbstractClass(AdvancedPluginSelectorBaseUnitTestPluginFormPluginInterface::class);
     $plugin->expects($this->once())
       ->method('submitConfigurationForm')
-      ->with($form['container']['plugin_form'], $form_state);
+      ->with($form['container']['plugin_form'], $this->isInstanceOf(FormStateInterface::class));
 
     $this->sut->submitSelectorForm($form, $form_state);
     $this->sut->setSelectedPlugin($plugin);
@@ -402,7 +402,7 @@ class AdvancedPluginSelectorBaseTest extends PluginSelectorBaseTestBase {
       ->method('setRebuild');
     $plugin_a->expects($this->once())
       ->method('validateConfigurationForm')
-      ->with($form['container']['plugin_form'], $form_state);
+      ->with($form['container']['plugin_form'], $this->isInstanceOf(FormStateInterface::class));
     $this->sut->validateSelectorForm($form, $form_state);
     $this->assertSame($plugin_a, $this->sut->getSelectedPlugin());
 
