diff --git a/core/lib/Drupal/Core/Entity/EntityListBuilder.php b/core/lib/Drupal/Core/Entity/EntityListBuilder.php
index 7f46915..8114712 100644
--- a/core/lib/Drupal/Core/Entity/EntityListBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityListBuilder.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Component\Serialization\Json;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -150,6 +151,13 @@ protected function getDefaultOperations(EntityInterface $entity) {
       $operations['delete'] = [
         'title' => $this->t('Delete'),
         'weight' => 100,
+        'attributes' => [
+          'class' => ['use-ajax'],
+          'data-dialog-type' => 'modal',
+          'data-dialog-options' => Json::encode([
+            'width' => 'auto',
+          ]),
+        ],
         'url' => $entity->urlInfo('delete-form'),
       ];
     }
@@ -201,6 +209,10 @@ public function buildOperations(EntityInterface $entity) {
     $build = [
       '#type' => 'operations',
       '#links' => $this->getOperations($entity),
+      // Allow links to use modals.
+      '#attached' => [
+        'library' => ['core/drupal.ajax'],
+      ],
     ];
 
     return $build;
diff --git a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
index fc92ff9..8bf3957 100644
--- a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
+++ b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
@@ -45,7 +45,7 @@ public static function buildCancelLink(ConfirmFormInterface $form, Request $requ
     return [
       '#type' => 'link',
       '#title' => $form->getCancelText(),
-      '#attributes' => ['class' => ['button']],
+      '#attributes' => ['class' => ['button', 'dialog-cancel']],
       '#url' => $url,
       '#cache' => [
         'contexts' => [
diff --git a/core/modules/config/src/Tests/ConfigEntityListTest.php b/core/modules/config/src/Tests/ConfigEntityListTest.php
index e9950ea..9adf740 100644
--- a/core/modules/config/src/Tests/ConfigEntityListTest.php
+++ b/core/modules/config/src/Tests/ConfigEntityListTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\config\Tests;
 
+use Drupal\Component\Serialization\Json;
 use Drupal\simpletest\WebTestBase;
 use Drupal\config_test\Entity\ConfigTest;
 use Drupal\Core\Entity\EntityStorageInterface;
@@ -64,6 +65,13 @@ public function testList() {
       'delete' => [
         'title' => t('Delete'),
         'weight' => 100,
+        'attributes' => [
+          'class' => ['use-ajax'],
+          'data-dialog-type' => 'modal',
+          'data-dialog-options' => Json::encode([
+            'width' => 'auto',
+          ]),
+        ],
         'url' => $entity->urlInfo('delete-form'),
       ],
     ];
@@ -134,6 +142,13 @@ public function testList() {
       'delete' => [
         'title' => t('Delete'),
         'weight' => 100,
+        'attributes' => [
+          'class' => ['use-ajax'],
+          'data-dialog-type' => 'modal',
+          'data-dialog-options' => Json::encode([
+            'width' => 'auto',
+          ]),
+        ],
         'url' => $entity->urlInfo('delete-form'),
       ],
     ];
diff --git a/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php b/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php
index 223963e..74e4e60 100644
--- a/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php
+++ b/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\views\Kernel\Plugin;
 
+use Drupal\Component\Serialization\Json;
+use Drupal\Component\Utility\Html;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\node\Entity\Node;
 use Drupal\node\Entity\NodeType;
@@ -181,7 +183,7 @@ protected function doTestRenderedOutput(AccountInterface $account, $check_cache
       $this->assertEqual($output, $expected);
       $expected = $access ? '  <div class="dropbutton-wrapper"><div class="dropbutton-widget"><ul class="dropbutton">' .
         '<li><a href="' . $node_url . '/edit?destination=/" hreflang="en">Edit</a></li>' .
-        '<li><a href="' . $node_url . '/delete?destination=/" hreflang="en">Delete</a></li>' .
+        '<li><a href="' . $node_url . '/delete?destination=/" class="use-ajax" data-dialog-type="modal" data-dialog-options="' . Html::escape(Json::encode(['width' => 'auto'])) . '" hreflang="en">Delete</a></li>' .
         '</ul></div></div>' : '';
       $output = $view->style_plugin->getField($index, 'operations');
       $this->assertEqual($output, $expected);
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Core/Entity/EntityListBuilderTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Core/Entity/EntityListBuilderTest.php
new file mode 100644
index 0000000..9548040
--- /dev/null
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Entity/EntityListBuilderTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Drupal\FunctionalJavascriptTests\Core\Entity;
+
+use Drupal\Core\Url;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+
+/**
+ * AJAX modal tests for entity list builder.
+ *
+ * @group entity
+ */
+class EntityListBuilderTest extends JavascriptTestBase {
+
+  /**
+   * A test user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $webUser;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['entity_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->webUser = $this->drupalCreateUser(['administer entity_test content']);
+    $this->drupalLogin($this->webUser);
+  }
+
+  /**
+   * Test that delete confirm forms use a modal dialog.
+   */
+  public function testDeleteConfirmForm() {
+    /** @var \Drupal\entity_test\Entity\EntityTest[] $entities */
+    $entities = [];
+    foreach (range(1, 10) as $i) {
+      $entities[$i] = EntityTest::create(['name' => 'Test entity ' . $i]);
+      $entities[$i]->save();
+    }
+
+    /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $entity_storage */
+    $entity_storage = $this->container->get('entity_type.manager')
+      ->getStorage('entity_test');
+
+    $this->assertTrue($entity_storage->load($entities[3]->id()));
+    $this->drupalGet(Url::fromRoute('entity.entity_test.collection'));
+
+    // Expand the operations for the 3rd entity.
+    $this->click('table tr:contains("Test entity 3") button .dropbutton-arrow');
+    $this->click('table tr:contains("Test entity 3") .delete a');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    // Should still be on the listing page.
+    $this->assertSession()
+      ->addressEquals(Url::fromRoute('entity.entity_test.collection')
+        ->getInternalPath());
+    // Delete the 3rd entity.
+    $this->click('.ui-dialog button:contains("' . t('Delete') . '")');
+    $entity_storage->resetCache();
+    $this->assertNull($entity_storage->load($entities[3]->id()));
+    $this->assertSession()
+      ->pageTextContains(t('The test entity Test entity 3 has been deleted.'));
+  }
+
+}
\ No newline at end of file
