diff --git a/core/modules/field/migrations/d6_field_formatter_settings.yml b/core/modules/field/migrations/d6_field_formatter_settings.yml
index 23ea2bd0e0..b36eba6aca 100644
--- a/core/modules/field/migrations/d6_field_formatter_settings.yml
+++ b/core/modules/field/migrations/d6_field_formatter_settings.yml
@@ -57,123 +57,132 @@ process:
   field_name: field_name
   "options/label": label
   "options/weight": weight
+  formatter_type_mapped:
+    -
+      plugin: static_map
+      field_formatter_map_alter: true
+      bypass: true
+      source:
+        - type
+        - 'display_settings/format'
+      map:
+        number_integer:
+          default: number_integer
+          us_0: number_integer
+          be_0: number_integer
+          fr_0: number_integer
+          unformatted: number_unformatted
+        number_float:
+          default: number_decimal
+          us_0: number_decimal
+          us_1: number_decimal
+          us_2: number_decimal
+          be_0: number_decimal
+          be_1: number_decimal
+          be_2: number_decimal
+          fr_0: number_decimal
+          fr_1: number_decimal
+          fr_2: number_decimal
+          unformatted: number_unformatted
+        number_decimal:
+          default: number_decimal
+          us_0: number_decimal
+          us_1: number_decimal
+          us_2: number_decimal
+          be_0: number_decimal
+          be_1: number_decimal
+          be_2: number_decimal
+          fr_0: number_decimal
+          fr_1: number_decimal
+          fr_2: number_decimal
+          unformatted: number_unformatted
+        email:
+          default: email_mailto
+          spamspan: email_mailto
+          contact: email_mailto
+          plain: basic_string
+        fr_phone:
+          default: basic_string
+        be_phone:
+          default: basic_string
+        it_phone:
+          default: basic_string
+        el_phone:
+          default: basic_string
+        ch_phone:
+          default: basic_string
+        ca_phone:
+          default: basic_string
+        cr_phone:
+          default: basic_string
+        pa_phone:
+          default: basic_string
+        gb_phone:
+          default: basic_string
+        ru_phone:
+          default: basic_string
+        ua_phone:
+          default: basic_string
+        es_phone:
+          default: basic_string
+        au_phone:
+          default: basic_string
+        cs_phone:
+          default: basic_string
+        hu_phone:
+          default: basic_string
+        pl_phone:
+          default: basic_string
+        nl_phone:
+          default: basic_string
+        se_phone:
+          default: basic_string
+        za_phone:
+          default: basic_string
+        il_phone:
+          default: basic_string
+        nz_phone:
+          default: basic_string
+        br_phone:
+          default: basic_string
+        cl_phone:
+          default: basic_string
+        cn_phone:
+          default: basic_string
+        hk_phone:
+          default: basic_string
+        mo_phone:
+          default: basic_string
+        ph_phone:
+          default: basic_string
+        sg_phone:
+          default: basic_string
+        jo_phone:
+          default: basic_string
+        eg_phone:
+          default: basic_string
+        pk_phone:
+          default: basic_string
+        int_phone:
+          default: basic_string
+        nodereference:
+          default: entity_reference_label
+          plain: entity_reference_label
+          full: entity_reference_entity_view
+          teaser: entity_reference_entity_view
+        userreference:
+          default: entity_reference_label
+          plain: entity_reference_label
+    -
+      plugin: d6_field_type_defaults
   "options/type":
-      -
-        plugin: static_map
-        bypass: true
-        source:
-          - type
-          - 'display_settings/format'
-        map:
-          number_integer:
-            default: number_integer
-            us_0: number_integer
-            be_0: number_integer
-            fr_0: number_integer
-            unformatted: number_unformatted
-          number_float:
-            default: number_decimal
-            us_0: number_decimal
-            us_1: number_decimal
-            us_2: number_decimal
-            be_0: number_decimal
-            be_1: number_decimal
-            be_2: number_decimal
-            fr_0: number_decimal
-            fr_1: number_decimal
-            fr_2: number_decimal
-            unformatted: number_unformatted
-          number_decimal:
-            default: number_decimal
-            us_0: number_decimal
-            us_1: number_decimal
-            us_2: number_decimal
-            be_0: number_decimal
-            be_1: number_decimal
-            be_2: number_decimal
-            fr_0: number_decimal
-            fr_1: number_decimal
-            fr_2: number_decimal
-            unformatted: number_unformatted
-          email:
-            default: email_mailto
-            spamspan: email_mailto
-            contact: email_mailto
-            plain: basic_string
-          fr_phone:
-            default: basic_string
-          be_phone:
-            default: basic_string
-          it_phone:
-            default: basic_string
-          el_phone:
-            default: basic_string
-          ch_phone:
-            default: basic_string
-          ca_phone:
-            default: basic_string
-          cr_phone:
-            default: basic_string
-          pa_phone:
-            default: basic_string
-          gb_phone:
-            default: basic_string
-          ru_phone:
-            default: basic_string
-          ua_phone:
-            default: basic_string
-          es_phone:
-            default: basic_string
-          au_phone:
-            default: basic_string
-          cs_phone:
-            default: basic_string
-          hu_phone:
-            default: basic_string
-          pl_phone:
-            default: basic_string
-          nl_phone:
-            default: basic_string
-          se_phone:
-            default: basic_string
-          za_phone:
-            default: basic_string
-          il_phone:
-            default: basic_string
-          nz_phone:
-            default: basic_string
-          br_phone:
-            default: basic_string
-          cl_phone:
-            default: basic_string
-          cn_phone:
-            default: basic_string
-          hk_phone:
-            default: basic_string
-          mo_phone:
-            default: basic_string
-          ph_phone:
-            default: basic_string
-          sg_phone:
-            default: basic_string
-          jo_phone:
-            default: basic_string
-          eg_phone:
-            default: basic_string
-          pk_phone:
-            default: basic_string
-          int_phone:
-            default: basic_string
-          nodereference:
-            default: entity_reference_label
-            plain: entity_reference_label
-            full: entity_reference_entity_view
-            teaser: entity_reference_entity_view
-          userreference:
-            default: entity_reference_label
-            plain: entity_reference_label
-      -
-        plugin: d6_field_type_defaults
+    -
+      plugin: field_formatter_fallback
+      field_type: type
+      source: '@formatter_type_mapped'
+    -
+      plugin: skip_on_empty
+      method: row
   "options/settings":
     -
       plugin: static_map
diff --git a/core/modules/field/migrations/d6_field_instance_widget_settings.yml b/core/modules/field/migrations/d6_field_instance_widget_settings.yml
index 9faac2f36b..9c12fd8b6b 100644
--- a/core/modules/field/migrations/d6_field_instance_widget_settings.yml
+++ b/core/modules/field/migrations/d6_field_instance_widget_settings.yml
@@ -40,9 +40,10 @@ process:
   field_name: field_name
   entity_type: 'constants/entity_type'
   'options/weight': weight
-  'options/type':
-    type:
+  widget_type_mapped:
+    -
       plugin: static_map
+      field_widget_map_alter: true
       bypass: true
       source: widget_type
       map:
@@ -60,12 +61,27 @@ process:
         nodereference_buttons: options_buttons
         nodereference_autocomplete: entity_reference_autocomplete_tags
         userreference_select: options_select
+  'options/type':
+    -
+      plugin: get
+      source: '@widget_type_mapped'
+    -
+      plugin: field_widget_fallback
+      field_type: type
   'options/settings':
     -
       plugin: field_instance_widget_settings
       source:
         - widget_type
         - widget_settings
+    -
+      plugin: null_if_not_equal
+      values_to_compare:
+        - '@widget_type_mapped'
+        - '@options/type'
+    -
+      plugin: default_value
+      default_value: []
   'options/third_party_settings': 'constants/third_party_settings'
 
 destination:
diff --git a/core/modules/field/migrations/d7_field_formatter_settings.yml b/core/modules/field/migrations/d7_field_formatter_settings.yml
index f41575fdc8..6cbbba3ee2 100644
--- a/core/modules/field/migrations/d7_field_formatter_settings.yml
+++ b/core/modules/field/migrations/d7_field_formatter_settings.yml
@@ -65,9 +65,10 @@ process:
     plugin: process_field
     source: type
     method: getFieldFormatterType
-  "options/type":
+  formatter_type_mapped:
     -
       plugin: static_map
+      field_formatter_map_alter: true
       bypass: true
       source:
         - '@plugin_id'
@@ -77,6 +78,11 @@ process:
       map: []
     -
       plugin: d7_field_type_defaults
+  "options/type":
+    -
+      plugin: field_formatter_fallback
+      field_type: type
+      source: '@formatter_type_mapped'
     -
       plugin: skip_on_empty
       method: row
@@ -87,9 +93,18 @@ process:
       hidden: true
     default_value: false
   "options/settings":
-    plugin: default_value
-    source: 'formatter/settings'
-    default_value: []
+    -
+      plugin: default_value
+      source: 'formatter/settings'
+      default_value: []
+    -
+      plugin: null_if_not_equal
+      values_to_compare:
+        - '@formatter_type_mapped'
+        - '@options/type'
+    -
+      plugin: default_value
+      default_value: []
   "options/third_party_settings": 'constants/third_party_settings'
 destination:
   plugin: component_entity_display
diff --git a/core/modules/field/migrations/d7_field_instance.yml b/core/modules/field/migrations/d7_field_instance.yml
index 6edf3047d3..6bbdcd1817 100644
--- a/core/modules/field/migrations/d7_field_instance.yml
+++ b/core/modules/field/migrations/d7_field_instance.yml
@@ -38,18 +38,45 @@ process:
           plugin: migration_lookup
           migration: d7_taxonomy_vocabulary
           source: vid
+  widget_with_fallback:
+    # @TODO: add the same mapping as d7_field_instance_widget_settings has.
+    -
+      plugin: get
+      source: 'widget/type'
+    -
+      plugin: field_widget_fallback
+      field_type: type
+      skip_message: true
   settings:
-    plugin: d7_field_instance_settings
-    source:
-      - settings
-      - widget
-      - field_definition
+    -
+      plugin: d7_field_instance_settings
+      source:
+        - settings
+        - widget
+        - field_definition
+    -
+      plugin: null_if_not_equal
+      values_to_compare:
+        - '@widget_with_fallback'
+        - 'widget/type'
+    -
+      plugin: default_value
+      default_value: []
   default_value_function: ''
   default_value:
-    plugin: d7_field_instance_defaults
-    source:
-      - default_value
-      - widget
+    -
+      plugin: d7_field_instance_defaults
+      source:
+        - default_value
+        - widget
+    -
+      plugin: null_if_not_equal
+      values_to_compare:
+        - '@widget_with_fallback'
+        - 'widget/type'
+    -
+      plugin: default_value
+      default_value: []
   translatable: translatable
 destination:
   plugin: entity:field_config
diff --git a/core/modules/field/migrations/d7_field_instance_widget_settings.yml b/core/modules/field/migrations/d7_field_instance_widget_settings.yml
index eaa7831d69..05325c2cfd 100644
--- a/core/modules/field/migrations/d7_field_instance_widget_settings.yml
+++ b/core/modules/field/migrations/d7_field_instance_widget_settings.yml
@@ -44,9 +44,10 @@ process:
     plugin: process_field
     source: type
     method: getFieldWidgetType
-  'options/type':
-    type:
+  widget_type_mapped:
+    -
       plugin: static_map
+      field_widget_map_alter: true
       bypass: true
       source: '@widget_type'
       map:
@@ -61,11 +62,27 @@ process:
         entityreference_autocomplete: entity_reference_autocomplete
         entityreference_autocomplete_tags: entity_reference_autocomplete_tags
         taxonomy_autocomplete: entity_reference_autocomplete
+  'options/type':
+    -
+      plugin: get
+      source: '@widget_type_mapped'
+    -
+      plugin: field_widget_fallback
+      field_type: type
   'options/settings':
-    plugin: field_instance_widget_settings
-    source:
-      - 'widget/type'
-      - 'widget/settings'
+    -
+      plugin: field_instance_widget_settings
+      source:
+        - 'widget/type'
+        - 'widget/settings'
+    -
+      plugin: null_if_not_equal
+      values_to_compare:
+        - '@widget_type_mapped'
+        - '@options/type'
+    -
+      plugin: default_value
+      default_value: []
   'options/third_party_settings': 'constants/third_party_settings'
 destination:
   plugin: component_entity_form_display
diff --git a/core/modules/field/src/Plugin/migrate/process/FieldFormatterFallback.php b/core/modules/field/src/Plugin/migrate/process/FieldFormatterFallback.php
new file mode 100644
index 0000000000..d6f38b0d45
--- /dev/null
+++ b/core/modules/field/src/Plugin/migrate/process/FieldFormatterFallback.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace Drupal\field\Plugin\migrate\process;
+
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\FormatterPluginManager;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\migrate\MigrateException;
+use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\ProcessPluginBase;
+use Drupal\migrate\Row;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Transforms field formatter plugin ID.
+ *
+ * Transform a non-mapped field formatter plugin ID to the D8 default formatter
+ * when the destination plugin ID is not available.
+ *
+ * Saves a migrate message with MigrationInterface::MESSAGE_NOTICE severity on
+ * fallback.
+ *
+ * Available configuration keys:
+ * - field_type: The name of the field type.
+ * - source: The input value - must be a string (optional).
+ *
+ * @code
+ * process:
+ *   valid_field_formatter_plugin_id:
+ *     plugin: field_formatter_fallback
+ *     field_type: '@field_type'
+ *     source: source_plugin_id
+ * @endcode
+ *
+ * @MigrateProcessPlugin(
+ *   id = "field_formatter_fallback"
+ * )
+ */
+class FieldFormatterFallback extends ProcessPluginBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * Field type plugin manager.
+   *
+   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+   */
+  protected $fieldTypeManager;
+
+  /**
+   * The field formatter plugin manager.
+   *
+   * @var \Drupal\Core\Field\FormatterPluginManager
+   */
+  protected $formatterPluginManager;
+
+  /**
+   * Constructs a FieldFormatterFallback plugin instance.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\migrate\Plugin\MigrationInterface $migration
+   *   The current migration.
+   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+   *   The field type manager.
+   * @param \Drupal\Core\Field\FormatterPluginManager $formatter_plugin_manager
+   *   The formatter manager.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, FieldTypePluginManagerInterface $field_type_manager, FormatterPluginManager $formatter_plugin_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->migration = $migration;
+    $this->fieldTypeManager = $field_type_manager;
+    $this->formatterPluginManager = $formatter_plugin_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $migration,
+      $container->get('plugin.manager.field.field_type'),
+      $container->get('plugin.manager.field.formatter')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+    if (empty($this->configuration['field_type'])) {
+      throw new MigrateException('Field type is required for the default field formatter.');
+    }
+    $field_type = $row->get($this->configuration['field_type']);
+    if (empty($field_type)) {
+      throw new MigrateException('Field type is required for the default field formatter.');
+    }
+
+    // If the required field formatter plugin ID is not available on the
+    // destination site, we map it to the default formatter of the given field
+    // type. The 'hidden' value is special in Drupal 7, and not a formatter.
+    if ($value && $value !== 'hidden' && !$this->formatterPluginManager->hasDefinition($value) && $this->fieldTypeManager->hasDefinition($field_type)) {
+      $field_type_definition = $this->fieldTypeManager->getDefinition($field_type);
+      $fallback = $field_type_definition['default_formatter'];
+
+      $message = sprintf('The field formatter plugin ID %s (used on field type %s) could not be mapped to an existing formatter plugin; defaulting to %s and dropping all formatter settings. Either redo the migration with the module installed that provides an equivalent formatter plugin, or modify the entity view display after the migration and manually choose the right field formatter.', $value, $field_type, $fallback);
+      $migrate_executable->saveMessage($message, MigrationInterface::MESSAGE_NOTICE);
+
+      return $fallback;
+    }
+
+    return $value;
+  }
+
+}
diff --git a/core/modules/field/src/Plugin/migrate/process/FieldWidgetFallback.php b/core/modules/field/src/Plugin/migrate/process/FieldWidgetFallback.php
new file mode 100644
index 0000000000..cd915140f2
--- /dev/null
+++ b/core/modules/field/src/Plugin/migrate/process/FieldWidgetFallback.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Drupal\field\Plugin\migrate\process;
+
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\WidgetPluginManager;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\migrate\MigrateException;
+use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\ProcessPluginBase;
+use Drupal\migrate\Row;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Transforms field widget plugin ID.
+ *
+ * Transform field widget plugin ID to the D8 default widget if the destination
+ * widget plugin id is unavailable.
+ *
+ * Saves a migrate message with MigrationInterface::MESSAGE_NOTICE severity on
+ * fallback.
+ *
+ * Available configuration keys:
+ * - field_type: The name of the field type.
+ * - source: The input value - must be a string (optional).
+ * - skip_message: Omit the default migration message when fallback was needed.
+ *     Optional boolean, defaults to FALSE.
+ *
+ * @code
+ * process:
+ *   destination_key:
+ *     plugin: field_widget_fallback
+ *     field_type: '@field_type'
+ *     source: source_property
+ * @endcode
+ *
+ * @MigrateProcessPlugin(
+ *   id = "field_widget_fallback"
+ * )
+ */
+class FieldWidgetFallback extends ProcessPluginBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * Field type plugin manager.
+   *
+   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+   */
+  protected $fieldTypeManager;
+
+  /**
+   * The field widget plugin manager.
+   *
+   * @var \Drupal\Core\Field\WidgetPluginManager
+   */
+  protected $widgetPluginManager;
+
+  /**
+   * Constructs a FieldWidgetFallback plugin instance.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\migrate\Plugin\MigrationInterface $migration
+   *   The current migration.
+   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+   *   The field type manager.
+   * @param \Drupal\Core\Field\WidgetPluginManager $widget_plugin_manager
+   *   The field widget manager.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, FieldTypePluginManagerInterface $field_type_manager, WidgetPluginManager $widget_plugin_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->migration = $migration;
+    $this->fieldTypeManager = $field_type_manager;
+    $this->widgetPluginManager = $widget_plugin_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $migration,
+      $container->get('plugin.manager.field.field_type'),
+      $container->get('plugin.manager.field.widget')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+    if (empty($this->configuration['field_type'])) {
+      throw new MigrateException('Field type is required for the default field widget.');
+    }
+    $field_type = $row->get($this->configuration['field_type']);
+    if (empty($field_type)) {
+      throw new MigrateException('Field type is required for the default field widget.');
+    }
+
+    // If the required field widget plugin ID is not available on the
+    // destination, we replace it with the field's default widget.
+    if (!empty($value) && $this->fieldTypeManager->hasDefinition($field_type) && !$this->widgetPluginManager->hasDefinition($value)) {
+      $field_type_definition = $this->fieldTypeManager->getDefinition($field_type);
+      $fallback = $field_type_definition['default_widget'];
+
+      if (!isset($this->configuration['skip_message']) || empty($this->configuration['skip_message'])) {
+        $message = sprintf('The field widget plugin ID %s (used on field type %s) could not be mapped to an existing widget plugin; defaulting to %s and dropping all widget settings. Either redo the migration with the module installed that provides an equivalent plugin, or modify the entity form display after the migration and manually choose the right field widget.', $value, $field_type, $fallback);
+        $migrate_executable->saveMessage($message, MigrationInterface::MESSAGE_NOTICE);
+      }
+
+      return $fallback;
+    }
+
+    return $value;
+  }
+
+}
diff --git a/core/modules/migrate/src/Plugin/migrate/process/NullIfNotEqual.php b/core/modules/migrate/src/Plugin/migrate/process/NullIfNotEqual.php
new file mode 100644
index 0000000000..21bd890428
--- /dev/null
+++ b/core/modules/migrate/src/Plugin/migrate/process/NullIfNotEqual.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Drupal\migrate\Plugin\migrate\process;
+
+use Drupal\migrate\MigrateException;
+use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\ProcessPluginBase;
+use Drupal\migrate\Row;
+
+/**
+ * Transforms a value by variable comparison.
+ *
+ * Changes the source value to null if the two given variables are not equal.
+ *
+ * Available configuration keys:
+ * - values_to_compare: array of the values to compare.
+ *
+ * @code
+ * process:
+ *   destination_key:
+ *     plugin: null_if_not_equal
+ *     values_to_compare:
+ *       - '@mapped_plugin_id'
+ *       - '@mapped_plugin_id_with_fallback'
+ *     source: source_settings
+ * @endcode
+ *
+ * @MigrateProcessPlugin(
+ *   id = "null_if_not_equal"
+ * )
+ */
+class NullIfNotEqual extends ProcessPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+    if (empty($this->configuration['values_to_compare']) || !is_array($this->configuration['values_to_compare']) || count($this->configuration['values_to_compare']) !== 2) {
+      throw new MigrateException('values_to_compare is a required key for plugin null_if_not_equal.');
+    }
+    list($pid_original_raw, $pid_final_raw) = $this->configuration['values_to_compare'];
+    if ($row->get($pid_original_raw) !== $row->get($pid_final_raw)) {
+      return NULL;
+    }
+
+    return $value;
+  }
+
+}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/field/FieldPluginBase.php b/core/modules/migrate_drupal/src/Plugin/migrate/field/FieldPluginBase.php
index a32e08ce27..d65f79d4fb 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/field/FieldPluginBase.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/field/FieldPluginBase.php
@@ -82,9 +82,28 @@ public function processFieldWidget(MigrationInterface $migration) {
   public function alterFieldWidgetMigration(MigrationInterface $migration) {
     $process = [];
     foreach ($this->getFieldWidgetMap() as $source_widget => $destination_widget) {
-      $process['type']['map'][$source_widget] = $destination_widget;
+      $process['map'][$source_widget] = $destination_widget;
+    }
+
+    $process_plugins_all = $migration->getProcessPlugins();
+
+    // Loop on every destination property...
+    foreach ($process_plugins_all as $destination_key => $process_plugins) {
+      // ..and on their process plugins.
+      foreach ($process_plugins as $process_plugin_key => $process_plugin) {
+        if (empty($process_plugin->configuration['field_widget_map_alter']) || $process_plugin->getPluginId() !== 'static_map') {
+          continue;
+        }
+        // If this is a static map tagged with 'field_widget_map_alter', we
+        // merge the previously discovererd widget mapping.
+        // We get normalized process plugins. If the source is set in this
+        // static_map plugin, we have an (implicit) preceding get plugin, and
+        // we have to lower the destination plugin key by 1.
+        $key = isset($process_plugin->configuration['source']) && is_numeric($process_plugin_key) ?
+          $process_plugin_key - 1 : $process_plugin_key;
+        $migration->mergeProcessOfProperty($destination_key, [$key => $process]);
+      }
     }
-    $migration->mergeProcessOfProperty('options/type', $process);
   }
 
   /**
@@ -142,9 +161,25 @@ public function alterFieldFormatterMigration(MigrationInterface $migration) {
     // this prefix from the plugin ID.
     $plugin_id = preg_replace('/d[67]_/', '', $this->pluginId);
     foreach ($this->getFieldFormatterMap() as $source_format => $destination_format) {
-      $process[0]['map'][$plugin_id][$source_format] = $destination_format;
+      $process['map'][$plugin_id][$source_format] = $destination_format;
+    }
+
+    $process_plugins_all = $migration->getProcessPlugins();
+
+    // Loop on every destination property...
+    foreach ($process_plugins_all as $destination_key => $process_plugins) {
+      // ..and on their process plugins.
+      foreach ($process_plugins as $process_plugin_key => $process_plugin) {
+        if (empty($process_plugin->configuration['field_formatter_map_alter']) || $process_plugin->getPluginId() !== 'static_map') {
+          continue;
+        }
+        // If this is a static map tagged with 'field_formatter_map_alter', we
+        // merge the formatter mapping.
+        $key = isset($process_plugin->configuration['source']) ?
+          $process_plugin_key - 1 : $process_plugin_key;
+        $migration->mergeProcessOfProperty($destination_key, [$key => $process]);
+      }
     }
-    $migration->mergeProcessOfProperty('options/type', $process);
   }
 
   /**
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/FieldDiscoveryTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/FieldDiscoveryTest.php
index 83ae30e908..22ce74caa8 100644
--- a/core/modules/migrate_drupal/tests/src/Kernel/d6/FieldDiscoveryTest.php
+++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/FieldDiscoveryTest.php
@@ -122,8 +122,8 @@ public function testAddAllFieldProcesses() {
    * @covers ::addAllFieldProcesses
    * @dataProvider addAllFieldProcessesAltersData
    */
-  public function testAddAllFieldProcessesAlters($field_plugin_method, $expected_process) {
-    $this->assertFieldProcess($this->fieldDiscovery, $this->migrationPluginManager, FieldDiscoveryInterface::DRUPAL_6, $field_plugin_method, $expected_process);
+  public function testAddAllFieldProcessesAlters($field_plugin_method, $expected_process, $initial_process) {
+    $this->assertFieldProcess($this->fieldDiscovery, $this->migrationPluginManager, FieldDiscoveryInterface::DRUPAL_6, $field_plugin_method, $expected_process, NULL, NULL, $initial_process);
   }
 
   /**
@@ -139,6 +139,8 @@ public function addAllFieldProcessesAltersData() {
         'expected_process' => [
           'options/type' => [
             0 => [
+              'plugin' => 'static_map',
+              'field_formatter_map_alter' => TRUE,
               'map' => [
                 'email' => [
                   'email_formatter_default' => 'email_mailto',
@@ -180,12 +182,23 @@ public function addAllFieldProcessesAltersData() {
             ],
           ],
         ],
+        'initial_process' => [
+          'options/type' => [
+            0 => [
+              'plugin' => 'static_map',
+              'field_formatter_map_alter' => TRUE,
+              'map' => [],
+            ],
+          ],
+        ],
       ],
       'Field Widget' => [
         'field_plugin_method' => 'alterFieldWidgetMigration',
         'expected_process' => [
           'options/type' => [
-            'type' => [
+            0 => [
+              'plugin' => 'static_map',
+              'field_widget_map_alter' => TRUE,
               'map' => [
                 'userreference' => 'userreference_default',
                 'nodereference' => 'nodereference_default',
@@ -200,6 +213,15 @@ public function addAllFieldProcessesAltersData() {
             ],
           ],
         ],
+        'initial_process' => [
+          'options/type' => [
+            0 => [
+              'plugin' => 'static_map',
+              'field_widget_map_alter' => TRUE,
+              'map' => [],
+            ],
+          ],
+        ],
       ],
     ];
   }
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d7/FieldDiscoveryTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d7/FieldDiscoveryTest.php
index 092b5bf699..be80414c7e 100644
--- a/core/modules/migrate_drupal/tests/src/Kernel/d7/FieldDiscoveryTest.php
+++ b/core/modules/migrate_drupal/tests/src/Kernel/d7/FieldDiscoveryTest.php
@@ -165,8 +165,8 @@ public function testAddAllFieldProcesses() {
    * @covers ::addAllFieldProcesses
    * @dataProvider addAllFieldProcessesAltersData
    */
-  public function testAddAllFieldProcessesAlters($field_plugin_method, $expected_process) {
-    $this->assertFieldProcess($this->fieldDiscovery, $this->migrationPluginManager, FieldDiscoveryInterface::DRUPAL_7, $field_plugin_method, $expected_process);
+  public function testAddAllFieldProcessesAlters($field_plugin_method, $expected_process, $initial_process) {
+    $this->assertFieldProcess($this->fieldDiscovery, $this->migrationPluginManager, FieldDiscoveryInterface::DRUPAL_7, $field_plugin_method, $expected_process, NULL, NULL, $initial_process);
   }
 
   /**
@@ -193,12 +193,15 @@ public function addAllFieldProcessesAltersData() {
             ],
           ],
         ],
+        'initial_process' => [],
       ],
       'Field Formatter' => [
         'field_plugin_method' => 'alterFieldFormatterMigration',
         'expected_process' => [
           'options/type' => [
             0 => [
+              'plugin' => 'static_map',
+              'field_formatter_map_alter' => TRUE,
               'map' => [
                 'taxonomy_term_reference' => [
                   'taxonomy_term_reference_link' => 'entity_reference_label',
@@ -241,12 +244,23 @@ public function addAllFieldProcessesAltersData() {
             ],
           ],
         ],
+        'initial_process' => [
+          'options/type' => [
+            0 => [
+              'plugin' => 'static_map',
+              'field_formatter_map_alter' => TRUE,
+              'map' => [],
+            ],
+          ],
+        ],
       ],
       'Field Widget' => [
         'field_plugin_method' => 'alterFieldWidgetMigration',
         'expected_process' => [
           'options/type' => [
-            'type' => [
+            0 => [
+              'plugin' => 'static_map',
+              'field_widget_map_alter' => TRUE,
               'map' => [
                 'd7_text' => 'd7_text_default',
                 'number_default' => 'number_default_default',
@@ -265,6 +279,15 @@ public function addAllFieldProcessesAltersData() {
             ],
           ],
         ],
+        'initial_process' => [
+          'options/type' => [
+            0 => [
+              'plugin' => 'static_map',
+              'field_widget_map_alter' => TRUE,
+              'map' => [],
+            ],
+          ],
+        ],
       ],
     ];
   }
diff --git a/core/modules/migrate_drupal/tests/src/Traits/FieldDiscoveryTestTrait.php b/core/modules/migrate_drupal/tests/src/Traits/FieldDiscoveryTestTrait.php
index 34a16ad85e..581009950f 100644
--- a/core/modules/migrate_drupal/tests/src/Traits/FieldDiscoveryTestTrait.php
+++ b/core/modules/migrate_drupal/tests/src/Traits/FieldDiscoveryTestTrait.php
@@ -28,12 +28,17 @@ trait FieldDiscoveryTestTrait {
    *   (optional) The entity type id.
    * @param string $bundle
    *   (optional) The bundle.
+   * @param array $initial_process
+   *   (optional) The initial process.
    */
-  public function assertFieldProcess(FieldDiscoveryInterface $field_discovery, MigrationPluginManagerInterface $migration_plugin_manager, $core, $field_plugin_method = NULL, array $expected_process = [], $entity_type_id = NULL, $bundle = NULL) {
+  public function assertFieldProcess(FieldDiscoveryInterface $field_discovery, MigrationPluginManagerInterface $migration_plugin_manager, $core, $field_plugin_method = NULL, array $expected_process = [], $entity_type_id = NULL, $bundle = NULL, $initial_process = []) {
     $definition = [
       'migration_tags' => ['Drupal ' . $core],
       'field_plugin_method' => $field_plugin_method,
     ];
+    if (!empty($initial_process)) {
+      $definition['process'] = $initial_process;
+    }
     $migration = $migration_plugin_manager->createStubMigration($definition);
     if ($bundle) {
       $field_discovery->addBundleFieldProcesses($migration, $entity_type_id, $bundle);
