diff --git a/core/lib/Drupal/Core/Field/WidgetBase.php b/core/lib/Drupal/Core/Field/WidgetBase.php
index b1579f2445..b90d9b941c 100644
--- a/core/lib/Drupal/Core/Field/WidgetBase.php
+++ b/core/lib/Drupal/Core/Field/WidgetBase.php
@@ -240,9 +240,21 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
             'effect' => 'fade',
           ],
         ];
+
+        // Allow modules to alter the field multiple widget form element.
+        $context = [
+          'form' => $form,
+          'widget' => $this,
+          'items' => $items,
+          'delta' => $delta,
+          'default' => $this->isDefaultValueWidget($form_state),
+        ];
+        \Drupal::moduleHandler()->alter([
+          'field_widget_multiple_form',
+          'field_widget_multiple_' . $this->getPluginId() . '_form',
+        ], $elements, $form_state, $context);
       }
     }
-
     return $elements;
   }
 
diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index 130f923180..aed3dde820 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -183,6 +183,7 @@ function hook_field_widget_info_alter(array &$info) {
  * @see \Drupal\Core\Field\WidgetBaseInterface::form()
  * @see \Drupal\Core\Field\WidgetBase::formSingleElement()
  * @see hook_field_widget_WIDGET_TYPE_form_alter()
+ * @see hook_field_widget_multiple_form_alter()
  */
 function hook_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
   // Add a css class to widget form elements for all fields of type mytype.
@@ -212,6 +213,7 @@ function hook_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInte
  * @see \Drupal\Core\Field\WidgetBaseInterface::form()
  * @see \Drupal\Core\Field\WidgetBase::formSingleElement()
  * @see hook_field_widget_form_alter()
+ * @see hook_field_widget_multiple_WIDGET_TYPE_form_alter()
  */
 function hook_field_widget_WIDGET_TYPE_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
   // Code here will only act on widgets of type WIDGET_TYPE.  For example,
@@ -220,6 +222,78 @@ function hook_field_widget_WIDGET_TYPE_form_alter(&$element, \Drupal\Core\Form\F
   $element['#autocomplete_route_name'] = 'mymodule.autocomplete_route';
 }
 
+/**
+ * Alter forms for multivalue field widgets provided by other modules.
+ *
+ * To adjust the individual widgets for the values themselves, use
+ * hook_field_widget_form_alter() instead.
+ *
+ * @param $element
+ *   The field widget form element as constructed by
+ *   \Drupal\Core\Field\WidgetBaseInterface::form().
+ * @param $form_state
+ *   The current state of the form.
+ * @param $context
+ *   An associative array containing the following key-value pairs:
+ *   - form: The form structure to which widgets are being attached. This may be
+ *     a full form structure, or a sub-element of a larger form.
+ *   - widget: The widget plugin instance.
+ *   - items: The field values, as a
+ *     \Drupal\Core\Field\FieldItemListInterface object.
+ *   - delta: The order of this item in the array of subelements (0, 1, 2, etc).
+ *   - default: A boolean indicating whether the form is being shown as a dummy
+ *     form to set default values.
+ *
+ * @see \Drupal\Core\Field\WidgetBaseInterface::form()
+ * @see \Drupal\Core\Field\WidgetBase::formSingleElement()
+ * @see hook_field_widget_form_alter()
+ * @see hook_field_widget_multiple_WIDGET_TYPE_form_alter()
+ */
+function hook_field_widget_multiple_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
+  // Add a css class to the overall element of any widget.
+  $element['#attributes']['class'][] = 'myclass';
+}
+
+/**
+ * Alter multi-value widget forms for a widget provided by another module.
+ *
+ * Modules can implement hook_field_widget_multiple_WIDGET_TYPE_form_alter() to
+ * modify a specific widget form, rather than using
+ * hook_field_widget_form_alter() and checking the widget type.
+ *
+ * To modify the widget for individual itesm, use
+ * hook_field_widget_WIDGET_TYPE_form_alter() instead.
+ *
+ * @param $element
+ *   The field widget form element as constructed by
+ *   \Drupal\Core\Field\WidgetBaseInterface::form().
+ * @param $form_state
+ *   The current state of the form.
+ * @param $context
+ *   An associative array. See hook_field_widget_form_alter() for the structure
+ *   and content of the array.
+ *
+ * @see \Drupal\Core\Field\WidgetBaseInterface::form()
+ * @see \Drupal\Core\Field\WidgetBase::formSingleElement()
+ * @see hook_field_widget_multiple_form_alter()
+ */
+function hook_field_widget_multiple_WIDGET_TYPE_form_alter(&$elements, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
+  // Alter the overall title for the multivalue widget (the table header,
+  // above the rows).
+  $elements['#title'] = \Drupal::t('Review your %title entries', ['%title' => $elements['#title']]);
+
+  if (!empty($elements['#description']))  {
+    // Add a paragraph to the description, above all the rows.
+    $elements['#description'] = [
+      '#markup' => \Drupal::t('These items might be out of date.'),
+      '#markup' => $elements['#description'],
+    ];
+  }
+  else {
+    $elements['#description'] = \Drupal::t('These items might be out of date.');
+  }
+}
+
 /**
  * @} End of "defgroup field_widget".
  */
diff --git a/core/modules/media/media.module b/core/modules/media/media.module
index 296945973a..fa76bb406a 100644
--- a/core/modules/media/media.module
+++ b/core/modules/media/media.module
@@ -9,6 +9,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Render\Markup;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Url;
@@ -172,3 +173,99 @@ function media_form_field_ui_field_storage_add_form_alter(&$form, FormStateInter
   $form['add']['new_storage_type']['#weight'] = 0;
   $form['add']['description_wrapper']['#weight'] = 1;
 }
+
+/**
+ * Implements hook_field_widget_WIDGET_TYPE_form_alter().
+ */
+function media_field_widget_entity_reference_autocomplete_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
+  // Only act on Media entity reference autocomplete fields.
+  if (!empty($element['target_id']['#type']) && ($element['target_id']['#type'] === 'entity_autocomplete') && ($element['target_id']['#target_type'] === 'media')) {
+
+    $label = $element['target_id']['#title'];
+    $element['target_id']['#title'] = t('<h4>Use existing %title media</h4>', ['%title' => $label]);
+
+    $reuse_message = t('Type part of the media name. See the <a href=":list" target="_blank">media list</a> (opens a new window) to help locate media.', [':list' => Url::fromRoute('entity.media.collection')->toString()]);
+
+    if (empty($element['target_id']['#description'])) {
+      $element['target_id']['#description'] = $reuse_message;
+    }
+    else {
+      $element['target_id']['#description'] = ['#markup' => $element['target_id']['#description'], '#markup' => $reuse_message];
+    }
+    // @todo There must be an easier way to get this.
+    $cardinality = $context['items']->getFieldDefinition()->getFieldStorageDefinition()->getCardinality();
+
+    // Only add the suffix if the user also has permission to create
+    // media. Some sites might allow content authors to pick approved media
+    // but not to submit their own.
+    // Don't add a suffix if the field is multi-value, as that would result in
+    // repetitive, spammy headers on every item.
+    if ($cardinality === 1) {
+      $access = FALSE;
+      $settings = $context['items']->getSettings();
+      if (!empty($settings['handler_settings']['target_bundles'])) {
+        foreach ($settings['handler_settings']['target_bundles'] as $bundle) {
+          if (\Drupal::entityTypeManager()->getAccessControlHandler('media')->createAccess($bundle, \Drupal::currentUser())) {
+            $access = TRUE;
+            break;
+          }
+        }
+      }
+      else {
+        $access = \Drupal::entityTypeManager()->getAccessControlHandler('media')->createAccess(NULL, \Drupal::currentUser());
+      }
+      if ($access) {
+        $new_message = t('<h4>Uploading new %title media</h4> Upload your media on the <a href=":add" target="_blank">media upload form</a> (opens a new window), then add it by name to the field below.', ['%title' => $label, ':add' => Url::fromRoute('entity.media.add_page')->toString()]);
+        if (empty($element['#prefix'])) {
+          $element['#prefix'] = $new_message;
+        }        else {
+          $element['#prefix'] = Markup::create($element['#prefix'] . $new_message);
+        }
+      }
+    }
+  }
+}
+
+
+/**
+ * Implements hook_field_widget_multiple_WIDGET_TYPE_form_alter().
+ */
+function media_field_widget_multiple_entity_reference_autocomplete_form_alter(&$elements, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
+  // Only act on Media entity reference autocomplete fields.
+  if (!empty($elements[0]['target_id']['#type']) && ($elements[0]['target_id']['#type'] === 'entity_autocomplete') && ($elements[0]['target_id']['#target_type'] === 'media')) {
+
+    // Use the user-defined label for the field to indicate that the form
+    // allows reusing existing media.
+    $label = $elements['#title'];
+    $elements['#title'] = t('<h4>Use existing %title media</h4>', ['%title' => $label]);
+
+    // Only add the suffix if the user also has permission to create
+    // media. Some sites might allow content authors to pick approved media
+    // but not to submit their own.
+    $access = FALSE;
+    $settings = $context['items']->getSettings();
+    if (!empty($settings['handler_settings']['target_bundles'])) {
+      foreach ($settings['handler_settings']['target_bundles'] as $bundle) {
+        if (\Drupal::entityTypeManager()->getAccessControlHandler('media')->createAccess($bundle, \Drupal::currentUser())) {
+          $access = TRUE;
+          break;
+        }
+      }
+    }
+    else {
+      $access = \Drupal::entityTypeManager()->getAccessControlHandler('media')->createAccess(NULL, \Drupal::currentUser());
+    }
+    if ($access) {
+      $new_message = t('<h4>Uploading new %title media</h4> Upload your media on the <a href=":add" target="_blank">media upload form</a> (opens a new window), then add it by name to the field below.', ['%title' => $label, ':add' => Url::fromRoute('entity.media.add_page')->toString()]);
+      if (empty($elements['#prefix'])) {
+        $elements['#prefix'] = $new_message;
+      }
+      else {
+        // @todo #prefix doesn't seem to accept a render array. We need to
+        //   append to the beginning of the current suffix, which is used to
+        //   clearfix floating things.
+        $elements['#prefix'] = Markup::create($elements['#prefix'] . $new_message);
+      }
+    }
+  }
+}
