diff --git a/core/lib/Drupal/Core/Field/FieldConfigBase.php b/core/lib/Drupal/Core/Field/FieldConfigBase.php
index e9032e8cc9f6708f4c2a62663e36fea952bc327e..ca9a5b5285ead4c5285ec4cf6b7a091b670f6405 100644
--- a/core/lib/Drupal/Core/Field/FieldConfigBase.php
+++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
@@ -6,7 +6,6 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
-use Drupal\field\Entity\FieldStorageConfig;
 
 /**
  * Base class for configurable field definitions.
@@ -475,11 +474,6 @@ public function __sleep() {
     // recalculated.
     unset($properties['itemDefinition'], $properties['original']);
 
-    // Field storage can be recalculated if it's not new.
-    if (array_key_exists('fieldStorage', $properties) && $properties['fieldStorage'] instanceof FieldStorageConfig && !$properties['fieldStorage']->isNew()) {
-      unset($properties['fieldStorage']);
-    }
-
     return array_keys($properties);
   }
 
diff --git a/core/lib/Drupal/Core/Form/SubformState.php b/core/lib/Drupal/Core/Form/SubformState.php
index 50f39117c25859522406767a618268a4bcc87e5d..bec19be6205177ecd8ddbc85dacdbda823c5d0c1 100644
--- a/core/lib/Drupal/Core/Form/SubformState.php
+++ b/core/lib/Drupal/Core/Form/SubformState.php
@@ -34,8 +34,10 @@ class SubformState extends FormStateDecoratorBase implements SubformStateInterfa
    *   The subform's parent form.
    * @param \Drupal\Core\Form\FormStateInterface $parent_form_state
    *   The parent form state.
+   * @param \Drupal\Core\Form\FormInterface|null $subformFormObject
+   *   The subform form object when it's not the same as the parent form.
    */
-  protected function __construct(array &$subform, array &$parent_form, FormStateInterface $parent_form_state) {
+  protected function __construct(array &$subform, array &$parent_form, FormStateInterface $parent_form_state, protected readonly ?FormInterface $subformFormObject = NULL) {
     $this->decoratedFormState = $parent_form_state;
     $this->parentForm = $parent_form;
     $this->subform = $subform;
@@ -50,11 +52,13 @@ protected function __construct(array &$subform, array &$parent_form, FormStateIn
    *   The subform's parent form.
    * @param \Drupal\Core\Form\FormStateInterface $parent_form_state
    *   The parent form state.
+   * @param \Drupal\Core\Form\FormInterface|null $subform_form_object
+   *   The subform form object when it's not the same as the parent form.
    *
    * @return static
    */
-  public static function createForSubform(array &$subform, array &$parent_form, FormStateInterface $parent_form_state) {
-    return new static($subform, $parent_form, $parent_form_state);
+  public static function createForSubform(array &$subform, array &$parent_form, FormStateInterface $parent_form_state, ?FormInterface $subform_form_object = NULL) {
+    return new static($subform, $parent_form, $parent_form_state, $subform_form_object);
   }
 
   /**
@@ -151,4 +155,15 @@ public function setErrorByName($name, $message = '') {
     return $this;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormObject() {
+    if ($this->subformFormObject) {
+      return $this->subformFormObject;
+    }
+
+    return parent::getFormObject();
+  }
+
 }
diff --git a/core/modules/comment/tests/src/Functional/CommentFieldsTest.php b/core/modules/comment/tests/src/Functional/CommentFieldsTest.php
index f67ebea79c7a45df4488ede97aedc464fb22953f..ad42c61a6553021ff816fbd12dd0be50443ab621 100644
--- a/core/modules/comment/tests/src/Functional/CommentFieldsTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentFieldsTest.php
@@ -162,8 +162,7 @@ public function testCommentFieldCreate() {
 
     // Try to save the comment field without selecting a comment type.
     $edit = [];
-    $this->drupalGet('admin/config/people/accounts/add-storage/user/field_user_comment');
-    $this->submitForm($edit, 'Continue');
+    $this->submitForm($edit, 'Update settings');
     // We should get an error message.
     $this->assertSession()->pageTextContains('The submitted value in the Comment type element is not allowed.');
 
@@ -178,10 +177,10 @@ public function testCommentFieldCreate() {
 
     // Select a comment type and try to save again.
     $edit = [
-      'settings[comment_type]' => 'user_comment_type',
+      'field_storage[subform][settings][comment_type]' => 'user_comment_type',
     ];
-    $this->drupalGet('admin/config/people/accounts/add-storage/user/field_user_comment');
-    $this->submitForm($edit, 'Continue');
+    $this->drupalGet('admin/config/people/accounts/add-field/user/field_user_comment');
+    $this->submitForm($edit, 'Update settings');
     // We shouldn't get an error message.
     $this->assertSession()->pageTextNotContains('The submitted value in the Comment type element is not allowed.');
 
diff --git a/core/modules/comment/tests/src/Functional/CommentNonNodeTest.php b/core/modules/comment/tests/src/Functional/CommentNonNodeTest.php
index c66865da10175bfe046e05327f918fbc01a4eb16..ca5b8f111ade2dee3262b15d7eac30809fd582b2 100644
--- a/core/modules/comment/tests/src/Functional/CommentNonNodeTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentNonNodeTest.php
@@ -277,8 +277,6 @@ public function testCommentFunctionality() {
     $this->assertSession()->statusCodeEquals(200);
     $this->assertSession()->fieldNotExists('edit-default-value-input-comment-und-0-status-0');
     // Test that field to change cardinality is not available.
-    $this->drupalGet('entity_test/structure/entity_test/fields/entity_test.entity_test.comment/storage');
-    $this->assertSession()->statusCodeEquals(200);
     $this->assertSession()->fieldNotExists('cardinality_number');
     $this->assertSession()->fieldNotExists('cardinality');
 
@@ -439,7 +437,7 @@ public function testCommentFunctionality() {
 
     // Add a new comment field.
     $storage_edit = [
-      'settings[comment_type]' => 'foobar',
+      'field_storage[subform][settings][comment_type]' => 'foobar',
     ];
     $this->fieldUIAddNewField('entity_test/structure/entity_test', 'foobar', 'Foobar', 'comment', $storage_edit);
 
diff --git a/core/modules/datetime/tests/src/Functional/DateTimeFieldTest.php b/core/modules/datetime/tests/src/Functional/DateTimeFieldTest.php
index ca201a398f72376e507bca26ac4e4f15f17969fa..9bcd0acc7c13d551592465165a04264a37a62db6 100644
--- a/core/modules/datetime/tests/src/Functional/DateTimeFieldTest.php
+++ b/core/modules/datetime/tests/src/Functional/DateTimeFieldTest.php
@@ -932,8 +932,8 @@ public function testDateStorageSettings() {
     ];
     $this->drupalGet('node/add/date_content');
     $this->submitForm($edit, 'Save');
-    $this->drupalGet('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name . '/storage');
-    $this->assertSession()->elementsCount('xpath', "//*[@id='edit-settings-datetime-type' and contains(@disabled, 'disabled')]", 1);
+    $this->drupalGet('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name);
+    $this->assertSession()->elementsCount('xpath', "//*[@name='field_storage[subform][settings][datetime_type]' and contains(@disabled, 'disabled')]", 1);
   }
 
 }
diff --git a/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php b/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php
index f19d18fd1b1e543503308ddf2b16f409b2370da9..ecbf3aa335b66316ba8e170eec242eac5b92d673 100644
--- a/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php
+++ b/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php
@@ -1422,8 +1422,8 @@ public function testDateStorageSettings() {
     ];
     $this->drupalGet('node/add/date_content');
     $this->submitForm($edit, 'Save');
-    $this->drupalGet('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name . '/storage');
-    $this->assertSession()->elementsCount('xpath', "//*[@id='edit-settings-datetime-type' and contains(@disabled, 'disabled')]", 1);
+    $this->drupalGet('admin/structure/types/manage/date_content/fields/node.date_content.' . $field_name);
+    $this->assertSession()->elementsCount('xpath', "//*[@name='field_storage[subform][settings][datetime_type]' and contains(@disabled, 'disabled')]", 1);
   }
 
 }
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index a5d0ee3b585f8461f2229fa03659884a4a26865d..f2c600715641b424659ee0d0155ed5ffdd64bbf4 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -448,3 +448,51 @@ function field_field_config_presave(FieldConfigInterface $field) {
     ]);
   }
 }
+
+/**
+ * Entity form builder for field config edit form.
+ *
+ * @param string $entity_type_id
+ *   The entity type identifier.
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity updated with the submitted values.
+ * @param array $form
+ *   The complete form array.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ *   The current state of the form.
+ *
+ * @see \Drupal\field_ui\Form\FieldConfigEditForm::form
+ * @see \Drupal\field_ui\Form\FieldConfigEditForm::copyFormValuesToEntity
+ */
+function field_form_field_config_edit_form_entity_builder($entity_type_id, $entity, &$form, FormStateInterface $form_state) {
+  assert($entity instanceof FieldConfigInterface);
+  $previous_field_storage = $form_state->getFormObject()->getEntity()->getFieldStorageDefinition();
+  assert($previous_field_storage instanceof FieldStorageConfigInterface);
+
+  // Act on all sub-types of the entity_reference field type.
+  /** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
+  $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
+  $item_class = 'Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem';
+  $class = $field_type_manager->getPluginClass($entity->getFieldStorageDefinition()->getType());
+  if ($class !== $item_class && !is_subclass_of($class, $item_class)) {
+    return;
+  }
+
+  // Update handler settings when target_type is changed.
+  if ($entity->getFieldStorageDefinition()->getSetting('target_type') !== $previous_field_storage->getSetting('target_type')) {
+    // @see field_field_storage_config_update().
+    $entity->setSetting('handler_settings', []);
+    // @see field_field_config_presave().
+    field_field_config_create($entity);
+
+    // Store updated settings in form state so that the form state can be copied
+    // directly to the entity.
+    $form_state->setValue('settings', $entity->getSettings());
+
+    // Unset user input for the settings because they are not valid after the
+    // target type has changed.
+    $user_input = $form_state->getUserInput();
+    unset($user_input['settings']);
+    $form_state->setUserInput($user_input);
+  }
+}
diff --git a/core/modules/field/src/Entity/FieldConfig.php b/core/modules/field/src/Entity/FieldConfig.php
index 0c858c334a8b3c69503c6afad7955841648136b3..ca59414c33060a600bcda5156d2464348c29267e 100644
--- a/core/modules/field/src/Entity/FieldConfig.php
+++ b/core/modules/field/src/Entity/FieldConfig.php
@@ -263,7 +263,6 @@ protected function linkTemplates() {
     $link_templates = parent::linkTemplates();
     if (\Drupal::moduleHandler()->moduleExists('field_ui')) {
       $link_templates["{$this->entity_type}-field-edit-form"] = 'entity.field_config.' . $this->entity_type . '_field_edit_form';
-      $link_templates["{$this->entity_type}-storage-edit-form"] = 'entity.field_config.' . $this->entity_type . '_storage_edit_form';
       $link_templates["{$this->entity_type}-field-delete-form"] = 'entity.field_config.' . $this->entity_type . '_field_delete_form';
 
       if (isset($link_templates['config-translation-overview'])) {
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
index ad2a135a2172fcf900815b1158794bc8ddc368ba..7f4d5f31e9ab97b0603fe1ab910976d1406ed93d 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
@@ -75,7 +75,8 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
    * {@inheritdoc}
    */
   public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
-    $form['test_field_storage_setting'] = [
+    $element = [];
+    $element['test_field_storage_setting'] = [
       '#type' => 'textfield',
       '#title' => $this->t('Field test field storage setting'),
       '#default_value' => $this->getSetting('test_field_storage_setting'),
@@ -83,14 +84,15 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state
       '#description' => $this->t('A dummy form element to simulate field storage setting.'),
     ];
 
-    return $form;
+    return $element;
   }
 
   /**
    * {@inheritdoc}
    */
   public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
-    $form['test_field_setting'] = [
+    $element = [];
+    $element['test_field_setting'] = [
       '#type' => 'textfield',
       '#title' => $this->t('Field test field setting'),
       '#default_value' => $this->getSetting('test_field_setting'),
@@ -98,7 +100,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
       '#description' => $this->t('A dummy form element to simulate field setting.'),
     ];
 
-    return $form;
+    return $element;
   }
 
   /**
diff --git a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
index 12362a73ffb4284932227b30f4ae5afdeef2e044..9f3790b9995d0b9a1d8d4749f178ab2c649160d1 100644
--- a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
+++ b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
@@ -118,9 +118,9 @@ public function testFieldAdminHandler() {
 
     // Set to unlimited.
     $edit = [
-      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+      'field_storage[subform][cardinality]' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
     ];
-    $this->submitForm($edit, 'Continue');
+    $this->submitForm($edit, 'Update settings');
 
     // Add the view to the test field.
     $edit = [
@@ -204,12 +204,11 @@ public function testFieldAdminHandler() {
     Vocabulary::create(['vid' => 'tags', 'name' => 'tags'])->save();
     $taxonomy_term_field_name = $this->createEntityReferenceField('taxonomy_term', ['tags']);
     $field_path = 'node.' . $this->type . '.field_' . $taxonomy_term_field_name;
-    $this->drupalGet($bundle_path . '/fields/' . $field_path . '/storage');
+    $this->drupalGet($bundle_path . '/fields/' . $field_path);
     $edit = [
-      'cardinality' => -1,
+      'field_storage[subform][cardinality]' => -1,
     ];
-    $this->submitForm($edit, 'Save');
-    $this->drupalGet($bundle_path . '/fields/' . $field_path);
+    $this->submitForm($edit, 'Update settings');
     $term_name = $this->randomString();
     $result = \Drupal::entityQuery('taxonomy_term')
       ->condition('name', $term_name)
@@ -382,7 +381,7 @@ protected function createEntityReferenceField($target_type, $bundles = []) {
     $field_name = $this->randomMachineName();
 
     $storage_edit = $field_edit = [];
-    $storage_edit['settings[target_type]'] = $target_type;
+    $storage_edit['field_storage[subform][settings][target_type]'] = $target_type;
     foreach ($bundles as $bundle) {
       $field_edit['settings[handler_settings][target_bundles][' . $bundle . ']'] = TRUE;
     }
diff --git a/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php b/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php
index 84f20b3501be711bf33a69648982567d85712047..74f981d84a2d05b9e930718ab1b299113d2d4421 100644
--- a/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php
+++ b/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php
@@ -130,13 +130,10 @@ public function testFieldAdminHandler() {
     $this->fieldUIAddNewFieldJS(NULL, 'test', 'Test', 'entity_reference', FALSE);
 
     // Node should be selected by default.
-    $this->assertSession()->fieldValueEquals('settings[target_type]', 'node');
+    $this->assertSession()->fieldValueEquals('field_storage[subform][settings][target_type]', 'node');
 
     // Check that all entity types can be referenced.
-    $this->assertFieldSelectOptions('settings[target_type]', array_keys(\Drupal::entityTypeManager()->getDefinitions()));
-
-    // Second step: 'Field settings' form.
-    $this->submitForm([], 'Continue');
+    $this->assertFieldSelectOptions('field_storage[subform][settings][target_type]', array_keys(\Drupal::entityTypeManager()->getDefinitions()));
 
     // The base handler should be selected by default.
     $this->assertSession()->fieldValueEquals('settings[handler]', 'default:node');
@@ -264,23 +261,19 @@ public function testFieldAdminHandler() {
     // Switch the target type to 'taxonomy_term' and check that the settings
     // specific to its selection handler are displayed.
     $field_name = 'node.' . $this->type . '.field_test';
-    $edit = [
-      'settings[target_type]' => 'taxonomy_term',
-    ];
-    $this->drupalGet($bundle_path . '/fields/' . $field_name . '/storage');
-    $this->submitForm($edit, 'Save');
     $this->drupalGet($bundle_path . '/fields/' . $field_name);
+    $page->findField('field_storage[subform][settings][target_type]')->setValue('taxonomy_term');
+    $this->assertSession()->assertWaitOnAjaxRequest();
     $this->assertSession()->fieldExists('settings[handler_settings][auto_create]');
+    $this->assertSession()->fieldValueEquals('settings[handler]', 'default:taxonomy_term');
 
     // Switch the target type to 'user' and check that the settings specific to
     // its selection handler are displayed.
     $field_name = 'node.' . $this->type . '.field_test';
-    $edit = [
-      'settings[target_type]' => 'user',
-    ];
-    $this->drupalGet($bundle_path . '/fields/' . $field_name . '/storage');
-    $this->submitForm($edit, 'Save');
     $this->drupalGet($bundle_path . '/fields/' . $field_name);
+    $target_type_input = $assert_session->fieldExists('field_storage[subform][settings][target_type]');
+    $target_type_input->setValue('user');
+    $assert_session->assertWaitOnAjaxRequest();
     $this->assertSession()->fieldValueEquals('settings[handler_settings][filter][type]', '_none');
     $this->assertSession()->fieldValueEquals('settings[handler_settings][sort][field]', '_none');
     $assert_session->optionNotExists('settings[handler_settings][sort][field]', 'nid');
@@ -295,11 +288,8 @@ public function testFieldAdminHandler() {
 
     // Switch the target type to 'node'.
     $field_name = 'node.' . $this->type . '.field_test';
-    $edit = [
-      'settings[target_type]' => 'node',
-    ];
-    $this->drupalGet($bundle_path . '/fields/' . $field_name . '/storage');
-    $this->submitForm($edit, 'Save');
+    $this->drupalGet($bundle_path . '/fields/' . $field_name);
+    $page->findField('field_storage[subform][settings][target_type]')->setValue('node');
 
     // Try to select the views handler.
     $this->drupalGet($bundle_path . '/fields/' . $field_name);
@@ -328,16 +318,13 @@ public function testFieldAdminHandler() {
     $assert_session->pageTextContains('Saved Test configuration.');
 
     // Switch the target type to 'entity_test'.
-    $edit = [
-      'settings[target_type]' => 'entity_test',
-    ];
-    $this->drupalGet($bundle_path . '/fields/' . $field_name . '/storage');
-    $this->submitForm($edit, 'Save');
     $this->drupalGet($bundle_path . '/fields/' . $field_name);
+    $page->findField('field_storage[subform][settings][target_type]')->setValue('entity_test');
     $page->findField('settings[handler]')->setValue('views');
-    $assert_session
-      ->waitForField('settings[handler_settings][view][view_and_display]')
-      ->setValue('test_entity_reference_entity_test:entity_reference_1');
+    $assert_session->assertWaitOnAjaxRequest();
+    $page
+      ->findField('settings[handler_settings][view][view_and_display]')
+      ->selectOption('test_entity_reference_entity_test:entity_reference_1');
     $edit = [
       'required' => FALSE,
     ];
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index 46c983503a5d0db7e0dda851487ecbee216f566e..b8f4dc6f70be20e4987a112b9e92709b72a40638 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -7,12 +7,15 @@
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Entity\EntityViewModeInterface;
 use Drupal\Core\Entity\EntityFormModeInterface;
 use Drupal\Core\Url;
+use Drupal\field\FieldConfigInterface;
 use Drupal\field_ui\FieldUI;
 use Drupal\field_ui\Form\FieldConfigEditForm;
+use Drupal\field_ui\Form\FieldStorageConfigEditForm;
 use Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask;
 
 /**
@@ -85,6 +88,7 @@ function field_ui_entity_type_build(array &$entity_types) {
   $entity_types['field_config']->setListBuilderClass('Drupal\field_ui\FieldConfigListBuilder');
 
   $entity_types['field_storage_config']->setFormClass('edit', 'Drupal\field_ui\Form\FieldStorageConfigEditForm');
+  $entity_types['field_storage_config']->setFormClass('default', FieldStorageConfigEditForm::class);
   $entity_types['field_storage_config']->setListBuilderClass('Drupal\field_ui\FieldStorageConfigListBuilder');
   $entity_types['field_storage_config']->setLinkTemplate('collection', '/admin/reports/fields');
 
@@ -155,7 +159,7 @@ function field_ui_entity_operation(EntityInterface $entity) {
       $operations['manage-display'] = [
         'title' => t('Manage display'),
         'weight' => 25,
-        'url' => Url::fromRoute("entity.entity_view_display.{$bundle_of}.default", [
+        'url' => Url::fromRoute("entity.entity_view_display.$bundle_of.default", [
           $entity->getEntityTypeId() => $entity->id(),
         ]),
       ];
@@ -279,3 +283,21 @@ function field_ui_form_manage_field_form_submit($form, FormStateInterface $form_
     $form_state->setRedirectUrl($route_info);
   }
 }
+
+/**
+ * Implements hook_form_FORM_ID_alter() for field_config_edit_form.
+ */
+function field_ui_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state) {
+  $field_config = $form_state->getFormObject()->getEntity();
+  assert($field_config instanceof FieldConfigInterface);
+
+  $form_id = 'field_storage_config_edit_form';
+  $hook = 'form_' . $form_id;
+
+  $field_storage_form = \Drupal::entityTypeManager()->getFormObject('field_storage_config', $form_state->getFormObject()->getOperation());
+  $field_storage_form->setEntity($field_config->getFieldStorageDefinition());
+  $subform_state = SubformState::createForSubform($form['field_storage']['subform'], $form, $form_state, $field_storage_form);
+
+  \Drupal::moduleHandler()->alterDeprecated('Use hook_form_field_config_edit_form_alter() instead. See https://www.drupal.org/node/3386675.', $hook, $form['field_storage']['subform'], $subform_state, $form_id);
+  \Drupal::theme()->alter($hook, $form['field_storage']['subform'], $subform_state, $form_id);
+}
diff --git a/core/modules/field_ui/src/Controller/FieldConfigAddController.php b/core/modules/field_ui/src/Controller/FieldConfigAddController.php
index 2a5a8b81de48cd0dbe9090e6fdc832229a49023d..8cb929608b0ebd024e3990a744f7c77972e077c3 100644
--- a/core/modules/field_ui/src/Controller/FieldConfigAddController.php
+++ b/core/modules/field_ui/src/Controller/FieldConfigAddController.php
@@ -5,8 +5,6 @@
 namespace Drupal\field_ui\Controller;
 
 use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
-use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\TempStore\PrivateTempStore;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -23,15 +21,9 @@ final class FieldConfigAddController extends ControllerBase {
    *
    * @param \Drupal\Core\TempStore\PrivateTempStore $tempStore
    *   The private tempstore.
-   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $fieldTypeManager
-   *   The field type plugin manager.
-   * @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selectionManager
-   *   The entity reference selection plugin manager.
    */
   public function __construct(
     protected readonly PrivateTempStore $tempStore,
-    protected readonly FieldTypePluginManagerInterface $fieldTypeManager,
-    protected readonly SelectionPluginManagerInterface $selectionManager,
   ) {}
 
   /**
@@ -40,8 +32,6 @@ public function __construct(
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('tempstore.private')->get('field_ui'),
-      $container->get('plugin.manager.field.field_type'),
-      $container->get('plugin.manager.entity_reference_selection')
     );
   }
 
diff --git a/core/modules/field_ui/src/Controller/FieldStorageAddController.php b/core/modules/field_ui/src/Controller/FieldStorageAddController.php
deleted file mode 100644
index c22ca0d94dd1010bef394bba13fa0abef692204d..0000000000000000000000000000000000000000
--- a/core/modules/field_ui/src/Controller/FieldStorageAddController.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Drupal\field_ui\Controller;
-
-use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\TempStore\PrivateTempStore;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
-
-/**
- * Controller for building the field storage form.
- *
- * @internal
- *
- * @todo remove in https://www.drupal.org/project/drupal/issues/3347291.
- */
-final class FieldStorageAddController extends ControllerBase {
-
-  /**
-   * FieldStorageAddController constructor.
-   *
-   * @param \Drupal\Core\TempStore\PrivateTempStore $tempStore
-   *   The private tempstore.
-   */
-  public function __construct(protected readonly PrivateTempStore $tempStore) {}
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('tempstore.private')->get('field_ui')
-    );
-  }
-
-  /**
-   * Builds the field storage form.
-   *
-   * @param string $entity_type
-   *   The entity type.
-   * @param string $field_name
-   *   The name of the field to create.
-   * @param string $bundle
-   *   The bundle where the field is being created.
-   *
-   * @return array
-   *   The field storage form.
-   */
-  public function storageAddConfigureForm(string $entity_type, string $field_name, string $bundle): array {
-    // @see \Drupal\field_ui\Form\FieldStorageAddForm::submitForm
-    $temp_storage = $this->tempStore->get($entity_type . ':' . $field_name);
-    if (!$temp_storage) {
-      throw new NotFoundHttpException();
-    }
-
-    return $this->entityFormBuilder()->getForm($temp_storage['field_storage'], 'edit', [
-      'entity_type_id' => $entity_type,
-      'bundle' => $bundle,
-    ]);
-  }
-
-}
diff --git a/core/modules/field_ui/src/FieldConfigListBuilder.php b/core/modules/field_ui/src/FieldConfigListBuilder.php
index 24cf2cdd91ac9e17848144800697754f7e39feef..0e4fbfeb489c071bb47a679cca5e8e7b01f96580 100644
--- a/core/modules/field_ui/src/FieldConfigListBuilder.php
+++ b/core/modules/field_ui/src/FieldConfigListBuilder.php
@@ -204,13 +204,6 @@ public function getDefaultOperations(EntityInterface $entity) {
       ];
     }
 
-    $operations['storage-settings'] = [
-      'title' => $this->t('Storage settings'),
-      'weight' => 20,
-      'attributes' => ['title' => $this->t('Edit storage settings.')],
-      'url' => $entity->toUrl("{$entity->getTargetEntityTypeId()}-storage-edit-form"),
-    ];
-
     return $operations;
   }
 
diff --git a/core/modules/field_ui/src/Form/FieldConfigEditForm.php b/core/modules/field_ui/src/Form/FieldConfigEditForm.php
index f18f550c670a209fa62f1b902c1cb5d9549371e3..79b0cc45396288548eaeef0b2cf10fe081e21308 100644
--- a/core/modules/field_ui/src/Form/FieldConfigEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldConfigEditForm.php
@@ -4,13 +4,16 @@
 
 use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
 use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageException;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
 use Drupal\Core\Field\FieldFilteredMarkup;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Render\ElementInfoManagerInterface;
 use Drupal\Core\TempStore\PrivateTempStore;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\Core\TypedData\TypedDataManagerInterface;
@@ -67,12 +70,16 @@ class FieldConfigEditForm extends EntityForm {
    *   The entity display repository.
    * @param \Drupal\Core\TempStore\PrivateTempStore|null $tempStore
    *   The private tempstore.
+   * @param \Drupal\Core\Render\ElementInfoManagerInterface|null $elementInfo
+   *   The element info manager.
    */
   public function __construct(
     EntityTypeBundleInfoInterface $entity_type_bundle_info,
     protected TypedDataManagerInterface $typedDataManager,
     protected ?EntityDisplayRepositoryInterface $entityDisplayRepository = NULL,
-    protected ?PrivateTempStore $tempStore = NULL) {
+    protected ?PrivateTempStore $tempStore = NULL,
+    protected ?ElementInfoManagerInterface $elementInfo = NULL,
+  ) {
     $this->entityTypeBundleInfo = $entity_type_bundle_info;
     if ($this->entityDisplayRepository === NULL) {
       @trigger_error('Calling FieldConfigEditForm::__construct() without the $entityDisplayRepository argument is deprecated in drupal:10.2.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3383771', E_USER_DEPRECATED);
@@ -82,6 +89,10 @@ public function __construct(
       @trigger_error('Calling FieldConfigEditForm::__construct() without the $tempStore argument is deprecated in drupal:10.2.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3383771', E_USER_DEPRECATED);
       $this->tempStore = \Drupal::service('tempstore.private')->get('field_ui');
     }
+    if ($this->elementInfo === NULL) {
+      @trigger_error('Calling FieldConfigEditForm::__construct() without the $elementInfo argument is deprecated in drupal:10.2.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3383771', E_USER_DEPRECATED);
+      $this->elementInfo = \Drupal::service('plugin.manager.element_info');
+    }
   }
 
   /**
@@ -92,15 +103,27 @@ public static function create(ContainerInterface $container) {
       $container->get('entity_type.bundle.info'),
       $container->get('typed_data_manager'),
       $container->get('entity_display.repository'),
-      $container->get('tempstore.private')->get('field_ui')
+      $container->get('tempstore.private')->get('field_ui'),
+      $container->get('plugin.manager.element_info'),
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    // Ensure that the form ID remains consistent between both 'default' and
+    // 'edit' operations. This is needed because historically it was only
+    // possible to edit the field configuration.
+    return 'field_config_edit_form';
+  }
+
   /**
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
     $form = parent::form($form, $form_state);
+    $form['#entity_builders'][] = 'field_form_field_config_edit_form_entity_builder';
 
     $field_storage = $this->entity->getFieldStorageDefinition();
     $bundles = $this->entityTypeBundleInfo->getBundleInfo($this->entity->getTargetEntityTypeId());
@@ -150,10 +173,43 @@ public function form(array $form, FormStateInterface $form_state) {
       'bundle' => $this->entity->getTargetBundle(),
       'entity_id' => NULL,
     ];
+    $form['field_storage'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Field Storage'),
+      '#weight' => -15,
+      '#tree' => TRUE,
+    ];
+    $form['field_storage']['subform'] = [
+      '#parents' => ['field_storage', 'subform'],
+    ];
+    $form['field_storage']['subform']['field_storage_submit'] = [
+      '#type' => 'submit',
+      '#name' => 'field_storage_submit',
+      '#attributes' => [
+        'class' => ['js-hide'],
+      ],
+      '#value' => $this->t('Update settings'),
+      '#process' => ['::processFieldStorageSubmit'],
+      '#limit_validation_errors' => [$form['field_storage']['subform']['#parents']],
+      '#submit' => ['::fieldStorageSubmit'],
+    ];
+    $field_storage_form = $this->entityTypeManager->getFormObject('field_storage_config', $this->operation);
+    $field_storage_form->setEntity($field_storage);
+    $subform_state = SubformState::createForSubform($form['field_storage']['subform'], $form, $form_state, $field_storage_form);
+    $form['field_storage']['subform'] = $field_storage_form->buildForm($form['field_storage']['subform'], $subform_state, $this->entity);
+
     $form['#entity'] = _field_create_entity_from_ids($ids);
-    $items = $this->getTypedData($form['#entity']);
+    $items = $this->getTypedData($this->entity, $form['#entity']);
     $item = $items->first() ?: $items->appendItem();
 
+    $this->addAjaxCallbacks($form['field_storage']['subform']);
+
+    if (isset($form['field_storage']['subform']['cardinality_container'])) {
+      $form['field_storage']['subform']['cardinality_container']['#parents'] = [
+        'field_storage',
+        'subform',
+      ];
+    }
     // Add field settings for the field type and a container for third party
     // settings that modules can add to via hook_form_FORM_ID_alter().
     $form['settings'] = [
@@ -168,7 +224,7 @@ public function form(array $form, FormStateInterface $form_state) {
 
     // Create a new instance of typed data for the field to ensure that default
     // value widget is always rendered from a clean state.
-    $items = $this->getTypedData($form['#entity']);
+    $items = $this->getTypedData($this->entity, $form['#entity']);
 
     // Add handling for default value.
     if ($element = $items->defaultValuesForm($form, $form_state)) {
@@ -201,10 +257,50 @@ public function form(array $form, FormStateInterface $form_state) {
 
       $form['default_value'] = $element;
     }
-
+    $form['#prefix'] = '<div id="field-combined">';
+    $form['#suffix'] = '</div>';
     return $form;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function afterBuild(array $element, FormStateInterface $form_state) {
+    // Delegate ::afterBuild to the subform.
+    // @todo remove after https://www.drupal.org/i/3385205 has been addressed.
+    if (isset($element['field_storage_submit'])) {
+      $field_storage_form = $this->entityTypeManager->getFormObject('field_storage_config', $this->operation);
+      $field_storage_form->setEntity($this->entity->getFieldStorageDefinition());
+      return $field_storage_form->afterBuild($element, SubformState::createForSubform($element, $form_state->getCompleteForm(), $form_state));
+    }
+
+    return parent::afterBuild($element, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
+    parent::copyFormValuesToEntity($entity, $form, $form_state);
+
+    // Update the current field storage instance based on subform state.
+    if (!empty($form['field_storage']['subform'])) {
+      $subform_state = SubformState::createForSubform($form['field_storage']['subform'], $form, $form_state);
+      $field_storage_form = $this->entityTypeManager->getFormObject('field_storage_config', $this->operation);
+      $field_storage_form->setEntity($entity->getFieldStorageDefinition());
+
+      $reflector = new \ReflectionObject($entity);
+
+      // Update the field storage entity based on subform values.
+      $property = $reflector->getProperty('fieldStorage');
+      $property->setValue($entity, $field_storage_form->buildEntity($form['field_storage']['subform'], $subform_state));
+
+      // Remove the item definition to make sure it's not storing stale data.
+      $property = $reflector->getProperty('itemDefinition');
+      $property->setValue($entity, NULL);
+    }
+  }
+
   /**
    * A function to check if element contains any required elements.
    *
@@ -267,16 +363,23 @@ protected function actions(array $form, FormStateInterface $form_state) {
   public function validateForm(array &$form, FormStateInterface $form_state) {
     parent::validateForm($form, $form_state);
 
-    // Before proceeding validation, rebuild the entity to make sure it's
-    // up-to-date. This is needed because element validators may update form
-    // state, and other validators use the entity for validating the field.
-    // @todo remove in https://www.drupal.org/project/drupal/issues/3372934.
-    $this->entity = $this->buildEntity($form, $form_state);
+    $field_storage_form = $this->entityTypeManager->getFormObject('field_storage_config', $this->operation);
+    $field_storage_form->setEntity($this->entity->getFieldStorageDefinition());
+    $subform_state = SubformState::createForSubform($form['field_storage']['subform'], $form, $form_state, $field_storage_form);
+    $field_storage_form->validateForm($form['field_storage']['subform'], $subform_state);
 
+    // Make sure that the default value form is validated using the field
+    // configuration that was just submitted.
+    $field_config = $this->buildEntity($form, $form_state);
     if (isset($form['default_value']) && (!isset($form['set_default_value']) || $form_state->getValue('set_default_value'))) {
-      $items = $this->getTypedData($form['#entity']);
+      $items = $this->getTypedData($field_config, $form['#entity']);
       $items->defaultValuesFormValidate($form['default_value'], $form, $form_state);
     }
+
+    // The form is rendered based on the entity property, meaning that it must
+    // be updated based on the latest form state even though it might be invalid
+    // at this point.
+    $this->entity = $this->buildEntity($form, $form_state);
   }
 
   /**
@@ -285,10 +388,22 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     parent::submitForm($form, $form_state);
 
+    $field_storage_form = $this->entityTypeManager->getFormObject('field_storage_config', $this->operation);
+    $field_storage_form->setEntity($this->entity->getFieldStorageDefinition());
+    $subform_state = SubformState::createForSubform($form['field_storage']['subform'], $form, $form_state, $field_storage_form);
+    $field_storage_form->submitForm($form['field_storage']['subform'], $subform_state);
+    try {
+      $field_storage_form->save($form['field_storage']['subform'], $subform_state);
+    }
+    catch (EntityStorageException $exception) {
+      $this->handleEntityStorageException($form_state, $exception);
+      return;
+    }
+
     // Handle the default value.
     $default_value = [];
     if (isset($form['default_value']) && (!isset($form['set_default_value']) || $form_state->getValue('set_default_value'))) {
-      $items = $this->getTypedData($form['#entity']);
+      $items = $this->getTypedData($this->entity, $form['#entity']);
       $default_value = $items->defaultValuesFormSubmit($form['default_value'], $form, $form_state);
     }
     $this->entity->setDefaultValue($default_value);
@@ -298,44 +413,52 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
-    $temp_storage = $this->tempStore->get($this->entity->getTargetEntityTypeId() . ':' . $this->entity->getName());
-    if ($this->entity->isNew()) {
-      // @todo remove in https://www.drupal.org/project/drupal/issues/3347291.
-      if ($temp_storage && $temp_storage['field_storage']->isNew()) {
-        // Save field storage.
-        try {
-          $temp_storage['field_storage']->save();
-        }
-        catch (EntityStorageException $e) {
-          $this->tempStore->delete($this->entity->getTargetEntityTypeId() . ':' . $this->entity->getName());
-          $form_state->setRedirectUrl(FieldUI::getOverviewRouteInfo($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle()));
-          $this->messenger()->addError($this->t('An error occurred while saving the field: @error', ['@error' => $e->getMessage()]));
-          return;
-        }
-      }
-    }
     // Save field config.
-    $this->entity->save();
-    if (isset($form_state->getStorage()['default_options'])) {
-      $default_options = $form_state->getStorage()['default_options'];
-      // Configure the default display modes.
-      $this->entityTypeId = $temp_storage['field_config_values']['entity_type'];
-      $this->bundle = $temp_storage['field_config_values']['bundle'];
-      $this->configureEntityFormDisplay($temp_storage['field_config_values']['field_name'], $default_options['entity_form_display'] ?? []);
-      $this->configureEntityViewDisplay($temp_storage['field_config_values']['field_name'], $default_options['entity_view_display'] ?? []);
-      // Delete the temp store entry.
-      $this->tempStore->delete($this->entity->getTargetEntityTypeId() . ':' . $this->entity->getName());
-    }
+    try {
+      try {
+        $this->entity->save();
+      }
+      catch (EntityStorageException $exception) {
+        $this->handleEntityStorageException($form_state, $exception);
+        return;
+      }
 
-    $this->messenger()->addStatus($this->t('Saved %label configuration.', ['%label' => $this->entity->getLabel()]));
+      if (isset($form_state->getStorage()['default_options'])) {
+        $default_options = $form_state->getStorage()['default_options'];
+        // Configure the default display modes.
+        $this->entityTypeId = $this->entity->getTargetEntityTypeId();
+        $this->bundle = $this->entity->getTargetBundle();
+        $this->configureEntityFormDisplay($this->entity->getName(), $default_options['entity_form_display'] ?? []);
+        $this->configureEntityViewDisplay($this->entity->getName(), $default_options['entity_view_display'] ?? []);
+      }
+
+      if ($this->entity->isNew()) {
+        // Delete the temp store entry.
+        $this->tempStore->delete($this->entity->getTargetEntityTypeId() . ':' . $this->entity->getName());
+      }
 
-    $request = $this->getRequest();
-    if (($destinations = $request->query->all('destinations')) && $next_destination = FieldUI::getNextDestination($destinations)) {
-      $request->query->remove('destinations');
-      $form_state->setRedirectUrl($next_destination);
+      $this->messenger()
+        ->addStatus($this->t('Saved %label configuration.', ['%label' => $this->entity->getLabel()]));
+
+      $request = $this->getRequest();
+      if (($destinations = $request->query->all('destinations')) && $next_destination = FieldUI::getNextDestination($destinations)) {
+        $request->query->remove('destinations');
+        $form_state->setRedirectUrl($next_destination);
+      }
+      else {
+        $form_state->setRedirectUrl(FieldUI::getOverviewRouteInfo($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle()));
+      }
     }
-    else {
-      $form_state->setRedirectUrl(FieldUI::getOverviewRouteInfo($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle()));
+    catch (\Exception $e) {
+      $this->messenger()->addStatus(
+        $this->t(
+          'Attempt to update field %label failed: %message.',
+          [
+            '%label' => $this->entity->getLabel(),
+            '%message' => $e->getMessage(),
+          ]
+        )
+      );
     }
   }
 
@@ -355,14 +478,85 @@ public function getTitle(FieldConfigInterface $field_config) {
   /**
    * Gets typed data object for the field.
    *
+   * @param \Drupal\field\FieldConfigInterface $field_config
+   *   The field configuration.
    * @param \Drupal\Core\Entity\FieldableEntityInterface $parent
    *   The parent entity that the field is attached to.
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface
    */
-  private function getTypedData(FieldableEntityInterface $parent): TypedDataInterface {
+  private function getTypedData(FieldConfigInterface $field_config, FieldableEntityInterface $parent): TypedDataInterface {
+    // Make sure that typed data manager is re-generating the instance. This
+    // important because we want the returned instance to match the current
+    // state, which could be different from what has been stored in config.
+    $this->typedDataManager->clearCachedDefinitions();
+
     $entity_adapter = EntityAdapter::createFromEntity($parent);
-    return $this->typedDataManager->create($this->entity, $this->entity->getDefaultValue($parent), $this->entity->getName(), $entity_adapter);
+    return $this->typedDataManager->create($field_config, $field_config->getDefaultValue($parent), $field_config->getName(), $entity_adapter);
+  }
+
+  /**
+   * Process handler for subform submit.
+   */
+  public static function processFieldStorageSubmit(array $element, FormStateInterface $form_state, &$complete_form) {
+    // Limit validation errors to the field storage form while the field storage
+    // form is being edited.
+    $complete_form['#limit_validation_errors'] = [array_slice($element['#parents'], 0, -1)];
+    return $element;
+  }
+
+  /**
+   * Submit handler for subform submit.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  public function fieldStorageSubmit(&$form, FormStateInterface $form_state) {
+    // The default value widget needs to be regenerated.
+    $form_storage = &$form_state->getStorage();
+    unset($form_storage['default_value_widget']);
+    $form_state->setRebuild();
+  }
+
+  /**
+   * Add Ajax callback for all inputs.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   */
+  private function addAjaxCallbacks(array &$form): void {
+    if (isset($form['#type']) && !isset($form['#ajax'])) {
+      if ($this->elementInfo->getInfoProperty($form['#type'], '#input') && !$this->elementInfo->getInfoProperty($form['#type'], '#is_button')) {
+        $form['#ajax'] = [
+          'trigger_as' => ['name' => 'field_storage_submit'],
+          'wrapper' => 'field-combined',
+          'event' => 'change',
+        ];
+      }
+    }
+
+    foreach (Element::children($form) as $child_key) {
+      $this->addAjaxCallbacks($form[$child_key]);
+    }
+  }
+
+  /**
+   * Handles entity storage exceptions and redirects the form.
+   *
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state.
+   * @param \Drupal\Core\Entity\EntityStorageException $exception
+   *   The exception.
+   */
+  protected function handleEntityStorageException(FormStateInterface $form_state, EntityStorageException $exception): void {
+    $this->tempStore->delete($this->entity->getTargetEntityTypeId() . ':' . $this->entity->getName());
+    $form_state->setRedirectUrl(FieldUI::getOverviewRouteInfo($this->entity->getTargetEntityTypeId(),
+      $this->entity->getTargetBundle()));
+    $this->messenger()
+      ->addError($this->t('An error occurred while saving the field: @error',
+        ['@error' => $exception->getMessage()]));
   }
 
 }
diff --git a/core/modules/field_ui/src/Form/FieldStorageAddForm.php b/core/modules/field_ui/src/Form/FieldStorageAddForm.php
index ea1f2f94436e4bfb087d1938b6e0b3c7e2f46e16..560e8d7c7268440d3f14419d27a340d1d937f11b 100644
--- a/core/modules/field_ui/src/Form/FieldStorageAddForm.php
+++ b/core/modules/field_ui/src/Form/FieldStorageAddForm.php
@@ -462,10 +462,6 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       'entity_type' => $this->entityTypeId,
       'field_name' => $field_name,
     ] + FieldUI::getRouteBundleParameter($entity_type, $this->bundle);
-    $destinations[] = [
-      'route_name' => "field_ui.field_storage_add_{$this->entityTypeId}",
-      'route_parameters' => $route_parameters,
-    ];
     $destinations[] = [
       'route_name' => "field_ui.field_add_{$this->entityTypeId}",
       'route_parameters' => $route_parameters,
diff --git a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
index f83c7d3e9e75d1a1608a191b0486bed6843449b3..c6192738fa0273c4809dc697b8bb70e25bc61294 100644
--- a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
@@ -6,11 +6,11 @@
 use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\Core\TempStore\PrivateTempStore;
 use Drupal\Core\TypedData\TypedDataManagerInterface;
 use Drupal\field\Entity\FieldConfig;
-use Drupal\field_ui\FieldUI;
+use Drupal\field\FieldConfigInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
@@ -33,27 +33,17 @@ class FieldStorageConfigEditForm extends EntityForm {
    *
    * @param \Drupal\Core\TypedData\TypedDataManagerInterface $typedDataManager
    *   The typed data manager.
-   * @param \Drupal\Core\TempStore\PrivateTempStore|null $tempStore
-   *   The private tempstore.
    */
   public function __construct(
     protected TypedDataManagerInterface $typedDataManager,
-    protected ?PrivateTempStore $tempStore = NULL,
   ) {
-    if ($this->tempStore === NULL) {
-      @trigger_error('Calling FieldStorageConfigEditForm::__construct() without the $tempStore argument is deprecated in drupal:10.2.0 and will be required in drupal:11.0.0. See https://www.drupal.org/node/3383720', E_USER_DEPRECATED);
-      $this->tempStore = \Drupal::service('tempstore.private')->get('field_ui');
-    }
   }
 
   /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('typed_data_manager'),
-      $container->get('tempstore.private')->get('field_ui')
-    );
+    return new static($container->get('typed_data_manager'));
   }
 
   /**
@@ -77,12 +67,15 @@ public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entit
    *   A nested array form elements comprising the form.
    * @param \Drupal\Core\Form\FormStateInterface $form_state
    *   The current state of the form.
-   * @param string $field_config
+   * @param \Drupal\field\FieldConfigInterface|string $field_config
    *   The ID of the field config whose field storage config is being edited.
    */
-  public function buildForm(array $form, FormStateInterface $form_state, $field_config = NULL) {
+  public function buildForm(array $form, FormStateInterface $form_state, FieldConfigInterface|string $field_config = NULL) {
     if ($field_config) {
-      $field = FieldConfig::load($field_config);
+      $field = $field_config;
+      if (is_string($field)) {
+        $field = FieldConfig::load($field_config);
+      }
       $form_state->set('field_config', $field);
 
       $form_state->set('entity_type_id', $field->getTargetEntityTypeId());
@@ -96,11 +89,9 @@ public function buildForm(array $form, FormStateInterface $form_state, $field_co
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
-    $temp_storage = $this->tempStore->get($this->entity->getTargetEntityTypeId() . ':' . $this->entity->getName());
     $form = parent::form($form, $form_state);
 
-    $field_label = $this->entity->isNew() ? $temp_storage['field_config_values']['label'] : $form_state->get('field_config')->label();
-    $form['#title'] = $field_label;
+    $field_label = $form_state->get('field_config')->label();
     $form['#prefix'] = '<p>' . $this->t('These settings apply to the %field field everywhere it is used. Some also impact the way that data is stored and cannot be changed once data has been created.', ['%field' => $field_label]) . '</p>';
 
     // Add settings provided by the field module. The field module is
@@ -122,12 +113,7 @@ public function form(array $form, FormStateInterface $form_state) {
       $items = $entity->get($this->entity->getName());
     }
     else {
-      // Create a temporary field config so that we can access the field
-      // definition.
-      $field_config = $this->entityTypeManager->getStorage('field_config')->create([
-        ...$temp_storage['field_config_values'],
-        'field_storage' => $temp_storage['field_storage'],
-      ]);
+      $field_config = $form_state->get('field_config');
       $items = $this->typedDataManager->create($field_config, name: $this->entity->getName(), parent: EntityAdapter::createFromEntity($entity));
     }
     $item = $items->first() ?: $items->appendItem();
@@ -170,7 +156,7 @@ protected function getCardinalityForm() {
       $form['cardinality'] = ['#markup' => $markup];
     }
     else {
-      $form['#element_validate'][] = '::validateCardinality';
+      $form['#element_validate'][] = [$this, 'validateCardinality'];
       $cardinality = $this->entity->getCardinality();
       $form['cardinality'] = [
         '#type' => 'select',
@@ -191,10 +177,10 @@ protected function getCardinalityForm() {
         '#size' => 2,
         '#states' => [
           'visible' => [
-            ':input[name="cardinality"]' => ['value' => 'number'],
+            ':input[name="field_storage[subform][cardinality]"]' => ['value' => 'number'],
           ],
           'disabled' => [
-            ':input[name="cardinality"]' => ['value' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED],
+            ':input[id="field_storage[subform][cardinality]"]' => ['value' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED],
           ],
         ],
       ];
@@ -207,9 +193,13 @@ protected function getCardinalityForm() {
    * {@inheritdoc}
    */
   protected function actions(array $form, FormStateInterface $form_state) {
+    if ($form_state instanceof SubformStateInterface) {
+      return [];
+    }
     $elements = parent::actions($form, $form_state);
     $elements['submit']['#value'] = $this->entity->isNew() ? $this->t('Continue') : $this->t('Save');
 
+    @trigger_error('Rendering ' . __CLASS__ . ' outside of a subform is deprecated in drupal:10.2.0 and is removed in drupal:11.0.0. See https://www.drupal.org/node/3391538', E_USER_DEPRECATED);
     return $elements;
   }
 
@@ -224,24 +214,33 @@ protected function actions(array $form, FormStateInterface $form_state) {
   public function validateCardinality(array &$element, FormStateInterface $form_state) {
     $field_storage_definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($this->entity->getTargetEntityTypeId());
 
+    $cardinality = $form_state->getValue([
+      ...$element['#parents'],
+      'cardinality',
+    ]);
+    $cardinality_number = $form_state->getValue([
+      ...$element['#parents'],
+      'cardinality_number',
+    ]);
+
     // Validate field cardinality.
-    if ($form_state->getValue('cardinality') === 'number' && !$form_state->getValue('cardinality_number')) {
+    if ($cardinality === 'number' && !$cardinality_number) {
       $form_state->setError($element['cardinality_number'], $this->t('Number of values is required.'));
     }
     // If a specific cardinality is used, validate that there are no entities
     // with a higher delta.
-    elseif (!$this->entity->isNew() && isset($field_storage_definitions[$this->entity->getName()]) && $form_state->getValue('cardinality') != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
+    elseif (!$this->entity->isNew() && isset($field_storage_definitions[$this->entity->getName()]) && $cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
 
       // Get a count of entities that have a value in a delta higher than the
       // one selected. Deltas start with 0, so the selected value does not
       // need to be incremented.
       $entities_with_higher_delta = \Drupal::entityQuery($this->entity->getTargetEntityTypeId())
         ->accessCheck(FALSE)
-        ->condition($this->entity->getName() . '.%delta', $form_state->getValue('cardinality'))
+        ->condition($this->entity->getName() . '.%delta', $cardinality_number)
         ->count()
         ->execute();
       if ($entities_with_higher_delta) {
-        $form_state->setError($element['cardinality_number'], $this->formatPlural($entities_with_higher_delta, 'There is @count entity with @delta or more values in this field, so the allowed number of values cannot be set to @allowed.', 'There are @count entities with @delta or more values in this field, so the allowed number of values cannot be set to @allowed.', ['@delta' => $form_state->getValue('cardinality') + 1, '@allowed' => $form_state->getValue('cardinality')]));
+        $form_state->setError($element['cardinality_number'], $this->formatPlural($entities_with_higher_delta, 'There is @count entity with @delta or more values in this field, so the allowed number of values cannot be set to @allowed.', 'There are @count entities with @delta or more values in this field, so the allowed number of values cannot be set to @allowed.', ['@delta' => $cardinality_number + 1, '@allowed' => $cardinality_number]));
       }
     }
   }
@@ -252,7 +251,7 @@ public function validateCardinality(array &$element, FormStateInterface $form_st
   public function buildEntity(array $form, FormStateInterface $form_state) {
     // Save field cardinality.
     if (!$this->getEnforcedCardinality() && $form_state->getValue('cardinality') === 'number' && $form_state->getValue('cardinality_number')) {
-      $form_state->setValue('cardinality', $form_state->getValue('cardinality_number'));
+      $form_state->setValue('cardinality', (int) $form_state->getValue('cardinality_number'));
     }
 
     return parent::buildEntity($form, $form_state);
@@ -262,31 +261,7 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
-    // Save field storage entity values in tempstore.
-    if ($this->entity->isNew()) {
-      $temp_storage = $this->tempStore->get($this->entity->getTargetEntityTypeId() . ':' . $this->entity->getName());
-      $field_label = $temp_storage['field_config_values']['label'];
-      $temp_storage['field_storage'] = $this->entity;
-      $this->tempStore->set($this->entity->getTargetEntityTypeId() . ':' . $this->entity->getName(), $temp_storage);
-    }
-    try {
-      if (!$this->entity->isNew()) {
-        $field_label = $form_state->get('field_config')->label();
-        $this->entity->save();
-        $this->messenger()->addMessage($this->t('Your settings have been saved.'));
-      }
-      $request = $this->getRequest();
-      if (($destinations = $request->query->all('destinations')) && $next_destination = FieldUI::getNextDestination($destinations)) {
-        $request->query->remove('destinations');
-        $form_state->setRedirectUrl($next_destination);
-      }
-      else {
-        $form_state->setRedirectUrl(FieldUI::getOverviewRouteInfo($form_state->get('entity_type_id'), $form_state->get('bundle')));
-      }
-    }
-    catch (\Exception $e) {
-      $this->messenger()->addStatus($this->t('Attempt to update field %label failed: %message.', ['%label' => $field_label, '%message' => $e->getMessage()]));
-    }
+    $this->entity->save();
   }
 
   /**
diff --git a/core/modules/field_ui/src/Plugin/Derivative/FieldUiLocalTask.php b/core/modules/field_ui/src/Plugin/Derivative/FieldUiLocalTask.php
index deff201664774b5f6d40be4cb8b7b7a3022fe9cc..af4c5674d132058873934eac7c48b6b1db93ded1 100644
--- a/core/modules/field_ui/src/Plugin/Derivative/FieldUiLocalTask.php
+++ b/core/modules/field_ui/src/Plugin/Derivative/FieldUiLocalTask.php
@@ -107,13 +107,6 @@ public function getDerivativeDefinitions($base_plugin_definition) {
           'base_route' => "entity.field_config.{$entity_type_id}_field_edit_form",
         ];
 
-        // Field settings tab.
-        $this->derivatives["field_storage_$entity_type_id"] = [
-          'route_name' => "entity.field_config.{$entity_type_id}_storage_edit_form",
-          'title' => $this->t('Field settings'),
-          'base_route' => "entity.field_config.{$entity_type_id}_field_edit_form",
-        ];
-
         // View and form modes secondary tabs.
         // The same base $path for the menu item (with a placeholder) can be
         // used for all bundles of a given entity type; but depending on
diff --git a/core/modules/field_ui/src/Routing/RouteSubscriber.php b/core/modules/field_ui/src/Routing/RouteSubscriber.php
index affe1ea2c732b0a6d8e7be83f107ce5839d99d8d..52968245e74ca76bc5265065fd4b908295f07c05 100644
--- a/core/modules/field_ui/src/Routing/RouteSubscriber.php
+++ b/core/modules/field_ui/src/Routing/RouteSubscriber.php
@@ -6,7 +6,6 @@
 use Drupal\Core\Routing\RouteSubscriberBase;
 use Drupal\Core\Routing\RoutingEvents;
 use Drupal\field_ui\Controller\FieldConfigAddController;
-use Drupal\field_ui\Controller\FieldStorageAddController;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
 
@@ -73,14 +72,6 @@ protected function alterRoutes(RouteCollection $collection) {
         );
         $collection->add("entity.field_config.{$entity_type_id}_field_edit_form", $route);
 
-        $route = new Route(
-          "$path/fields/{field_config}/storage",
-          ['_entity_form' => 'field_storage_config.edit'] + $defaults,
-          ['_permission' => 'administer ' . $entity_type_id . ' fields'],
-          $options
-        );
-        $collection->add("entity.field_config.{$entity_type_id}_storage_edit_form", $route);
-
         $route = new Route(
           "$path/fields/{field_config}/delete",
           ['_entity_form' => 'field_config.delete'] + $defaults,
@@ -122,18 +113,6 @@ protected function alterRoutes(RouteCollection $collection) {
         );
         $collection->add("field_ui.field_add_$entity_type_id", $route);
 
-        // @todo remove in https://www.drupal.org/project/drupal/issues/3347291.
-        $route = new Route(
-          "$path/add-storage/{entity_type}/{field_name}",
-          [
-            '_controller' => FieldStorageAddController::class . '::storageAddConfigureForm',
-            '_title' => 'Add storage',
-          ] + $defaults,
-          ['_permission' => 'administer ' . $entity_type_id . ' fields'],
-          $options
-        );
-        $collection->add("field_ui.field_storage_add_$entity_type_id", $route);
-
         $route = new Route(
           "$path/fields/reuse",
           [
diff --git a/core/modules/field_ui/tests/modules/field_ui_test_deprecated/field_ui_test_deprecated.info.yml b/core/modules/field_ui/tests/modules/field_ui_test_deprecated/field_ui_test_deprecated.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..09e77b8e5733cb9294662a9cb4823c6d0331f78c
--- /dev/null
+++ b/core/modules/field_ui/tests/modules/field_ui_test_deprecated/field_ui_test_deprecated.info.yml
@@ -0,0 +1,5 @@
+name: 'Field UI test deprecated'
+type: module
+description: 'Support module for testing deprecated Field UI functionality.'
+package: Testing
+version: VERSION
diff --git a/core/modules/field_ui/tests/modules/field_ui_test_deprecated/field_ui_test_deprecated.module b/core/modules/field_ui/tests/modules/field_ui_test_deprecated/field_ui_test_deprecated.module
new file mode 100644
index 0000000000000000000000000000000000000000..81f4a774b9bb3b107aff28e7ecb6b09d5c4e3b51
--- /dev/null
+++ b/core/modules/field_ui/tests/modules/field_ui_test_deprecated/field_ui_test_deprecated.module
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Field UI test module.
+ */
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\field\FieldStorageConfigInterface;
+use Drupal\field_ui\Form\FieldStorageConfigEditForm;
+
+/**
+ * Implements hook_form_FORM_ID_alter() for field_storage_config_edit_form.
+ */
+function field_ui_test_deprecated_form_field_storage_config_edit_form_alter(&$form, FormStateInterface $form_state) {
+  if (!($form_state->getFormObject() instanceof FieldStorageConfigEditForm)) {
+    throw new \LogicException('field_storage_config_edit_form() expects to get access to the field storage config entity edit form.');
+  }
+  if (!($form_state->getFormObject()->getEntity() instanceof FieldStorageConfigInterface)) {
+    throw new \LogicException('field_storage_config_edit_form() expects to get access to the field storage config entity.');
+  }
+  if (!isset($form['cardinality_container']['cardinality'])) {
+    throw new \LogicException('field_storage_config_edit_form() expects to that the cardinality container with the cardinality form element exists.');
+  }
+
+  $form['cardinality_container']['hello'] = [
+    '#markup' => 'Greetings from the field_storage_config_edit_form() alter.',
+  ];
+}
diff --git a/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php b/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php
index dec8e17d64ad914e38f34a4a03ec756170eec711..349a6e5d118513ef16c5c877e59d7fe8be84a86c 100644
--- a/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php
+++ b/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php
@@ -257,10 +257,10 @@ public function testExternalDestinations() {
     $options = [
       'query' => ['destinations' => ['http://example.com']],
     ];
-    $this->drupalGet('admin/structure/types/manage/article/fields/node.article.body/storage', $options);
-    $this->submitForm([], 'Save');
+    $this->drupalGet('admin/structure/types/manage/article/fields/node.article.body', $options);
+    $this->submitForm([], 'Save settings');
     // The external redirect should not fire.
-    $this->assertSession()->addressEquals('admin/structure/types/manage/article/fields/node.article.body/storage?destinations%5B0%5D=http%3A//example.com');
+    $this->assertSession()->addressEquals('admin/structure/types/manage/article/fields/node.article.body?destinations%5B0%5D=http%3A//example.com');
     $this->assertSession()->statusCodeEquals(200);
     $this->assertSession()->responseContains('Attempt to update field <em class="placeholder">Body</em> failed: <em class="placeholder">The internal path component &#039;http://example.com&#039; is external. You are not allowed to specify an external URL together with internal:/.</em>.');
   }
@@ -369,9 +369,33 @@ public function testNonExistentFieldUrls() {
 
     $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id);
     $this->assertSession()->statusCodeEquals(404);
+  }
 
-    $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id . '/storage');
-    $this->assertSession()->statusCodeEquals(404);
+  /**
+   * Tests that the 'field_prefix' setting works on Field UI.
+   */
+  public function testFieldPrefix() {
+    // Change default field prefix.
+    $field_prefix = $this->randomMachineName(10);
+    $this->config('field_ui.settings')->set('field_prefix', $field_prefix)->save();
+
+    // Create a field input and label exceeding the new maxlength, which is 22.
+    $field_exceed_max_length_label = $this->randomString(23);
+    $field_exceed_max_length_input = $this->randomMachineName(23);
+
+    // Try to create the field.
+    $edit = [
+      'label' => $field_exceed_max_length_label,
+      'field_name' => $field_exceed_max_length_input,
+    ];
+    $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/add-field');
+    $this->submitForm($edit, 'Continue');
+    $this->assertSession()->pageTextContains('Machine-readable name cannot be longer than 22 characters but is currently 23 characters long.');
+
+    // Create a valid field.
+    $this->fieldUIAddNewField('admin/structure/types/manage/' . $this->contentType, $this->fieldNameInput, $this->fieldLabel);
+    $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/node.' . $this->contentType . '.' . $field_prefix . $this->fieldNameInput);
+    $this->assertSession()->pageTextContains($this->fieldLabel . ' settings for ' . $this->contentType);
   }
 
   /**
diff --git a/core/modules/field_ui/tests/src/Functional/ManageFieldsLifecycleTest.php b/core/modules/field_ui/tests/src/Functional/ManageFieldsLifecycleTest.php
index f3d8ce5ea7e702b59daba593afded18c9850d6da..d18854a18b382ac98fdb462fc559117914bea16a 100644
--- a/core/modules/field_ui/tests/src/Functional/ManageFieldsLifecycleTest.php
+++ b/core/modules/field_ui/tests/src/Functional/ManageFieldsLifecycleTest.php
@@ -51,7 +51,7 @@ protected function manageFieldsPage($type = '') {
     $this->assertSession()->linkExists('Create a new field');
 
     // Assert entity operations for all fields.
-    $number_of_links = 3;
+    $number_of_links = 2;
     $number_of_links_found = 0;
     $operation_links = $this->xpath('//ul[@class = "dropbutton"]/li/a');
     $url = base_path() . "admin/structure/types/manage/$type/fields/node.$type.body";
@@ -63,11 +63,6 @@ protected function manageFieldsPage($type = '') {
           $number_of_links_found++;
           break;
 
-        case 'Edit storage settings.':
-          $this->assertSame("$url/storage", $link->getAttribute('href'));
-          $number_of_links_found++;
-          break;
-
         case 'Delete field.':
           $this->assertSame("$url/delete", $link->getAttribute('href'));
           $number_of_links_found++;
@@ -95,20 +90,14 @@ protected function createField() {
   protected function updateField() {
     $field_id = 'node.' . $this->contentType . '.' . $this->fieldName;
     // Go to the field edit page.
-    $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id . '/storage');
+    $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id);
     $this->assertSession()->assertEscaped($this->fieldLabel);
 
     // Populate the field settings with new settings.
     $string = 'updated dummy test string';
-    $edit = [
-      'settings[test_field_storage_setting]' => $string,
-    ];
-    $this->submitForm($edit, 'Save');
-
-    // Go to the field edit page.
-    $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id);
     $edit = [
       'settings[test_field_setting]' => $string,
+      'field_storage[subform][settings][test_field_storage_setting]' => $string,
     ];
     $this->assertSession()->pageTextContains('Default value');
     $this->submitForm($edit, 'Save settings');
@@ -145,34 +134,28 @@ protected function addExistingField() {
    * numeric value. That is tested already in FormTest::testNumber().
    */
   protected function cardinalitySettings() {
-    $field_edit_path = 'admin/structure/types/manage/article/fields/node.article.body/storage';
+    $field_edit_path = 'admin/structure/types/manage/article/fields/node.article.body';
 
     // Assert the cardinality other field cannot be empty when cardinality is
     // set to 'number'.
     $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => '',
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => '',
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
     $this->assertSession()->pageTextContains('Number of values is required.');
 
     // Submit a custom number.
     $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => 6,
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => 6,
     ];
+    $this->submitForm($edit, 'Update settings');
+    $this->submitForm([], 'Save settings');
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
-    $this->drupalGet($field_edit_path);
-    $this->assertSession()->fieldValueEquals('cardinality', 'number');
-    $this->assertSession()->fieldValueEquals('cardinality_number', 6);
-
-    // Check that tabs displayed.
-    $this->assertSession()->linkExists('Edit');
-    $this->assertSession()->linkByHrefExists('admin/structure/types/manage/article/fields/node.article.body');
-    $this->assertSession()->linkExists('Field settings');
-    $this->assertSession()->linkByHrefExists($field_edit_path);
+    $this->assertSession()->fieldValueEquals('field_storage[subform][cardinality]', 'number');
+    $this->assertSession()->fieldValueEquals('field_storage[subform][cardinality_number]', 6);
 
     // Add two entries in the body.
     $edit = ['title[0][value]' => 'Cardinality', 'body[0][value]' => 'Body 1', 'body[1][value]' => 'Body 2'];
@@ -182,11 +165,11 @@ protected function cardinalitySettings() {
     // Assert that you can't set the cardinality to a lower number than the
     // highest delta of this field.
     $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => 1,
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => 1,
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
     $this->assertSession()->pageTextContains("There is 1 entity with 2 or more values in this field");
 
     // Create a second entity with three values.
@@ -196,47 +179,49 @@ protected function cardinalitySettings() {
 
     // Set to unlimited.
     $edit = [
-      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+      'field_storage[subform][cardinality]' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
+    $this->submitForm([], 'Save settings');
     $this->drupalGet($field_edit_path);
-    $this->assertSession()->fieldValueEquals('cardinality', FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
-    $this->assertSession()->fieldValueEquals('cardinality_number', 1);
+    $this->assertSession()->fieldValueEquals('field_storage[subform][cardinality]', FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
+    $this->assertSession()->fieldValueEquals('field_storage[subform][cardinality_number]', 1);
 
     // Assert that you can't set the cardinality to a lower number then the
     // highest delta of this field but can set it to the same.
     $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => 1,
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => 1,
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
+    $this->submitForm([], 'Save settings');
     $this->assertSession()->pageTextContains("There are 2 entities with 2 or more values in this field");
 
     $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => 2,
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => 2,
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
     $this->assertSession()->pageTextContains("There is 1 entity with 3 or more values in this field");
 
     $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => 3,
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => 3,
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
 
     // Test the cardinality validation is not access sensitive.
 
     // Remove the cardinality limit 4 so we can add a node the user doesn't have access to.
     $edit = [
-      'cardinality' => (string) FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+      'field_storage[subform][cardinality]' => (string) FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
     $node = $this->drupalCreateNode([
       'private' => TRUE,
       'uid' => 0,
@@ -253,25 +238,26 @@ protected function cardinalitySettings() {
     // set it to the same.
     $this->drupalGet($field_edit_path);
     $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => 2,
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => 2,
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
     $this->assertSession()->pageTextContains("There are 2 entities with 3 or more values in this field");
     $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => 3,
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => 3,
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
     $this->assertSession()->pageTextContains("There is 1 entity with 4 or more values in this field");
     $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => 4,
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => 4,
     ];
     $this->drupalGet($field_edit_path);
-    $this->submitForm($edit, 'Save');
+    $this->submitForm($edit, 'Update settings');
+    $this->submitForm([], 'Save settings');
   }
 
   /**
diff --git a/core/modules/field_ui/tests/src/Functional/ManageFieldsTest.php b/core/modules/field_ui/tests/src/Functional/ManageFieldsTest.php
index 9a5c6de5487dc224c5247b7f6ac986a34f413264..dafe0df15daebc10d90a19c7749a797f0fc417b9 100644
--- a/core/modules/field_ui/tests/src/Functional/ManageFieldsTest.php
+++ b/core/modules/field_ui/tests/src/Functional/ManageFieldsTest.php
@@ -151,14 +151,12 @@ public function testAddField() {
     $this->assertSession()->statusMessageNotContains('Saved');
 
     // Change the storage form values.
-    $edit = ['cardinality_number' => 5];
-    $this->submitForm($edit, 'Continue');
+    $edit = ['field_storage[subform][cardinality_number]' => 5];
+    $this->submitForm($edit, 'Update settings');
     $this->assertSession()->statusMessageNotContains('Saved');
 
-    // Go back to the field storage form.
-    $this->drupalGet('/admin/structure/types/manage/' . $type->id() . '/add-storage/node/field_test_field');
     // Assert that the form values persist.
-    $this->assertEquals(5, $page->findField('cardinality_number')->getValue());
+    $this->assertEquals(5, $page->findField('field_storage[subform][cardinality_number]')->getValue());
 
     // Try creating a field with the same machine name.
     $this->drupalGet('/admin/structure/types/manage/' . $type->id() . '/fields/add-field');
@@ -169,10 +167,10 @@ public function testAddField() {
     ];
     $this->submitForm($edit, 'Continue');
     // Assert that the values in the field storage form are reset.
-    $this->assertEquals(1, $page->findField('cardinality_number')->getValue());
+    $this->assertEquals(1, $page->findField('field_storage[subform][cardinality_number]')->getValue());
 
     // Assert that the field is created with the new settings.
-    $this->submitForm([], 'Continue');
+    $this->submitForm([], 'Update settings');
     $this->assertSession()->statusMessageNotContains('Saved');
     $this->submitForm([], 'Save settings');
     $this->assertSession()->statusMessageContains('Saved');
@@ -202,9 +200,9 @@ public function testAddFieldWithMultipleUsers() {
     ];
     $this->submitForm($edit, 'Continue');
     // Make changes to the storage form.
-    $edit = ['cardinality_number' => 5];
+    $edit = ['field_storage[subform][cardinality_number]' => 5];
     $storage_form_url = $this->getUrl();
-    $this->submitForm($edit, 'Continue');
+    $this->submitForm($edit, 'Update settings');
     $this->drupalLogout();
 
     // Actually add a field as user 2.
@@ -216,11 +214,11 @@ public function testAddFieldWithMultipleUsers() {
       'new_storage_type' => 'test_field',
     ];
     $this->submitForm($edit, 'Continue');
-    $allowed_no_of_values = $page->findField('cardinality_number')->getValue();
+    $allowed_no_of_values = $page->findField('field_storage[subform][cardinality_number]')->getValue();
     // Assert that the changes made by any user do not affect other users until
     // the field is saved.
     $this->assertEquals(1, $allowed_no_of_values);
-    $this->submitForm(['cardinality_number' => 2], 'Continue');
+    $this->submitForm(['field_storage[subform][cardinality_number]' => 2], 'Update settings');
     $this->submitForm([], 'Save settings');
     $this->assertSession()->pageTextContains("Saved Test field configuration.");
     $this->drupalLogout();
@@ -228,7 +226,6 @@ public function testAddFieldWithMultipleUsers() {
     // Continue adding a field as user 1, using the URL saved previously.
     $this->drupalLogin($user1);
     $this->drupalGet($storage_form_url);
-    $this->submitForm([], 'Continue');
     // Assert that the user can go on with configuring a field with a machine
     // that is already taken.
     $this->assertSession()->pageTextNotContains('error');
@@ -275,10 +272,6 @@ public function testEditFieldWithLeftOverFieldInTempStore() {
       ])
       ->save();
 
-    $this->drupalGet("$bundle_path/fields/node.{$node_type->id()}.test_field/storage");
-    $this->submitForm([], 'Save');
-    $this->assertSession()->statusMessageContains('Your settings have been saved.', 'status');
-
     $this->drupalGet("$bundle_path/fields/node.{$node_type->id()}.test_field");
     $this->submitForm([], 'Save settings');
     $this->assertSession()->statusMessageContains('Saved test_field configuration.', 'status');
@@ -304,4 +297,20 @@ public function testEntityReferenceToNonBundleableEntity() {
     $this->assertEquals([['target_id' => $this->adminUser->id()]], $field->getDefaultValue(User::create(['name' => '1337'])));
   }
 
+  /**
+   * Tests hook_form_field_storage_config_form_edit_alter().
+   *
+   * @group legacy
+   */
+  public function testFieldStorageFormAlter() {
+    $this->container->get('module_installer')->install(['field_ui_test_deprecated']);
+    $this->rebuildContainer();
+
+    $node_type = $this->drupalCreateContentType();
+    $bundle = $node_type->id();
+    $this->expectDeprecation('The deprecated alter hook hook_form_field_storage_config_edit_form_alter() is implemented in these functions: field_ui_test_deprecated_form_field_storage_config_edit_form_alter. Use hook_form_field_config_edit_form_alter() instead. See https://www.drupal.org/node/3386675.');
+    $this->drupalGet("/admin/structure/types/manage/$bundle/fields/node.$bundle.body");
+    $this->assertSession()->elementTextContains('css', '#edit-field-storage', 'Greetings from the field_storage_config_edit_form() alter.');
+  }
+
 }
diff --git a/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php b/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php
index 3db32a07236dae766271f13a03c91bcde86c6c32..4be9ffcf2faf1e04050ff3e1bfbe48af4be33828 100644
--- a/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php
+++ b/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php
@@ -212,9 +212,49 @@ public function testAddField() {
     $text_plain->click();
     $this->assertTrue($assert_session->elementExists('css', '[name="group_field_options_wrapper"][value="string"]')->isSelected());
     $page->pressButton('Continue');
-    $this->assertMatchesRegularExpression('/.*article\/add-storage\/node\/field_test_field_1.*/', $this->getUrl());
-    $page->pressButton('Continue');
+
     $this->assertMatchesRegularExpression('/.*article\/add-field\/node\/field_test_field_1.*/', $this->getUrl());
+
+    // Ensure the default value is reloaded when the field storage settings
+    // are changed.
+    $default_input_1_name = 'default_value_input[field_test_field_1][0][value]';
+    $default_input_1 = $assert_session->fieldExists($default_input_1_name);
+    $this->assertFalse($default_input_1->isVisible());
+
+    $default_value = $assert_session->fieldExists('set_default_value');
+    $default_value->check();
+    $assert_session->waitForElementVisible('xpath', $default_value->getXpath());
+    $default_input_1->setValue('There can be only one!');
+    $default_input_2_name = 'default_value_input[field_test_field_1][1][value]';
+    $assert_session->fieldNotExists($default_input_2_name);
+    $cardinality = $assert_session->fieldExists('field_storage[subform][cardinality_number]');
+    $cardinality->setValue(2);
+    $default_input_2 = $assert_session->waitForField($default_input_2_name);
+    // Ensure the default value for first input is retained.
+    $assert_session->fieldValueEquals($default_input_1_name, 'There can be only one!');
+    $page->findField($default_input_2_name)->setValue('But maybe also two?');
+    $cardinality->setValue('1');
+    $assert_session->assertWaitOnAjaxRequest();
+    $assert_session->waitForElementRemoved('xpath', $default_input_2->getXpath());
+    // Ensure the first input retains its value.
+    $assert_session->fieldValueEquals($default_input_1_name, 'There can be only one!');
+    $cardinality->setValue(2);
+    $assert_session->waitForField($default_input_2_name);
+    // Ensure when the second input is added again it does not retain its value.
+    $assert_session->fieldValueEquals($default_input_2_name, '');
+
+    // Ensure changing the max length input will also reload the form.
+    $max_length_input = $assert_session->fieldExists('field_storage[subform][settings][max_length]');
+    $this->assertSame('255', $max_length_input->getValue());
+    $this->assertSame('255', $default_input_1->getAttribute('maxlength'));
+    $max_length_input->setValue('5');
+    $page->waitFor(5, function () use ($default_input_1) {
+      return $default_input_1->getAttribute('maxlength') === '5';
+    });
+    $this->assertSame('5', $default_input_1->getAttribute('maxlength'));
+    // Set a default value that is under the new limit.
+    $default_input_1->setValue('Five!');
+
     $page->pressButton('Save settings');
     $assert_session->pageTextContains('Saved ' . $field_name . ' configuration.');
     $this->assertNotNull($field_storage = FieldStorageConfig::loadByName('node', "field_$field_name"));
@@ -240,8 +280,6 @@ public function testAddField() {
     $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="test_field"]')->isSelected());
     $assert_session->pageTextNotContains('Choose an option below');
 
-    $page->pressButton('Continue');
-    $this->assertMatchesRegularExpression('/.*article\/add-storage\/node\/field_test_field_2.*/', $this->getUrl());
     $page->pressButton('Continue');
     $this->assertMatchesRegularExpression('/.*article\/add-field\/node\/field_test_field_2.*/', $this->getUrl());
     $page->pressButton('Save settings');
diff --git a/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php b/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php
index 1a4625095d6affdc2b8a9cb3cf4877bfe082474f..2630b36815b76c3876f328781542852b4a38f47e 100644
--- a/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php
+++ b/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php
@@ -64,10 +64,7 @@ public function fieldUIAddNewFieldJS(?string $bundle_path, string $field_name, ?
     $page->findButton('Continue')->click();
     $assert_session->waitForText("These settings apply to the $label field everywhere it is used.");
     if ($save_settings) {
-      // Second step: 'Storage settings' form.
-      $page->findButton('Continue')->click();
-
-      // Third step: 'Field settings' form.
+      // Second step: Save field settings.
       $page->findButton('Save settings')->click();
       $assert_session->pageTextContains("Saved $label configuration.");
 
diff --git a/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php b/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php
index 9a01349a30770de684ca8c83516e86652ca735dc..c73388645f7ff9ce79267d48e13e16892f9466a9 100644
--- a/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php
+++ b/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php
@@ -85,9 +85,7 @@ public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $fi
       $this->getSession()->getPage()->findLink($label);
 
       // Second step: 'Storage settings' form.
-      $this->submitForm($storage_edit, 'Continue');
-      // Assert that the field is not created.
-      $this->assertFieldDoesNotExist($bundle_path, $label);
+      $this->submitForm($storage_edit, 'Update settings');
 
       // Third step: 'Field settings' form.
       $this->submitForm($field_edit, 'Save settings');
diff --git a/core/modules/field_ui/tests/src/Unit/FieldConfigEditFormTest.php b/core/modules/field_ui/tests/src/Unit/FieldConfigEditFormTest.php
index 650baff4c69395365865d9f1f81241179a18c086..d680842acc1d7953edc56e6f388cb6e34b7aca79 100644
--- a/core/modules/field_ui/tests/src/Unit/FieldConfigEditFormTest.php
+++ b/core/modules/field_ui/tests/src/Unit/FieldConfigEditFormTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\field_ui\Unit;
 
 use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
+use Drupal\Core\Render\ElementInfoManagerInterface;
 use Drupal\Core\TempStore\PrivateTempStore;
 use Drupal\field_ui\Form\FieldConfigEditForm;
 use Drupal\Tests\UnitTestCase;
@@ -30,8 +31,9 @@ protected function setUp(): void {
     $entity_type_bundle_info = $this->createMock('\Drupal\Core\Entity\EntityTypeBundleInfoInterface');
     $typed_data = $this->createMock('\Drupal\Core\TypedData\TypedDataManagerInterface');
     $temp_store = $this->createMock(PrivateTempStore::class);
+    $element_info_manager = $this->createMock(ElementInfoManagerInterface::class);
     $entity_display_repository = $this->createMock(EntityDisplayRepositoryInterface::class);
-    $this->fieldConfigEditForm = new FieldConfigEditForm($entity_type_bundle_info, $typed_data, $entity_display_repository, $temp_store);
+    $this->fieldConfigEditForm = new FieldConfigEditForm($entity_type_bundle_info, $typed_data, $entity_display_repository, $temp_store, $element_info_manager);
   }
 
   /**
diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
index b5947c9e5b7098163e6e0bade049e7706bf82835..d61822ceb04d1520ad7e6c85344f17a8cce5746f 100644
--- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
@@ -139,7 +139,7 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state
       '#description' => $this->t('This setting only has an effect if the display option is enabled.'),
       '#states' => [
         'visible' => [
-          ':input[name="settings[display_field]"]' => ['checked' => TRUE],
+          ':input[name="field_storage[subform][settings][display_field]"]' => ['checked' => TRUE],
         ],
       ],
     ];
diff --git a/core/modules/file/tests/src/Functional/FileFieldWidgetTest.php b/core/modules/file/tests/src/Functional/FileFieldWidgetTest.php
index 42511f153a3113a42b6247640a24d30e03052bdf..212c5fb6d239d62af15d520d0935c2d0f7ad8bfa 100644
--- a/core/modules/file/tests/src/Functional/FileFieldWidgetTest.php
+++ b/core/modules/file/tests/src/Functional/FileFieldWidgetTest.php
@@ -259,8 +259,8 @@ public function testPrivateFileSetting() {
     $test_file = $this->getTestFile('text');
 
     // Change the field setting to make its files private, and upload a file.
-    $edit = ['settings[uri_scheme]' => 'private'];
-    $this->drupalGet("admin/structure/types/manage/{$type_name}/fields/{$field_id}/storage");
+    $edit = ['field_storage[subform][settings][uri_scheme]' => 'private'];
+    $this->drupalGet("admin/structure/types/manage/{$type_name}/fields/{$field_id}");
     $this->submitForm($edit, 'Save');
     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
     $node = $node_storage->loadUnchanged($nid);
@@ -273,13 +273,13 @@ public function testPrivateFileSetting() {
 
     // Ensure we can't change 'uri_scheme' field settings while there are some
     // entities with uploaded files.
-    $this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_id/storage");
-    $this->assertSession()->fieldDisabled("edit-settings-uri-scheme-public");
+    $this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_id");
+    $this->assertSession()->fieldDisabled("edit-field-storage-subform-settings-uri-scheme-public");
 
     // Delete node and confirm that setting could be changed.
     $node->delete();
-    $this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_id/storage");
-    $this->assertSession()->fieldEnabled("edit-settings-uri-scheme-public");
+    $this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_id");
+    $this->assertSession()->fieldEnabled("edit-field-storage-subform-settings-uri-scheme-public");
   }
 
   /**
@@ -302,7 +302,7 @@ public function testPrivateFileComment() {
 
     $name = $this->randomMachineName();
     $label = $this->randomMachineName();
-    $storage_edit = ['settings[uri_scheme]' => 'private'];
+    $storage_edit = ['field_storage[subform][settings][uri_scheme]' => 'private'];
     $this->fieldUIAddNewField('admin/structure/comment/manage/comment', $name, $label, 'file', $storage_edit);
 
     // Manually clear cache on the tester side.
diff --git a/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php b/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php
index 9ac7dd18f3e2ec766c69b2a0dad4efaa76f7c0db..b3b1dd9d248b6e37ece66ef9bbe9c883bbd3619a 100644
--- a/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php
+++ b/core/modules/image/tests/src/Functional/ImageFieldDefaultImagesTest.php
@@ -126,17 +126,17 @@ public function testDefaultImages() {
       ->save();
 
     // Confirm the defaults are present on the article field storage settings
-    // form.
+    // sub-form.
     $field_id = $field->id();
-    $this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
-    $this->assertSession()->hiddenFieldValueEquals('settings[default_image][uuid][fids]', $default_images['field_storage']->id());
+    $this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
+    $this->assertSession()->hiddenFieldValueEquals('field_storage[subform][settings][default_image][uuid][fids]', $default_images['field_storage']->id());
     // Confirm the defaults are present on the article field edit form.
     $this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
     $this->assertSession()->hiddenFieldValueEquals('settings[default_image][uuid][fids]', $default_images['field']->id());
 
     // Confirm the defaults are present on the page field storage settings form.
-    $this->drupalGet("admin/structure/types/manage/page/fields/$field_id/storage");
-    $this->assertSession()->hiddenFieldValueEquals('settings[default_image][uuid][fids]', $default_images['field_storage']->id());
+    $this->drupalGet("admin/structure/types/manage/page/fields/$field_id");
+    $this->assertSession()->hiddenFieldValueEquals('field_storage[subform][settings][default_image][uuid][fids]', $default_images['field_storage']->id());
     // Confirm the defaults are present on the page field edit form.
     $field2_id = $field2->id();
     $this->drupalGet("admin/structure/types/manage/page/fields/$field2_id");
@@ -167,8 +167,8 @@ public function testDefaultImages() {
 
     // Confirm that the new default is used on the article field storage
     // settings form.
-    $this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
-    $this->assertSession()->hiddenFieldValueEquals('settings[default_image][uuid][fids]', $default_images['field_storage_new']->id());
+    $this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
+    $this->assertSession()->hiddenFieldValueEquals('field_storage[subform][settings][default_image][uuid][fids]', $default_images['field_storage_new']->id());
 
     // Reload the nodes and confirm the field defaults are used.
     $node_storage->resetCache([$article->id(), $page->id()]);
@@ -240,9 +240,9 @@ public function testDefaultImages() {
     $field_storage->save();
 
     // Confirm that the new default is used on the article field storage
-    // settings form.
-    $this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
-    $this->assertSession()->hiddenFieldValueEquals('settings[default_image][uuid][fids]', $default_images['field_storage_private']->id());
+    // settings sub-form.
+    $this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
+    $this->assertSession()->hiddenFieldValueEquals('field_storage[subform][settings][default_image][uuid][fids]', $default_images['field_storage_private']->id());
 
     // Upload a new default for the article's field after setting the field
     // storage upload destination to 'private'.
diff --git a/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php b/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php
index bb270feda8f12ecdcb0f788c4312479e27591441..3ce49168a630f3b46da2f5fdc945cda9c4880ade 100644
--- a/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php
+++ b/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php
@@ -334,9 +334,9 @@ public function testImageFieldSettings() {
     // 1, so we need to make sure the file widget prevents these notices by
     // providing all settings, even if they are not used.
     // @see FileWidget::formMultipleElements().
-    $this->drupalGet('admin/structure/types/manage/article/fields/node.article.' . $field_name . '/storage');
+    $this->drupalGet('admin/structure/types/manage/article/fields/node.article.' . $field_name);
     $this->submitForm([
-      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+      'field_storage[subform][cardinality]' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
     ], 'Save');
     $edit = [
       'files[' . $field_name . '_1][]' => \Drupal::service('file_system')->realpath($test_image->uri),
@@ -499,11 +499,11 @@ public function testImageFieldDefaultImage() {
     $title = $this->randomString(1024);
     $edit = [
       // Get the path of the 'image-test.png' file.
-      'files[settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($images[0]->uri),
-      'settings[default_image][alt]' => $alt,
-      'settings[default_image][title]' => $title,
+      'files[field_storage_subform_settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($images[0]->uri),
+      'field_storage[subform][settings][default_image][alt]' => $alt,
+      'field_storage[subform][settings][default_image][title]' => $title,
     ];
-    $this->drupalGet("admin/structure/types/manage/article/fields/node.article.{$field_name}/storage");
+    $this->drupalGet("admin/structure/types/manage/article/fields/node.article.{$field_name}");
     $this->submitForm($edit, 'Save');
     // Clear field definition cache so the new default image is detected.
     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
@@ -558,8 +558,8 @@ public function testImageFieldDefaultImage() {
 
     // Remove default image from the field and make sure it is no longer used.
     // Can't use fillField cause Mink can't fill hidden fields.
-    $this->drupalGet("admin/structure/types/manage/article/fields/node.article.$field_name/storage");
-    $this->getSession()->getPage()->find('css', 'input[name="settings[default_image][uuid][fids]"]')->setValue(0);
+    $this->drupalGet("admin/structure/types/manage/article/fields/node.article.$field_name");
+    $this->getSession()->getPage()->find('css', 'input[name="field_storage[subform][settings][default_image][uuid][fids]"]')->setValue(0);
     $this->getSession()->getPage()->pressButton('Save');
 
     // Clear field definition cache so the new default image is detected.
@@ -574,11 +574,11 @@ public function testImageFieldDefaultImage() {
     // Add a default image to the new field.
     $edit = [
       // Get the path of the 'image-test.gif' file.
-      'files[settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($images[2]->uri),
-      'settings[default_image][alt]' => $alt,
-      'settings[default_image][title]' => $title,
+      'files[field_storage_subform_settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($images[2]->uri),
+      'field_storage[subform][settings][default_image][alt]' => $alt,
+      'field_storage[subform][settings][default_image][title]' => $title,
     ];
-    $this->drupalGet('admin/structure/types/manage/article/fields/node.article.' . $private_field_name . '/storage');
+    $this->drupalGet('admin/structure/types/manage/article/fields/node.article.' . $private_field_name);
     $this->submitForm($edit, 'Save');
     // Clear field definition cache so the new default image is detected.
     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
diff --git a/core/modules/link/tests/src/Functional/LinkFieldUITest.php b/core/modules/link/tests/src/Functional/LinkFieldUITest.php
index 2c40adac32ca8544105dea4b0bbf3fbf25516026..b435e2d3e9d844e3d657522c9133a241f0120dc5 100644
--- a/core/modules/link/tests/src/Functional/LinkFieldUITest.php
+++ b/core/modules/link/tests/src/Functional/LinkFieldUITest.php
@@ -165,7 +165,7 @@ public function runFieldUIItem($cardinality, $link_type, $title, $label, $field_
       $field_edit['default_value_input[field_' . $field_name . '][0][title]'] = 'Default title';
     }
     $storage_edit = [
-      'cardinality_number' => $cardinality,
+      'field_storage[subform][cardinality_number]' => $cardinality,
     ];
     $this->fieldUIAddNewField($type_path, $field_name, $label, 'link', $storage_edit, $field_edit);
 
diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/FieldUiIntegrationTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/FieldUiIntegrationTest.php
index c87def8210b807dde1d17579e363e8cef13f7725..4e26cd1739d3e6d7424c53677dedfe1ae18fdeb8 100644
--- a/core/modules/media_library/tests/src/FunctionalJavascript/FieldUiIntegrationTest.php
+++ b/core/modules/media_library/tests/src/FunctionalJavascript/FieldUiIntegrationTest.php
@@ -71,8 +71,6 @@ public function testFieldUiIntegration() {
     $page->fillField('label', 'Shatner');
     $this->waitForText('field_shatner');
     $page->pressButton('Continue');
-    $this->assertMatchesRegularExpression('/.*article\/add-storage\/node\/field_shatner.*/', $this->getUrl());
-    $page->pressButton('Continue');
     $this->assertMatchesRegularExpression('/.*article\/add-field\/node\/field_shatner.*/', $this->getUrl());
     $assert_session->pageTextNotContains('Undefined index: target_bundles');
     $this->waitForFieldExists('Type One')->check();
@@ -91,7 +89,7 @@ public function testFieldUiIntegration() {
       ->pressButton('Add media');
     $this->waitForText('Add or select media');
     $this->selectMediaItem(0);
-    $this->pressInsertSelected();
+    $this->pressInsertSelected('Added one media item.');
 
     $page->pressButton('Save settings');
     $assert_session->pageTextContains('Saved Shatner configuration.');
diff --git a/core/modules/node/tests/src/Functional/NodeTranslationUITest.php b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
index 202fcbceae2d341cd52cc0e72f2127612e1e9f3b..3d17ab6cf1a089cc8f8cdf366176aa516008aa03 100644
--- a/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
+++ b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
@@ -559,8 +559,8 @@ public function testDetailsTitleIsNotEscaped() {
     $this->drupalLogin($this->administrator);
     // Make the image field a multi-value field in order to display a
     // details form element.
-    $edit = ['cardinality_number' => 2];
-    $this->drupalGet('admin/structure/types/manage/article/fields/node.article.field_image/storage');
+    $edit = ['field_storage[subform][cardinality_number]' => 2];
+    $this->drupalGet('admin/structure/types/manage/article/fields/node.article.field_image');
     $this->submitForm($edit, 'Save');
 
     // Make the image field non-translatable.
diff --git a/core/modules/node/tests/src/Functional/NodeTypeTranslationTest.php b/core/modules/node/tests/src/Functional/NodeTypeTranslationTest.php
index 4bfbbe48de5dea1c0ee4fa12d02135eb42b32805..043cdb80b4d5fed775a1199846de33930e49b88d 100644
--- a/core/modules/node/tests/src/Functional/NodeTypeTranslationTest.php
+++ b/core/modules/node/tests/src/Functional/NodeTypeTranslationTest.php
@@ -173,7 +173,7 @@ public function testNodeTypeTitleLabelTranslation() {
       'label' => 'Email',
       'field_name' => 'email',
     ], 'Continue');
-    $this->submitForm([], 'Continue');
+    $this->submitForm([], 'Update settings');
     $this->submitForm([], 'Save settings');
 
     $type = $this->randomMachineName(16);
diff --git a/core/modules/options/options.module b/core/modules/options/options.module
index 608f4bcc6586553976e12ab1fdbf416a931f0315..30aefdd33cadbdaea59dec819eb4afd74f51f122 100644
--- a/core/modules/options/options.module
+++ b/core/modules/options/options.module
@@ -5,12 +5,10 @@
  * Defines selection, check box and radio button widgets for text and numeric fields.
  */
 
-use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Url;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
-use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\field\FieldStorageConfigInterface;
 
@@ -138,20 +136,3 @@ function _options_values_in_use($entity_type, $field_name, $values) {
 
   return FALSE;
 }
-
-/**
- * Implements hook_form_FORM_ID_alter().
- *
- * Add additional classes to enable styling for list field types.
- *
- * @see \Drupal\options\Plugin\Field\FieldType\ListItemBase::storageSettingsForm
- */
-function options_form_field_storage_config_edit_form_alter(&$form, FormStateInterface $form_state) {
-  $table = &NestedArray::getValue($form, ['settings', 'allowed_values', 'table']);
-  if (!$table) {
-    return;
-  }
-
-  $form['#attached']['library'][] = 'field_ui/drupal.field_ui';
-  $table['#attributes']['class'][] = 'allowed-values-table';
-}
diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php b/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
index 3bdf31578a8d71a9902a33afce6e5000ba71548c..fbd26da53aaecad7c12413b830fc29f160a45b13 100644
--- a/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
+++ b/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
@@ -93,6 +93,8 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state
     if (!array_key_exists('allowed_values', $form_state->getStorage())) {
       $form_state->set('allowed_values', $this->getFieldDefinition()->getSetting('allowed_values'));
     }
+    $form['field_storage_submit']['#submit'][] = [static::class, 'submitFieldStorageUpdate'];
+    $form['field_storage_submit']['#limit_validation_errors'] = [];
 
     $allowed_values = $form_state->getStorage()['allowed_values'];
     $allowed_values_function = $this->getSetting('allowed_values_function');
@@ -122,6 +124,7 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state
       '#attributes' => [
         'id' => 'allowed-values-order',
         'data-field-list-table' => TRUE,
+        'class' => ['allowed-values-table'],
       ],
       '#tabledrag' => [
         [
@@ -131,7 +134,10 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state
         ],
       ],
       '#attached' => [
-        'library' => ['core/drupal.fieldListKeyboardNavigation'],
+        'library' => [
+          'core/drupal.fieldListKeyboardNavigation',
+          'field_ui/drupal.field_ui',
+        ],
       ],
     ];
 
@@ -293,12 +299,17 @@ public static function deleteSubmit(array $form, FormStateInterface $form_state)
     $remaining_allowed_values = array_diff($allowed_values, [$item_to_be_removed]);
     $form_state->set('allowed_values', $remaining_allowed_values);
 
-    $delta = $button['#delta'];
-    $user_input = $form_state->getUserInput();
     // The user input is directly modified to preserve the rest of the data on
     // the page as it cannot be rebuilt from a fresh form state.
-    unset($user_input['settings']['allowed_values']['table'][$delta]);
-    $user_input['settings']['allowed_values']['table'] = array_values($user_input['settings']['allowed_values']['table']);
+    $user_input = $form_state->getUserInput();
+    NestedArray::unsetValue($user_input, $element['#parents']);
+
+    // Reset the keys in the array.
+    $table_parents = $element['#parents'];
+    array_pop($table_parents);
+    $new_values = array_values(NestedArray::getValue($user_input, $table_parents));
+    NestedArray::setValue($user_input, $table_parents, $new_values);
+
     $form_state->setUserInput($user_input);
     $form_state->set('items_count', $form_state->get('items_count') - 1);
 
@@ -350,7 +361,7 @@ public static function validateAllowedValues($element, FormStateInterface $form_
     }, Element::children($element['table'])), function ($item) {
       return $item;
     });
-    if ($reordered_items = $form_state->getValue(['settings', 'allowed_values', 'table'])) {
+    if ($reordered_items = $form_state->getValue([...$element['#parents'], 'table'])) {
       uksort($items, function ($a, $b) use ($reordered_items) {
         $a_weight = $reordered_items[$a]['weight'] ?? 0;
         $b_weight = $reordered_items[$b]['weight'] ?? 0;
@@ -552,4 +563,11 @@ protected static function castAllowedValue($value) {
     return $value;
   }
 
+  /**
+   * Resets the static variable on field storage update.
+   */
+  public static function submitFieldStorageUpdate() {
+    drupal_static_reset('options_allowed_values');
+  }
+
 }
diff --git a/core/modules/options/tests/src/Functional/OptionsFieldUITest.php b/core/modules/options/tests/src/Functional/OptionsFieldUITest.php
index c89e3e8ea3170ee2d5bcdb2a7ad25c3d3b0360ab..c56f2f37d14b9f5c40319d7e8243f5651c08bfb7 100644
--- a/core/modules/options/tests/src/Functional/OptionsFieldUITest.php
+++ b/core/modules/options/tests/src/Functional/OptionsFieldUITest.php
@@ -96,31 +96,31 @@ public function testOptionsAllowedValuesInteger() {
 
     // Explicit integer keys.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 0,
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => 2,
-      'settings[allowed_values][table][1][item][label]' => 'Two',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => 2,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Two',
     ];
     $array = [0 => 'Zero', 2 => 'Two'];
     $this->assertAllowedValuesInput($input, $array, 'Integer keys are accepted.');
 
     // Non-integer keys.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 1.1,
-      'settings[allowed_values][table][0][item][label]' => 'One',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 1.1,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'One',
     ];
     $this->assertAllowedValuesInput($input, 'keys must be integers', 'Non integer keys are rejected.');
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 'abc',
-      'settings[allowed_values][table][0][item][label]' => 'abc',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'abc',
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'abc',
     ];
     $this->assertAllowedValuesInput($input, 'keys must be integers', 'Non integer keys are rejected.');
 
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 0,
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => 1,
-      'settings[allowed_values][table][1][item][label]' => 'One',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => 1,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
     ];
     $array = [0 => 'Zero', 1 => 'One'];
     $this->assertAllowedValuesInput($input, $array, '');
@@ -148,10 +148,10 @@ public function testOptionsAllowedValuesInteger() {
 
     // Check that the same key can only be used once.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 0,
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => 0,
-      'settings[allowed_values][table][1][item][label]' => 'One',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => 0,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
     ];
     $array = ['0' => 'One'];
     $this->assertAllowedValuesInput($input, $array, 'Same value cannot be used multiple times.');
@@ -168,39 +168,39 @@ public function testOptionsAllowedValuesFloat() {
 
     // Explicit numeric keys.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 0,
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => .5,
-      'settings[allowed_values][table][1][item][label]' => 'Point five',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => .5,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Point five',
     ];
     $array = ['0' => 'Zero', '0.5' => 'Point five'];
     $this->assertAllowedValuesInput($input, $array, 'Integer keys are accepted.');
 
     // Check that values can be added.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 0,
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => .5,
-      'settings[allowed_values][table][1][item][label]' => 'Point five',
-      'settings[allowed_values][table][2][item][key]' => 1,
-      'settings[allowed_values][table][2][item][label]' => 'One',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => .5,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Point five',
+      'field_storage[subform][settings][allowed_values][table][2][item][key]' => 1,
+      'field_storage[subform][settings][allowed_values][table][2][item][label]' => 'One',
     ];
     $array = ['0' => 'Zero', '0.5' => 'Point five', '1' => 'One'];
     $this->assertAllowedValuesInput($input, $array, 'Values can be added.');
     // Non-numeric keys.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 'abc',
-      'settings[allowed_values][table][0][item][label]' => 'abc',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'abc',
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'abc',
     ];
     $this->assertAllowedValuesInput($input, 'each key must be a valid integer or decimal', 'Non numeric keys are rejected.');
 
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 0,
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => .5,
-      'settings[allowed_values][table][1][item][label]' => 'Point five',
-      'settings[allowed_values][table][2][item][key]' => 2,
-      'settings[allowed_values][table][2][item][label]' => 'Two',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => .5,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Point five',
+      'field_storage[subform][settings][allowed_values][table][2][item][key]' => 2,
+      'field_storage[subform][settings][allowed_values][table][2][item][label]' => 'Two',
     ];
     $array = ['0' => 'Zero', '0.5' => 'Point five', '2' => 'Two'];
     $this->assertAllowedValuesInput($input, $array, '');
@@ -227,20 +227,20 @@ public function testOptionsAllowedValuesFloat() {
     $this->assertSame($field_storage->getSetting('allowed_values'), [0 => 'Zero', 2 => 'Two']);
 
     $input = [
-      'settings[allowed_values][table][0][item][key]' => .5,
-      'settings[allowed_values][table][0][item][label]' => 'Point five',
-      'settings[allowed_values][table][1][item][key]' => .5,
-      'settings[allowed_values][table][1][item][label]' => 'Half',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => .5,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Point five',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => .5,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Half',
     ];
     $array = ['0.5' => 'Half'];
     $this->assertAllowedValuesInput($input, $array, 'Same value cannot be used multiple times.');
 
     // Check that different forms of the same float value cannot be used.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => .5,
-      'settings[allowed_values][table][0][item][label]' => 'Point five',
-      'settings[allowed_values][table][1][item][key]' => 0.5,
-      'settings[allowed_values][table][1][item][label]' => 'Half',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => .5,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Point five',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => 0.5,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Half',
     ];
     $array = ['0.5' => 'Half'];
     $this->assertAllowedValuesInput($input, $array, 'Different forms of the same value cannot be used.');
@@ -257,28 +257,28 @@ public function testOptionsAllowedValuesText() {
 
     // Explicit keys.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => '_zero',
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => '_one',
-      'settings[allowed_values][table][1][item][label]' => 'One',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => '_zero',
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => '_one',
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
     ];
     $array = ['_zero' => 'Zero', '_one' => 'One'];
     $this->assertAllowedValuesInput($input, $array, 'Explicit keys are accepted.');
 
     // Overly long keys.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 'zero',
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => $this->randomMachineName(256),
-      'settings[allowed_values][table][1][item][label]' => 'One',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'zero',
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => $this->randomMachineName(256),
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
     ];
     $this->assertAllowedValuesInput($input, 'each key must be a string at most 255 characters long', 'Overly long keys are rejected.');
 
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 'zero',
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => 'one',
-      'settings[allowed_values][table][1][item][label]' => 'One',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'zero',
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => 'one',
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
     ];
     $array = ['zero' => 'Zero', 'one' => 'One'];
     $this->assertAllowedValuesInput($input, $array, '');
@@ -293,7 +293,7 @@ public function testOptionsAllowedValuesText() {
     $this->drupalGet($this->adminPath);
     $assert_session->elementExists('css', '#remove_row_button__1');
     $delete_button_1 = $page->findById('remove_row_button__1');
-    $value_field_1 = $page->findField('settings[allowed_values][table][1][item][key]');
+    $value_field_1 = $page->findField('field_storage[subform][settings][allowed_values][table][1][item][key]');
     $this->assertTrue($delete_button_1->hasAttribute('disabled'), 'Button is disabled');
     $this->assertTrue($value_field_1->hasAttribute('disabled'), 'Button is disabled');
 
@@ -308,19 +308,19 @@ public function testOptionsAllowedValuesText() {
 
     // Check that string values with dots can not be used.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 'zero',
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => 'example.com',
-      'settings[allowed_values][table][1][item][label]' => 'Example',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'zero',
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => 'example.com',
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'Example',
     ];
     $this->assertAllowedValuesInput($input, 'The machine-readable name must contain only lowercase letters, numbers, and underscores.', 'String value with dot is not supported.');
 
     // Check that the same key can only be used once.
     $input = [
-      'settings[allowed_values][table][0][item][key]' => 'zero',
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => 'zero',
-      'settings[allowed_values][table][1][item][label]' => 'One',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 'zero',
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => 'zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
     ];
     $array = ['zero' => 'One'];
     $this->assertAllowedValuesInput($input, $array, 'Same value cannot be used multiple times.');
@@ -350,7 +350,7 @@ protected function createOptionsField($type) {
       ->setComponent($this->fieldName)
       ->save();
 
-    $this->adminPath = 'admin/structure/types/manage/' . $this->type . '/fields/node.' . $this->type . '.' . $this->fieldName . '/storage';
+    $this->adminPath = 'admin/structure/types/manage/' . $this->type . '/fields/node.' . $this->type . '.' . $this->fieldName;
   }
 
   /**
@@ -397,10 +397,10 @@ public function testNodeDisplay() {
     $on = $this->randomMachineName();
     $off = $this->randomMachineName();
     $edit = [
-      'settings[allowed_values][table][0][item][key]' => 1,
-      'settings[allowed_values][table][0][item][label]' => $on,
-      'settings[allowed_values][table][1][item][key]' => 0,
-      'settings[allowed_values][table][1][item][label]' => $off,
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 1,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => $on,
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => 0,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => $off,
     ];
 
     $this->drupalGet($this->adminPath);
diff --git a/core/modules/options/tests/src/Functional/OptionsFloatFieldImportTest.php b/core/modules/options/tests/src/Functional/OptionsFloatFieldImportTest.php
index 2610530d2a4b7f8cc530571260d84cfd448ce24d..18cb151f5426809b20aa822f4f2a06e637c87c23 100644
--- a/core/modules/options/tests/src/Functional/OptionsFloatFieldImportTest.php
+++ b/core/modules/options/tests/src/Functional/OptionsFloatFieldImportTest.php
@@ -65,17 +65,17 @@ public function testImport() {
     $field_storage = FieldStorageConfig::loadByName('node', $field_name);
     $this->assertSame($array = ['0' => 'Zero', '0.5' => 'Point five'], $field_storage->getSetting('allowed_values'));
 
-    $admin_path = 'admin/structure/types/manage/' . $type . '/fields/node.' . $type . '.' . $field_name . '/storage';
+    $admin_path = 'admin/structure/types/manage/' . $type . '/fields/node.' . $type . '.' . $field_name;
 
     // Export active config to sync.
     $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
 
     // Set the active to not use dots in the allowed values key names.
     $edit = [
-      'settings[allowed_values][table][0][item][key]' => 0,
-      'settings[allowed_values][table][0][item][label]' => 'Zero',
-      'settings[allowed_values][table][1][item][key]' => 1,
-      'settings[allowed_values][table][1][item][label]' => 'One',
+      'field_storage[subform][settings][allowed_values][table][0][item][key]' => 0,
+      'field_storage[subform][settings][allowed_values][table][0][item][label]' => 'Zero',
+      'field_storage[subform][settings][allowed_values][table][1][item][key]' => 1,
+      'field_storage[subform][settings][allowed_values][table][1][item][label]' => 'One',
     ];
     $this->drupalGet($admin_path);
     $this->submitForm($edit, 'Save');
diff --git a/core/modules/options/tests/src/FunctionalJavascript/OptionsFieldUITest.php b/core/modules/options/tests/src/FunctionalJavascript/OptionsFieldUITest.php
index 593fd85ef041dd3ce49635776df84995333f3748..e74463f2cd7748d895afa42a6bec2162aab391db 100644
--- a/core/modules/options/tests/src/FunctionalJavascript/OptionsFieldUITest.php
+++ b/core/modules/options/tests/src/FunctionalJavascript/OptionsFieldUITest.php
@@ -5,6 +5,7 @@
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use Drupal\Tests\field_ui\Traits\FieldUiJSTestTrait;
 
 /**
  * Tests the Options field UI functionality.
@@ -13,6 +14,8 @@
  */
 class OptionsFieldUITest extends WebDriverTestBase {
 
+  use FieldUiJSTestTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -91,9 +94,10 @@ public function testOptionsAllowedValues($option_type, $options, $is_string_opti
     $expected_rows = 1;
     $this->assertAllowValuesRowCount(1);
     foreach ($options as $option_key => $option_label) {
-      $enter_element_name = $label_element_name = "settings[allowed_values][table][$i][item][label]";
+      $enter_element_name = $label_element_name = "field_storage[subform][settings][allowed_values][table][$i][item][label]";
       $page->fillField($label_element_name, $option_label);
-      $key_element_name = "settings[allowed_values][table][$i][item][key]";
+      $this->assertSession()->assertWaitOnAjaxRequest();
+      $key_element_name = "field_storage[subform][settings][allowed_values][table][$i][item][key]";
 
       // Add keys if not string option list.
       if (!$is_string_option) {
@@ -104,6 +108,7 @@ public function testOptionsAllowedValues($option_type, $options, $is_string_opti
         $enter_element_name = $key_element_name;
         $this->assertHasFocusByAttribute('name', $key_element_name);
         $page->fillField($key_element_name, $option_key);
+        $this->assertSession()->assertWaitOnAjaxRequest();
       }
       else {
         $this->assertFalse($assert->fieldExists($key_element_name)->isVisible());
@@ -146,8 +151,8 @@ public function testOptionsAllowedValues($option_type, $options, $is_string_opti
 
       $i++;
       $expected_rows++;
-      $this->assertSession()->waitForElementVisible('css', "[name='settings[allowed_values][table][$i][item][label]']");
-      $this->assertHasFocusByAttribute('name', "settings[allowed_values][table][$i][item][label]");
+      $this->assertSession()->waitForElementVisible('css', "[name='field_storage[subform][settings][allowed_values][table][$i][item][label]']");
+      $this->assertHasFocusByAttribute('name', "field_storage[subform][settings][allowed_values][table][$i][item][label]");
       $this->assertAllowValuesRowCount($expected_rows);
 
       if ($is_string_option) {
@@ -171,8 +176,8 @@ public function testOptionsAllowedValues($option_type, $options, $is_string_opti
     // Test the order of the option list on admin path.
     $this->drupalGet($this->adminPath);
     $this->assertOrder(['First', 'Second', 'Third', ''], $is_string_option);
-    $drag_handle = $page->find('css', '[data-drupal-selector="edit-settings-allowed-values-table-0"] .tabledrag-handle');
-    $target = $page->find('css', '[data-drupal-selector="edit-settings-allowed-values-table-2"]');
+    $drag_handle = $page->find('css', '[data-drupal-selector="edit-field-storage-subform-settings-allowed-values-table-0"] .tabledrag-handle');
+    $target = $page->find('css', '[data-drupal-selector="edit-field-storage-subform-settings-allowed-values-table-2"]');
 
     // Change the order the items appear.
     $drag_handle->dragTo($target);
@@ -202,6 +207,47 @@ public function testOptionsAllowedValues($option_type, $options, $is_string_opti
     $this->assertOrder(['Second', 'First', ''], $is_string_option);
   }
 
+  /**
+   * Tests that the allowed options are available to the default value widget.
+   */
+  public function testDefaultValueOptions() {
+    $page = $this->getSession()->getPage();
+    $assert_session = $this->assertSession();
+    $bundle_path = 'admin/structure/types/manage/' . $this->type;
+    // Create a field of type list:string.
+    $this->fieldUIAddNewFieldJS($bundle_path, 'test_string_list', 'Test string list', 'list_string', FALSE);
+    $page->findField('field_storage[subform][settings][allowed_values][table][0][item][label]')->setValue('first');
+    $assert_session->assertWaitOnAjaxRequest();
+    $page->findField('set_default_value')->setValue(TRUE);
+    // Assert that the option added in the subform is available to the default
+    // value field.
+    $this->assertSession()->optionExists('default_value_input[field_test_string_list]', 'first');
+    $page->pressButton('Add another item');
+    $this->assertNotNull($assert_session->waitForElement('css', "[name='field_storage[subform][settings][allowed_values][table][1][item][label]']"));
+    $page->findField('field_storage[subform][settings][allowed_values][table][1][item][label]')->setValue('second');
+    $assert_session->assertWaitOnAjaxRequest();
+    $assert_session->optionExists('default_value_input[field_test_string_list]', 'second');
+    $page->selectFieldOption('default_value_input[field_test_string_list]', 'second');
+    $page->pressButton('Save settings');
+    $assert_session->pageTextContains('Saved Test string list configuration.');
+
+    // Create a field of type list:integer.
+    $this->fieldUIAddNewFieldJS($bundle_path, 'test_int_list', 'Test int list', 'list_integer', FALSE);
+    $page->findField('field_storage[subform][settings][allowed_values][table][0][item][label]')->setValue('first');
+    $assert_session->assertWaitOnAjaxRequest();
+    // Assert that no validation is performed.
+    $assert_session->statusMessageNotContains('Value field is required.');
+    $page->findField('field_storage[subform][settings][allowed_values][table][0][item][key]')->setValue(1);
+    $assert_session->assertWaitOnAjaxRequest();
+    $page->findField('set_default_value')->setValue(TRUE);
+    // Assert that the option added in the subform is available to the default
+    // value field.
+    $this->assertSession()->optionExists('default_value_input[field_test_int_list]', 'first');
+    $page->selectFieldOption('default_value_input[field_test_int_list]', 'first');
+    $page->pressButton('Save settings');
+    $assert_session->pageTextContains('Saved Test int list configuration.');
+  }
+
   /**
    * Asserts the order of provided option list on admin path.
    *
@@ -262,7 +308,7 @@ protected function createOptionsField($type) {
       ->setComponent($this->fieldName)
       ->save();
 
-    $this->adminPath = 'admin/structure/types/manage/' . $this->type . '/fields/node.' . $this->type . '.' . $this->fieldName . '/storage';
+    $this->adminPath = 'admin/structure/types/manage/' . $this->type . '/fields/node.' . $this->type . '.' . $this->fieldName;
   }
 
   /**
@@ -364,7 +410,7 @@ private function exposeOptionMachineName(int $row): void {
     $index = $row - 1;
     $rows = $this->getSession()->getPage()->findAll('css', '#allowed-values-order tr.draggable');
     $this->assertSession()->buttonExists('Edit', $rows[$index])->click();
-    $this->assertSession()->waitForElementVisible('css', "[name='settings[allowed_values][table][$index][item][key]']");
+    $this->assertSession()->waitForElementVisible('css', "[name='field_storage[subform][settings][allowed_values][table][$index][item][key]']");
   }
 
 }
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php b/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php
index 7d001f8df336c551a89564278eb12c74a5374035..89a185704257354b3ea1d036c4a3787b8434d583 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php
@@ -36,7 +36,7 @@ public function testAddReferenceFieldTargetingEntityTypeWithoutId() {
     // Entity types without an ID key should not be presented as options when
     // creating an entity reference field in the UI.
     $this->fieldUIAddNewField("/admin/structure/types/manage/$node_type", 'test_reference_field', 'Test Field', 'entity_reference', [], [], FALSE);
-    $this->assertSession()->optionNotExists('settings[target_type]', 'entity_test_no_id');
+    $this->assertSession()->optionNotExists('field_storage[subform][settings][target_type]', 'entity_test_no_id');
 
     // Trying to do it programmatically should raise an exception.
     $this->expectException('\Drupal\Core\Field\FieldException');
diff --git a/core/modules/system/tests/src/Functional/System/DateTimeTest.php b/core/modules/system/tests/src/Functional/System/DateTimeTest.php
index d6f62b7878c24c8a56e2d32eade745a8699f8b56..c3eaf3ecca14d11c335f62dd2c1b230a310172b2 100644
--- a/core/modules/system/tests/src/Functional/System/DateTimeTest.php
+++ b/core/modules/system/tests/src/Functional/System/DateTimeTest.php
@@ -215,9 +215,9 @@ public function testEnteringDateTimeViaSelectors() {
     $this->assertSession()->statusCodeEquals(200);
 
     $storage_edit = [
-      'settings[datetime_type]' => 'datetime',
-      'cardinality' => 'number',
-      'cardinality_number' => '1',
+      'field_storage[subform][settings][datetime_type]' => 'datetime',
+      'field_storage[subform][cardinality]' => 'number',
+      'field_storage[subform][cardinality_number]' => '1',
     ];
     $this->fieldUIAddNewField('admin/structure/types/manage/page_with_date', 'dt', 'dt', 'datetime', $storage_edit);
 
diff --git a/core/tests/Drupal/Tests/Core/Form/SubformStateTest.php b/core/tests/Drupal/Tests/Core/Form/SubformStateTest.php
index 6d5318402d924b9b79a5aeaac286659ca42ce120..01d57e46a3eae77906b83c4bdd1deb576303be35 100644
--- a/core/tests/Drupal/Tests/Core/Form/SubformStateTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/SubformStateTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\Core\Form;
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Form\SubformState;
@@ -311,4 +312,18 @@ public function testSetErrorByName() {
     $this->assertSame($subform_state, $subform_state->setErrorByName($subform_error_name, $message));
   }
 
+  /**
+   * @covers ::getFormObject
+   */
+  public function testFormObject() {
+    $parent_form_state = $this->prophesize(FormStateInterface::class);
+    $parent_form_object = $this->prophesize(FormInterface::class)->reveal();
+    $parent_form_state->getFormObject()->willReturn($parent_form_object)->shouldBeCalledOnce();
+
+    $subform_form_object = $this->prophesize(FormInterface::class)->reveal();
+    $subform_state = SubformState::createForSubform($this->parentForm['dog'], $this->parentForm, $parent_form_state->reveal(), $subform_form_object);
+    $this->assertSame($subform_form_object, $subform_state->getFormObject());
+    $this->assertSame($parent_form_object, $subform_state->getCompleteFormState()->getFormObject());
+  }
+
 }