diff --git a/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php b/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php
index 7cd33ab..c05cfad 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,13 @@ 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 entities</a>.', [
+          '@entity_type' => $entity_type->getLabel(),
+          ':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..cf7e80f
--- /dev/null
+++ b/core/modules/system/src/Form/PrepareModulesEntityUninstallForm.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace Drupal\system\Form;
+
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\ConfirmFormHelper;
+use Drupal\Core\Url;
+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;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'system_prepare_modules_entity_uninstall';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    $entity_type = \Drupal::entityTypeManager()->getDefinition($this->entityTypeId);
+    return $this->t('Are you sure you want to delete all @entity-type entities?', array(
+      '@entity-type' => $entity_type->getLabel()
+    ));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    $entity_type = \Drupal::entityTypeManager()->getDefinition($this->entityTypeId);
+    // Display how many content entities there are to remove.
+    $count = \Drupal::entityQuery($this->entityTypeId)->count()->execute();
+    return
+      $this->formatPlural($count,
+        'There is 1 @entity_type entity to remove.',
+        'There are @count @entity_type entities to remove.',
+        [
+          '@entity_type' => $entity_type->getLabel()
+        ]
+      )
+      . ' ' .
+      $this->t('This action cannot be undone.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    $entity_type = \Drupal::entityTypeManager()->getDefinition($this->entityTypeId);
+    return $this->t('Delete all @entity_type entities', [
+      '@entity_type' => $entity_type->getLabel(),
+    ]);
+  }
+
+  /**
+   * {@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_state->set('entity_type', $entity_type_id);
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $entity_type_id = $form_state->get('entity_type');
+
+    /* @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
+    $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
+
+    $batch = [
+      'title' => t('Deleting @entity_type data', [
+        '@entity_type' => $entity_type->getLabel(),
+      ]),
+      'operations' => [
+        [
+          [__CLASS__, 'deleteContentEntities'], [$entity_type_id],
+        ],
+      ],
+      'finished' => [__CLASS__, 'moduleBatchFinished'],
+      'progress_message' => static::t('Deleting @entity_type data... Completed @percentage% (@current of @total).', [
+        '@entity_type' => $entity_type->getLabel(),
+      ]),
+    ];
+    batch_set($batch);
+  }
+
+  /**
+   * Deletes the content entities of the module.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID from which data will be deleted.
+   * @param array $context
+   */
+  public static function deleteContentEntities($entity_type_id, &$context) {
+    $entity_type_ids = \Drupal::entityQuery($entity_type_id)->range(0, 100)->execute();
+    $storage = \Drupal::entityManager()->getStorage($entity_type_id);
+    if ($entity_types = $storage->loadMultiple($entity_type_ids)) {
+      $storage->delete($entity_types);
+    }
+    $context['finished'] = (int) count($entity_type_ids) < 100;
+
+    /* @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
+    $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
+    $context['results']['entity_type'] = $entity_type->getLabel();
+  }
+
+  /**
+   * 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) {
+    drupal_set_message(t('@entity_type data has been deleted.', [
+      '@entity_type' => $results['entity_type'],
+    ]));
+    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..d92c61f
--- /dev/null
+++ b/core/modules/system/src/Tests/Module/PrepareUninstallTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\system\Tests\Module;
+
+use Drupal\Core\Url;
+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 {
+
+  /**
+   * 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 some nodes.
+    $this->drupalCreateNode(['type' => 'page']);
+    $this->drupalCreateNode(['type' => 'page']);
+    $this->drupalCreateNode(['type' => 'article']);
+    $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(t('Remove Content entities'));
+    $this->assertLinkByHref('admin/modules/uninstall/entity/node');
+
+    // Delete Node data.
+    $this->drupalGet('admin/modules/uninstall/entity/node');
+    $this->assertText(t('There are 4 Content entities to remove. This action cannot be undone.'));
+    $this->drupalPostForm(NULL, [], t('Delete all Content entities'));
+
+    // Check we are redirected to the uninstall page and data has been removed.
+    $this->assertUrl('admin/modules/uninstall', []);
+    $this->assertText(t('Content data has been deleted.'));
+
+    // Check there is no more data to be deleted, Node is ready to be disabled.
+    $this->assertText(t('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(t('The selected modules have been uninstalled.'));
+    $this->assertNoText(t('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..1531536 100644
--- a/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
+++ b/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
@@ -37,16 +37,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() {
