diff --git a/core/modules/media/config/schema/media.schema.yml b/core/modules/media/config/schema/media.schema.yml
index dad518985a..7842248d22 100644
--- a/core/modules/media/config/schema/media.schema.yml
+++ b/core/modules/media/config/schema/media.schema.yml
@@ -58,3 +58,14 @@ media.source.field_aware:
     source_field:
       type: string
       label: 'Source field'
+
+field.widget.settings.media_image:
+  type: mapping
+  label: 'Media image field display format settings'
+  mapping:
+    progress_indicator:
+      type: string
+      label: 'Progress indicator'
+    preview_image_style:
+      type: string
+      label: 'Preview image style'
diff --git a/core/modules/media/src/Plugin/Field/FieldWidget/MediaImageWidget.php b/core/modules/media/src/Plugin/Field/FieldWidget/MediaImageWidget.php
new file mode 100644
index 0000000000..a7d2beda24
--- /dev/null
+++ b/core/modules/media/src/Plugin/Field/FieldWidget/MediaImageWidget.php
@@ -0,0 +1,224 @@
+<?php
+
+namespace Drupal\media\Plugin\Field\FieldWidget;
+
+use Drupal\image\Plugin\Field\FieldWidget\ImageWidget;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\media\Entity\MediaType;
+use Drupal\media\Entity\Media;
+
+/**
+ * Plugin implementation of the 'media_image' widget.
+ *
+ * @FieldWidget(
+ *   id = "media_image",
+ *   label = @Translation("Image"),
+ *   field_types = {
+ *     "entity_reference"
+ *   }
+ * )
+ */
+class MediaImageWidget extends MediaFileWidget {
+
+  /**
+   * This class extends MediaFileWidget, but wants to act like ImageWidget in
+   * many cases (e.g. calculateDependencies(), settingsForm(), others). In order
+   * to get access to those methods, we keep this ImageWidget object handy.
+   */
+  protected $imageWidget;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings() {
+    return ImageWidget::defaultSettings();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state) {
+    $form = parent::settingsForm($form, $form_state);
+    return $this->getImageWidget()->settingsForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+    // Privilege ImageWidget::settingsSummary() over the direct parent method,
+    // because here we're trying to be most like the former.
+    return $this->getImageWidget()->settingsSummary() + parent::settingsSummary();
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Add preview and validation settings specific to this widget.
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
+    // We're relying on parent::formElement() to set file extension and file
+    // size validators.
+    $element = parent::formElement($items, $delta, $element, $form, $form_state);
+
+    // This widget provides a preview image style setting.
+    $element['#preview_image_style'] = $this->getSetting('preview_image_style');
+
+    // Drill down to the image media bundle's source field. Then figure out min
+    // and max resolution, if they're set, and ensure that they're respected.
+    $target_bundle = array_shift($this->fieldDefinition->getSetting('handler_settings')['target_bundles']);
+    /** @var \Drupal\media\Entity\MediaType $media_type */
+    $media_type = MediaType::load($target_bundle);
+    $source_machine_name = $media_type->get('source_configuration')['source_field'];
+    $bundle_fields = \Drupal::getContainer()->get('entity_field.manager')->getFieldDefinitions('media', $target_bundle);
+    /** @var \Drupal\Core\Field\BaseFieldDefinition $source_field_definition */
+    $source_field_definition = $bundle_fields[$source_machine_name];
+    $field_settings = $source_field_definition->getSettings();
+    if ($field_settings['max_resolution'] || $field_settings['min_resolution']) {
+      $element['#upload_validators']['file_validate_image_resolution'] = [$field_settings['max_resolution'], $field_settings['min_resolution']];
+    }
+
+    return $element;
+  }
+
+  /**
+   * If we have an image media entity to work with: add the alt and title fields
+   * if they are required, assign the right preview_image_style, and get the
+   * resulting image's height and width.
+   *
+   * This method is assigned as a #process callback in formElement() method.
+   */
+  public static function process($element, FormStateInterface $form_state, $form) {
+    $element = parent::process($element, $form_state, $form);
+    $mid = FALSE;
+    $show_form = FALSE;
+
+    if (!empty($element['#value']['target_id'])) {
+      $mid = $element['#value']['target_id'];
+    }
+    else if (!empty($element['#value']['mids']) && count($element['#value']['mids']) === 1) {
+      $mid = $element['#value']['mids'][0];
+
+      // Don't show any kind of form (even for required elements!) if this media
+      // has already been successfully saved via this form.
+      if (empty($element['#value']['media_already_saved'])) {
+        $show_form = TRUE;
+      }
+    }
+
+    if ($mid) {
+      $media_form_element = &$element['media_' . $mid];
+      $entity = Media::load($mid);
+      /** @var \Drupal\Core\Field\FieldDefinitionInterface $source_field_definition */
+      $source_field_definition = $entity->getSource()->getSourceFieldDefinition($entity->bundle->entity);
+      $alt_field = $source_field_definition->getSetting('alt_field') ?: FALSE;
+      $alt_field_required = $source_field_definition->getSetting('alt_field_required') ?: FALSE;
+      $title_field = $source_field_definition->getSetting('title_field') ?: FALSE;
+      $title_field_required = $source_field_definition->getSetting('title_field_required') ?: FALSE;
+
+      // Get only _some_ logic from ImageWidget::process(), which complains
+      // without some kind of values for the alt and title field keys.
+      $element['#alt_field'] = $alt_field;
+      $element['#alt_field_required'] = $alt_field_required;
+      $element['#title_field'] = $title_field;
+      $element['#title_field_required'] = $title_field_required;
+      $dummy_element = ImageWidget::process($element, $form_state, $form);
+
+      if ($show_form) {
+        // If the media source field is an image, and the alt field is both
+        // available and required, require it here too.
+        if ($alt_field && $alt_field_required) {
+          $media_form_element['alt'] = $dummy_element['alt'];
+          $media_form_element['alt']['#weight'] = -5;
+        }
+
+        // Same as above, but for title field.
+        if ($title_field && $title_field_required) {
+          $media_form_element['title'] = $dummy_element['title'];
+          $media_form_element['title']['#weight'] = -3;
+        }
+      }
+
+      // Only show an image preview if we have a style set for it.
+      if ($element['#preview_image_style']) {
+        $icon_element = $media_form_element['media_icon'];
+        $icon_element['#style_name'] = $element['#preview_image_style'];
+
+        // Let ImageWidget do the work of figuring out height and width. (It
+        // will work on the target media entity's underlying File object, since
+        // we still have that stored in $element['#files'].)
+        $icon_element['#height'] = $dummy_element['preview']['#height'];
+        $icon_element['#width'] = $dummy_element['preview']['#width'];
+      }
+    }
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function value($element, $input, FormStateInterface $form_state) {
+    if (!empty($input['mids'])) {
+      $mid = $input['mids'];
+      $media_fields_key = 'media_' . $mid;
+
+      // If the "alt" and/or "title" input values are here, it's because the
+      // site builder made them required for this media item's source field
+      // (i.e. an image field).
+      foreach (['alt', 'title'] as $extra_image_field_attribute) {
+        if (!empty($input[$media_fields_key][$extra_image_field_attribute])) {
+          $media_entity = Media::load($mid);
+          $source_field = $media_entity->getSource()
+            ->getSourceFieldDefinition($media_entity->bundle->entity)
+            ->getName();
+          $source_val = $media_entity->get($source_field)->getValue();
+          $source_val[0][$extra_image_field_attribute] = $input[$media_fields_key][$extra_image_field_attribute];
+          $media_entity->set($source_field, $source_val);
+          unset($input[$media_fields_key][$extra_image_field_attribute]);
+        }
+
+        if (isset($media_entity)) {
+          $media_entity->save();
+        }
+      }
+    }
+
+    return parent::value($element, $input, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    return $this->getImageWidget()->calculateDependencies();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onDependencyRemoval(array $dependencies) {
+    return $this->getImageWidget()->onDependencyRemoval($dependencies);
+
+  }
+
+  /**
+   * Gets this class's ImageWidget instance.
+   *
+   * @return \Drupal\image\Plugin\Field\FieldWidget\ImageWidget
+   *   An ImageWidget instance from which to call the methods this class needs.
+   */
+  protected function getImageWidget() {
+    if (empty($this->imageWidget)) {
+      $configuration = [
+        'field_definition' => $this->fieldDefinition,
+        'settings' => $this->getSettings(),
+        'third_party_settings' => $this->getThirdPartySettings(),
+      ];
+      $this->imageWidget = ImageWidget::create(\Drupal::getContainer(), $configuration, $this->getPluginId(), $this->getPluginDefinition());
+    }
+    return $this->imageWidget;
+  }
+
+}
diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaInlineImageWidgetTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaInlineImageWidgetTest.php
new file mode 100644
index 0000000000..00912a0bf9
--- /dev/null
+++ b/core/modules/media/tests/src/FunctionalJavascript/MediaInlineImageWidgetTest.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Drupal\Tests\media\FunctionalJavascript;
+
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+
+/**
+ * @group media
+ */
+class MediaInlineImageWidgetTest extends MediaJavascriptTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['node'];
+
+  /**
+   * The bundle name of the test node.
+   *
+   * @var string
+   */
+  protected $nodeTypeId;
+
+  /**
+   * The bundle name of the test media.
+   *
+   * @var string
+   */
+  protected $mediaTypeId;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // First, create the media type.
+    $bundle = 'image';
+    $media_type = $this->createMediaType(['bundle' => $bundle], 'image');
+    $this->mediaTypeId = $media_type->id();
+
+    // For the Image field, "alt required" is on by default. We'll also turn on
+    // "title" and "title required".
+    $media_type->getSource()
+      ->getSourceFieldDefinition($media_type)
+      ->getConfig($bundle)
+      ->setSetting('title_field', TRUE)
+      ->setSetting('title_field_required', TRUE)
+      ->save();
+
+    // Create the node type and add the media-reference field.
+    $node_type = $this->drupalCreateContentType();
+    $this->nodeTypeId = $node_type->id();
+    $field_storage = FieldStorageConfig::create([
+      'type' => 'entity_reference',
+      'cardinality' => -1,
+      'entity_type' => 'node',
+      'field_name' => 'field_media_reference',
+      'settings' => [
+        'target_type' => 'media',
+      ],
+    ]);
+    $field_storage->save();
+    FieldConfig::create([
+      'field_storage' => $field_storage,
+      'bundle' => $this->nodeTypeId,
+      'settings' => [
+        'handler_settings' => [
+          'target_bundles' => [
+            $this->mediaTypeId => $this->mediaTypeId,
+          ],
+        ],
+      ],
+    ])->save();
+
+    // Ensure that the field, with our widget, shows up on the default
+    // node-entry form, and also that its values appear on the default node
+    // display.
+    entity_get_form_display('node', $this->nodeTypeId, 'default')
+      ->setComponent('field_media_reference', [
+          'type' => 'media_image',
+          'settings' => [
+            'progress_indicator' => 'throbber',
+            'preview_image_syle' => 'thumbnail'
+          ]
+        ])
+      ->save();
+    entity_get_display('node', $this->nodeTypeId, 'default')
+      ->setComponent('field_media_reference', [
+        'type' => 'entity_reference_entity_view',
+        'label' => 'hidden',
+        'settings' => [
+          'view_mode' => 'full',
+        ],
+      ])->save();
+  }
+
+  /**
+   * Tests the file widget behavior.
+   */
+  public function testInlineImageWidget() {
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+
+    $this->drupalGet('/node/add/' . $this->nodeTypeId);
+
+    // Ensure the media field is present.
+    $media_field = $assert_session->elementExists('css', 'details[data-drupal-selector="edit-field-media-reference"]');
+
+    // Upload a file, and wait long enough for the form to be redrawn.
+    $upload_element_id = 'edit-field-media-reference-0-upload';
+    $image_to_upload = drupal_get_path('module', 'media') . '/tests/fixtures/example_1.jpeg';
+    $page->attachFileToField($upload_element_id, $image_to_upload);
+    $result = $assert_session->waitForButton('Remove');
+    $this->assertNotEmpty($result);
+
+    // Ensure that the image preview has appeared.
+    $assert_session->elementExists('css', 'img[data-drupal-selector$="-media-icon"]', $media_field);
+
+    // The alt field should now be present and required.
+    $assert_session->elementExists('css', 'input[data-drupal-selector$="-alt"].required', $media_field);
+
+    // The title field should also be present and required.
+    $assert_session->elementExists('css', 'input[data-drupal-selector$="-title"].required', $media_field);
+  }
+
+}
