diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php
index 6fced03dd17fe637acc3aa23deb5906bae6208ef..4f7fe4d19bf98ae4a0caa580f36aec3927a70875 100644
--- a/core/modules/views_ui/src/ViewUI.php
+++ b/core/modules/views_ui/src/ViewUI.php
@@ -13,8 +13,8 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\views\ViewExecutable;
 use Drupal\Core\Database\Database;
+use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Session\AccountInterface;
-use Drupal\views\Plugin\views\query\Sql;
 use Drupal\views\Entity\View;
 use Drupal\views\ViewEntityInterface;
 use Drupal\Core\Routing\RouteObjectInterface;
@@ -524,6 +524,55 @@ public function endQueryCapture() {
     $this->additionalQueries = $queries;
   }
 
+  /**
+   * Gets the EXPLAIN output for a query.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection.
+   * @param string|\Drupal\views\Plugin\views\query\Sql $query_string
+   *   The query string or SQL query object.
+   *
+   * @return array
+   *   Renderable array containing the explain output.
+   */
+  protected function getExplainOutput($connection, $query_string) {
+    $args = [];
+    if ($query_string instanceof SelectInterface) {
+      $args = $query_string->getArguments();
+    }
+
+    try {
+      $explain_results = $connection->query('EXPLAIN ' . $query_string, $args)->fetchAll();
+    }
+    catch (\Exception $e) {
+      return [
+        '#markup' => t('Unable to execute EXPLAIN: @message', ['@message' => $e->getMessage()]),
+      ];
+    }
+    if (empty($explain_results)) {
+      return [
+        '#markup' => t('No EXPLAIN results available.'),
+      ];
+    }
+
+    $headers = array_keys((array) $explain_results[0]);
+
+    // Format rows
+    $table_rows = [];
+    foreach ($explain_results as $row) {
+      $table_rows[] = array_values((array) $row);
+    }
+
+    return [
+      '#theme' => 'table',
+      '#header' => $headers,
+      '#rows' => $table_rows,
+      '#attributes' => [
+        'class' => ['explain-query-table'],
+      ],
+    ];
+  }
+
   public function renderPreview($display_id, $args = []) {
     // Save the current path so it can be restored before returning from this function.
     $request_stack = \Drupal::requestStack();
@@ -630,14 +679,16 @@ public function renderPreview($display_id, $args = []) {
       if ($show_info || $show_query || $show_stats) {
         // Get information from the preview for display.
         if (!empty($executable->build_info['query'])) {
+          $connection = Database::getConnection();
+
           if ($show_query) {
             $query_string = $executable->build_info['query'];
+
             // Only the sql default class has a method getArguments.
             $quoted = [];
 
-            if ($executable->query instanceof Sql) {
+            if ($query_string instanceof SelectInterface) {
               $quoted = $query_string->getArguments();
-              $connection = Database::getConnection();
               foreach ($quoted as $key => $val) {
                 if (is_array($val)) {
                   $quoted[$key] = implode(', ', array_map([$connection, 'quote'], $val));
@@ -662,6 +713,25 @@ public function renderPreview($display_id, $args = []) {
                 ],
               ],
             ];
+
+            $explain_output = $this->getExplainOutput($connection, $query_string);
+
+            $rows['query'][] = [
+              [
+                'data' => [
+                  '#type' => 'inline_template',
+                  '#template' => "<strong>{% trans 'Explain' %}</strong>",
+                ],
+              ],
+              [
+                'data' => [
+                  '#type' => 'inline_template',
+                  '#template' => '<pre>{{ explain }}</pre>',
+                  '#context' => ['explain' => $explain_output],
+                ],
+              ],
+            ];
+
             if (!empty($this->additionalQueries)) {
               $queries[] = [
                 '#prefix' => '<strong>',
diff --git a/core/modules/views_ui/tests/src/Functional/PreviewTest.php b/core/modules/views_ui/tests/src/Functional/PreviewTest.php
index e0baf9193c053916d737f7d6d287d38646c65c49..889e7058124621ebe0e1fc638f41750648a33bcb 100644
--- a/core/modules/views_ui/tests/src/Functional/PreviewTest.php
+++ b/core/modules/views_ui/tests/src/Functional/PreviewTest.php
@@ -115,6 +115,7 @@ public function testPreviewUI(): void {
     $this->assertSession()->pageTextContains('Query execute time');
     $this->assertSession()->pageTextContains('View render time');
     $this->assertSession()->responseNotContains('<strong>Query</strong>');
+    $this->assertSession()->responseNotContains('<strong>Explain</strong>');
 
     // Statistics and query.
     $settings->set('ui.show.sql_query.enabled', TRUE)->save();
@@ -130,6 +131,7 @@ public function testPreviewUI(): void {
 WHERE (views_test_data.id = '100')
 SQL;
     $this->assertSession()->assertEscaped($query_string);
+    $this->assertSession()->responseContains('<strong>Explain</strong>');
 
     // Test that the statistics and query are rendered above the preview.
     $this->assertLessThan(strpos($this->getSession()->getPage()->getContent(), 'js-view-dom-id'), strpos($this->getSession()->getPage()->getContent(), 'views-query-info'));