diff --git a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
index ba054a6..08ada45 100644
--- a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
+++ b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
@@ -234,20 +234,15 @@ public function settingsForm(array $form, FormStateInterface $form_state, Editor
   /**
    * {@inheritdoc}
    */
-  public function settingsFormSubmit(array $form, FormStateInterface $form_state) {
-    // Modify the toolbar settings by reference. The values in
-    // $form_state->getValue(array('editor', 'settings')) will be saved directly
-    // by editor_form_filter_admin_format_submit().
-    $toolbar_settings = &$form_state->getValue(array('editor', 'settings', 'toolbar'));
-
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
     // The rows key is not built into the form structure, so decode the button
     // groups data into this new key and remove the button_groups key.
-    $toolbar_settings['rows'] = json_decode($toolbar_settings['button_groups'], TRUE);
-    unset($toolbar_settings['button_groups']);
+    $form_state->setValue(['toolbar', 'rows'], json_decode($form_state->getValue(['toolbar', 'button_groups']), TRUE));
+    $form_state->unsetValue(['toolbar', 'button_groups']);
 
     // Remove the plugin settings' vertical tabs state; no need to save that.
-    if ($form_state->hasValue(array('editor', 'settings', 'plugins'))) {
-      $form_state->unsetValue(array('editor', 'settings', 'plugin_settings'));
+    if ($form_state->hasValue('plugins')) {
+      $form_state->unsetValue('plugin_settings');
     }
   }
 
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 8df1f04..1db3039 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Utility\Html;
+use Drupal\Core\Form\SubformState;
 use Drupal\editor\Entity\Editor;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
@@ -170,11 +171,11 @@ function editor_form_filter_format_form_alter(&$form, FormStateInterface $form_s
   if ($editor) {
     /** @var $plugin \Drupal\editor\Plugin\EditorPluginInterface */
     $plugin = $manager->createInstance($editor->getEditor());
-    $settings_form = array();
-    $settings_form['#element_validate'][] = array($plugin, 'validateConfigurationForm');
-    $form['editor']['settings']['subform'] = $plugin->buildConfigurationForm($settings_form, $form_state);
+    $form_state->set('editor_plugin', $plugin);
+    $form['editor']['settings']['subform'] = [];
+    $subform_state = SubformState::createForSubform($form['editor']['settings']['subform'], $form, $form_state);
+    $form['editor']['settings']['subform'] = $plugin->buildConfigurationForm($form['editor']['settings']['subform'], $subform_state);
     $form['editor']['settings']['subform']['#parents'] = array('editor', 'settings');
-    $form['actions']['submit']['#submit'][] = array($plugin, 'submitConfigurationForm');
   }
 
   $form['#validate'][] = 'editor_form_filter_admin_format_validate';
@@ -214,6 +215,11 @@ function editor_form_filter_admin_form_ajax($form, FormStateInterface $form_stat
  * Additional validate handler for filter_format_form().
  */
 function editor_form_filter_admin_format_validate($form, FormStateInterface $form_state) {
+  if ($editor_plugin = $form_state->get('editor_plugin')) {
+    $subform_state = SubformState::createForSubform($form['editor']['settings']['subform'], $form, $form_state);
+    $editor_plugin->validateConfigurationForm($form['editor']['settings']['subform'], $subform_state);
+  }
+
   // This validate handler is not applicable when using the 'Configure' button.
   if ($form_state->getTriggeringElement()['#name'] === 'editor_configure') {
     return;
@@ -240,6 +246,11 @@ function editor_form_filter_admin_format_submit($form, FormStateInterface $form_
     $original_editor->delete();
   }
 
+  if ($editor_plugin = $form_state->get('editor_plugin')) {
+    $subform_state = SubformState::createForSubform($form['editor']['settings']['subform'], $form, $form_state);
+    $editor_plugin->submitConfigurationForm($form['editor']['settings']['subform'], $subform_state);
+  }
+
   // Create a new editor or update the existing editor.
   if ($editor = $form_state->get('editor')) {
     // Ensure the text format is set: when creating a new text format, this
diff --git a/core/modules/editor/src/Plugin/EditorBase.php b/core/modules/editor/src/Plugin/EditorBase.php
index bd429d1..3456e44 100644
--- a/core/modules/editor/src/Plugin/EditorBase.php
+++ b/core/modules/editor/src/Plugin/EditorBase.php
@@ -3,6 +3,7 @@
 namespace Drupal\editor\Plugin;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformStateInterface;
 use Drupal\Core\Plugin\PluginBase;
 use Drupal\editor\Entity\Editor;
 
@@ -61,6 +62,9 @@ public function settingsFormSubmit(array $form, FormStateInterface $form_state)
    * {@inheritdoc}
    */
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    if ($form_state instanceof SubformStateInterface) {
+      $form_state = $form_state->getCompleteFormState();
+    }
     return $this->settingsForm($form, $form_state, $form_state->get('editor'));
   }
 
@@ -68,6 +72,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    * {@inheritdoc}
    */
   public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
+    if ($form_state instanceof SubformStateInterface) {
+      $form_state = $form_state->getCompleteFormState();
+    }
     return $this->settingsFormValidate($form, $form_state);
   }
 
@@ -75,6 +82,9 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form
    * {@inheritdoc}
    */
   public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    if ($form_state instanceof SubformStateInterface) {
+      $form_state = $form_state->getCompleteFormState();
+    }
     return $this->settingsFormSubmit($form, $form_state);
   }
 
diff --git a/core/modules/editor/tests/src/Unit/EditorBaseTest.php b/core/modules/editor/tests/src/Unit/EditorBaseTest.php
index a088fb0..e80e17f 100644
--- a/core/modules/editor/tests/src/Unit/EditorBaseTest.php
+++ b/core/modules/editor/tests/src/Unit/EditorBaseTest.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
 use Drupal\editor\Entity\Editor;
 use Drupal\editor\Plugin\EditorBase;
 use Drupal\Tests\UnitTestCase;
@@ -23,29 +24,36 @@ public function testBc() {
     $form_state = new FormState();
     $form_state->set('editor', $this->prophesize(Editor::class)->reveal());
     $editor_plugin = new BcEditor([], 'editor_plugin', []);
+    $form['#parents'] = [];
+    $form['nested'] = ['#parents' => ['nested']];
+    $subform_state = SubformState::createForSubform($form['nested'], $form, $form_state);
 
     // settingsForm() is deprecated in favor of buildConfigurationForm().
     $this->assertSame(
       $editor_plugin->settingsForm([], clone $form_state, $this->prophesize(Editor::class)->reveal()),
-      $editor_plugin->buildConfigurationForm([], clone $form_state)
+      $editor_plugin->buildConfigurationForm([], clone $subform_state)
     );
 
     // settingsFormValidate() is deprecated in favor of
     // validateConfigurationForm().
     $form = [];
     $form_state_a = clone $form_state;
-    $form_state_b = clone $form_state;
+    $form_state_b = clone $subform_state;
     $editor_plugin->settingsFormValidate($form, $form_state_a, $this->prophesize(Editor::class)->reveal());
     $editor_plugin->validateConfigurationForm($form, $form_state_b);
-    $this->assertEquals($form_state_a, $form_state_b);
+    $this->assertEquals('bar', $form_state_a->getValue(['nested', 'foo']));
+    $this->assertEquals('bar', $form_state_b->getValue('foo'));
+    $this->assertEquals($form_state_a, $form_state_b->getCompleteFormState());
 
     // settingsFormSubmit() is deprecated in favor of submitConfigurationForm().
     $form = [];
     $form_state_a = clone $form_state;
-    $form_state_b = clone $form_state;
+    $form_state_b = clone $subform_state;
     $editor_plugin->settingsFormSubmit($form, $form_state_a, $this->prophesize(Editor::class)->reveal());
     $editor_plugin->submitConfigurationForm($form, $form_state_b);
-    $this->assertEquals($form_state_a, $form_state_b);
+    $this->assertEquals('baz', $form_state_a->getValue(['nested', 'bar']));
+    $this->assertEquals('baz', $form_state_b->getValue('bar'));
+    $this->assertEquals($form_state_a, $form_state_b->getCompleteFormState());
   }
 
 }
@@ -57,11 +65,11 @@ public function settingsForm(array $form, FormStateInterface $form_state, Editor
   }
 
   public function settingsFormValidate(array $form, FormStateInterface $form_state) {
-    $form_state->setValue('foo', 'bar');
+    $form_state->setValue(['nested', 'foo'], 'bar');
   }
 
   public function settingsFormSubmit(array $form, FormStateInterface $form_state) {
-    $form_state->setValue('bar', 'baz');
+    $form_state->setValue(['nested', 'bar'], 'baz');
   }
 
   public function getJSSettings(Editor $editor) {
