diff --git a/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php b/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php
index 7cd33ab..9cd3f7a 100644
--- a/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php
+++ b/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Url;
 
 /**
  * Validates module uninstall readiness based on existing content entities.
@@ -37,10 +38,14 @@ public function __construct(EntityManagerInterface $entity_manager, TranslationI
    */
   public function validate($module) {
     $entity_types = $this->entityManager->getDefinitions();
-    $reasons = array();
+    $reasons = [];
     foreach ($entity_types as $entity_type) {
       if ($module == $entity_type->getProvider() && $entity_type instanceof ContentEntityTypeInterface && $this->entityManager->getStorage($entity_type->id())->hasData()) {
-        $reasons[] = $this->t('There is content for the entity type: @entity_type', array('@entity_type' => $entity_type->getLabel()));
+        $reasons[] = $this->t('There is content for the entity type: @entity_type. <a href=":link">Remove @entity_type_plural</a>.', [
+          '@entity_type' => $entity_type->getLabel(),
+          '@entity_type_plural' => $entity_type->getPluralLabel(),
+          ':link' => Url::fromRoute('system.prepare_modules_entity_uninstall', ['entity_type_id' => $entity_type->id()])->toString(),
+        ]);
       }
     }
     return $reasons;
diff --git a/core/modules/system/src/Form/PrepareModulesEntityUninstallForm.php b/core/modules/system/src/Form/PrepareModulesEntityUninstallForm.php
new file mode 100644
index 0000000..ed17c8c
--- /dev/null
+++ b/core/modules/system/src/Form/PrepareModulesEntityUninstallForm.php
@@ -0,0 +1,231 @@
+<?php
+
+namespace Drupal\system\Form;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * Provides a form removing module content entities data before uninstallation.
+ */
+class PrepareModulesEntityUninstallForm extends ConfirmFormBase {
+
+  /**
+   * The entity type ID of the entities to delete.
+   *
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a PrepareModulesEntityUninstallForm object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'system_prepare_modules_entity_uninstall';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    $entity_type = $this->entityTypeManager->getDefinition($this->entityTypeId);
+
+    return $this->t('Are you sure you want to delete all @entity_type_plural?', ['@entity_type_plural' => $entity_type->getPluralLabel()]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return $this->t('This action cannot be undone.<br />Make a backup of your database if you want to be able to restore these items.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    $entity_type = $this->entityTypeManager->getDefinition($this->entityTypeId);
+
+    return $this->t('Delete all @entity_type_plural', ['@entity_type_plural' => $entity_type->getPluralLabel()]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return Url::fromRoute('system.modules_uninstall');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) {
+    $this->entityTypeId = $entity_type_id;
+    $form = parent::buildForm($form, $form_state);
+
+    $storage = $this->entityTypeManager->getStorage($entity_type_id);
+    $count = $storage->getQuery()->count()->execute();
+
+    $form['entity_type_id'] = [
+      '#type' => 'value',
+      '#value' => $entity_type_id,
+    ];
+
+    // Display a list of the 10 entity labels, if possible.
+    $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
+    if ($entity_type->hasKey('label')) {
+      $recent_entity_ids = $storage->getQuery()
+        ->sort($entity_type->getKey('id'), 'DESC')
+        ->pager(10)
+        ->execute();
+      $recent_entities = $storage->loadMultiple($recent_entity_ids);
+
+      $labels = [];
+      foreach ($recent_entities as $entity) {
+        $labels[] = $entity->label();
+      }
+
+      if ($labels) {
+        $form['recent_entity_labels'] = [
+          '#theme' => 'item_list',
+          '#items' => $labels,
+        ];
+        $form['total'] = [
+          '#markup' => $this->t('And <strong>@count</strong> more @entity_type_plural.', ['@count' => $count - count($labels), '@entity_type_plural' => $entity_type->getPluralLabel()]),
+          '#access' => $count > count($labels),
+        ];
+      }
+    }
+
+    $form['description']['#prefix'] = '<p>';
+    $form['description']['#suffix'] = '</p>';
+    $form['description']['#weight'] = 5;
+
+    $form['advanced_options'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Advanced options'),
+      '#weight' => 10,
+      '#tree' => FALSE,
+    ];
+
+    $form['advanced_options']['limit'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Limit'),
+      '#description' => $this->t('Select how many items should be deleted at once in a single batch run.'),
+      '#default_value' => ($count < 100) ? $count : 100,
+    ];
+
+    // Make "Cancel" the default button, "Delete" a secondary red link.
+    $form['actions']['cancel']['#weight'] = -1;
+    $form['actions']['submit']['#button_type'] = 'danger';
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $entity_type_id = $form_state->getValue('entity_type_id');
+    $limit = $form_state->getValue('limit');
+
+    $entity_type_plural = $this->entityTypeManager->getDefinition($entity_type_id)->getPluralLabel();
+    $batch = [
+      'title' => t('Deleting @entity_type_plural', [
+        '@entity_type_plural' => $entity_type_plural,
+      ]),
+      'operations' => [
+        [
+          [__CLASS__, 'deleteContentEntities'], [$entity_type_id, $limit],
+        ],
+      ],
+      'finished' => [__CLASS__, 'moduleBatchFinished'],
+      'progress_message' => '',
+    ];
+    batch_set($batch);
+  }
+
+  /**
+   * Deletes the content entities of the specified entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID from which data will be deleted.
+   * @param int $limit
+   *   How many items to remove in a batch run.
+   * @param array|\ArrayAccess $context
+   *   The batch context array, passed by reference.
+   */
+  public static function deleteContentEntities($entity_type_id, $limit, &$context) {
+    $storage = \Drupal::entityTypeManager()->getStorage($entity_type_id);
+
+    // Set the entity type ID in the results array so we can acess it in the
+    // batch finished callback.
+    $context['results']['entity_type_id'] = $entity_type_id;
+
+    if (!isset($context['sandbox']['progress'])) {
+      $context['sandbox']['progress'] = 0;
+      $context['sandbox']['max'] = \Drupal::entityQuery($entity_type_id)->count()->execute();
+    }
+
+    $entity_ids = $storage->getQuery()->range(0, $limit)->execute();
+    if ($entity_types = $storage->loadMultiple($entity_ids)) {
+      $storage->delete($entity_types);
+    }
+    $context['sandbox']['progress'] += count($entity_ids);
+
+    // Inform the batch engine that we are not finished,
+    // and provide an estimation of the completion level we reached.
+    if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+      $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+      $context['message'] = t('Deleting items... Completed @percentage% (@current of @total).', ['@percentage' => round(100 * $context['sandbox']['progress'] / $context['sandbox']['max']), '@current' => $context['sandbox']['progress'], '@total' => $context['sandbox']['max']]);
+
+    }
+    else {
+      $context['finished'] = 1;
+    }
+  }
+
+  /**
+   * Implements callback_batch_finished().
+   *
+   * Finishes the module batch, redirect to the uninstall page and output the
+   * successful data deletion message.
+   */
+  public static function moduleBatchFinished($success, $results, $operations) {
+    $entity_type_plural = \Drupal::entityTypeManager()->getDefinition($results['entity_type_id'])->getPluralLabel();
+    drupal_set_message(t('All @entity_type_plural have been deleted.', ['@entity_type_plural' => $entity_type_plural]));
+
+    return new RedirectResponse(Url::fromRoute('system.modules_uninstall')->setAbsolute()->toString());
+  }
+
+}
diff --git a/core/modules/system/src/Tests/Module/PrepareUninstallTest.php b/core/modules/system/src/Tests/Module/PrepareUninstallTest.php
new file mode 100644
index 0000000..d59673c
--- /dev/null
+++ b/core/modules/system/src/Tests/Module/PrepareUninstallTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\system\Tests\Module;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests that modules can be uninstalled.
+ *
+ * Here we take the Node module as example to test the prepare uninstallation.
+ *
+ * @group Module
+ */
+class PrepareUninstallTest extends WebTestBase {
+
+  /**
+   * An array of node objects.
+   *
+   * @var \Drupal\node\NodeInterface[]
+   */
+  protected $nodes;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['node'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $admin_user = $this->drupalCreateUser(['administer modules']);
+    $this->drupalLogin($admin_user);
+
+    // Create 12 nodes.
+    for ($i = 1; $i <= 6; $i++) {
+      $this->nodes[] = $this->drupalCreateNode(['type' => 'page']);
+      $this->nodes[] = $this->drupalCreateNode(['type' => 'article']);
+    }
+  }
+
+  /**
+   * Tests that Node module can be uninstalled.
+   */
+  public function testUninstall() {
+    // Check Node cannot be uninstalled yet, there is content to be removed.
+    $this->drupalGet('admin/modules/uninstall');
+    $this->assertText('Remove content items');
+    $this->assertLinkByHref('admin/modules/uninstall/entity/node');
+
+    // Delete Node data.
+    $this->drupalGet('admin/modules/uninstall/entity/node');
+    for ($i = 11; $i >= 2; $i--) {
+      $this->assertText($this->nodes[$i]->label());
+    }
+    $this->assertText('And 2 more content items.');
+    $this->assertText('This action cannot be undone.');
+    $this->assertText('Make a backup of your database if you want to be able to restore these items.');
+    $this->drupalPostForm(NULL, ['limit' => 1], t('Delete all content items'));
+
+    // Check we are redirected to the uninstall page and data has been removed.
+    $this->assertUrl('admin/modules/uninstall', []);
+    $this->assertText('All content items have been deleted.');
+
+    // Check there is no more data to be deleted, Node is ready to be disabled.
+    $this->assertText('Allows content to be submitted to the site and displayed on pages.');
+    $this->assertNoLinkByHref('admin/modules/uninstall/entity/node');
+
+    // Uninstall Node module.
+    $this->drupalPostForm('admin/modules/uninstall', ['uninstall[node]' => TRUE], t('Uninstall'));
+    $this->drupalPostForm(NULL, [], t('Uninstall'));
+    $this->assertText('The selected modules have been uninstalled.');
+    $this->assertNoText('Allows content to be submitted to the site and displayed on pages.');
+  }
+
+}
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 547b083..6927d6b 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -423,6 +423,14 @@ system.modules_uninstall_confirm:
   requirements:
     _permission: 'administer modules'
 
+system.prepare_modules_entity_uninstall:
+  path: '/admin/modules/uninstall/entity/{entity_type_id}'
+  defaults:
+    _form: '\Drupal\system\Form\PrepareModulesEntityUninstallForm'
+    _title_callback: '\Drupal\system\Form\PrepareModulesEntityUninstallForm::formTitle'
+  requirements:
+    _permission: 'administer modules'
+
 system.timezone:
   path: '/system/timezone/{abbreviation}/{offset}/{is_daylight_saving_time}'
   defaults:
diff --git a/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php b/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
index 9beac7a..ff400e1 100644
--- a/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
+++ b/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
@@ -3,7 +3,6 @@
 namespace Drupal\Tests\system\Kernel\Extension;
 
 use Drupal\Core\Extension\MissingDependencyException;
-use Drupal\Core\DependencyInjection\ContainerBuilder;
 use \Drupal\Core\Extension\ModuleUninstallValidatorException;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\KernelTests\KernelTestBase;
@@ -37,16 +36,6 @@ protected function setUp() {
   }
 
   /**
-   * {@inheritdoc}
-   */
-  public function register(ContainerBuilder $container) {
-    parent::register($container);
-    // Put a fake route bumper on the container to be called during uninstall.
-    $container
-      ->register('router.dumper', 'Drupal\Core\Routing\NullMatcherDumper');
-  }
-
-  /**
    * The basic functionality of retrieving enabled modules.
    */
   function testModuleList() {
