diff --git a/core/modules/layout_builder/layout_builder.module b/core/modules/layout_builder/layout_builder.module
index 8d19c35badc6b0d168c5651e273ab7c9038cede5..d746d343254b3611c843df51de5f37f8761a1d6f 100644
--- a/core/modules/layout_builder/layout_builder.module
+++ b/core/modules/layout_builder/layout_builder.module
@@ -147,6 +147,27 @@ function layout_builder_entity_view_alter(array &$build, EntityInterface $entity
       }
     }
   }
+
+  $route_name = \Drupal::routeMatch()->getRouteName();
+
+  // If the entity is displayed within a Layout Builder block and the current
+  // route is in the Layout Builder UI, then remove all contextual link
+  // placeholders.
+  if ($display instanceof LayoutBuilderEntityViewDisplay && strpos($route_name, 'layout_builder.') === 0) {
+    unset($build['#contextual_links']);
+  }
+}
+
+/**
+ * Implements hook_entity_build_defaults_alter().
+ */
+function layout_builder_entity_build_defaults_alter(array &$build, EntityInterface $entity, $view_mode) {
+  // Contextual links are removed for entities viewed in Layout Builder's UI.
+  // The route.name.is_layout_builder_ui cache context accounts for this
+  // difference.
+  // @see layout_builder_entity_view_alter()
+  // @see \Drupal\layout_builder\Cache\LayoutBuilderUiCacheContext
+  $build['#cache']['contexts'][] = 'route.name.is_layout_builder_ui';
 }
 
 /**
diff --git a/core/modules/layout_builder/layout_builder.services.yml b/core/modules/layout_builder/layout_builder.services.yml
index 65fb66a895c78d6f7f1f530a10a4846bc643b738..6b40cf9e3beb321e1dfaa72b25317f0253cadb28 100644
--- a/core/modules/layout_builder/layout_builder.services.yml
+++ b/core/modules/layout_builder/layout_builder.services.yml
@@ -29,6 +29,11 @@ services:
     arguments: ['@current_route_match']
     tags:
       - { name: cache.context}
+  cache_context.route.name.is_layout_builder_ui:
+    class: Drupal\layout_builder\Cache\LayoutBuilderUiCacheContext
+    arguments: ['@current_route_match']
+    tags:
+      - { name: cache.context }
   layout_builder.sample_entity_generator:
     class: Drupal\layout_builder\Entity\LayoutBuilderSampleEntityGenerator
     arguments: ['@tempstore.shared', '@entity_type.manager']
diff --git a/core/modules/layout_builder/src/Cache/LayoutBuilderUiCacheContext.php b/core/modules/layout_builder/src/Cache/LayoutBuilderUiCacheContext.php
new file mode 100644
index 0000000000000000000000000000000000000000..53b589452dcb8f924189c988a9913d4b3828a76c
--- /dev/null
+++ b/core/modules/layout_builder/src/Cache/LayoutBuilderUiCacheContext.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\layout_builder\Cache;
+
+use Drupal\Core\Cache\Context\RouteNameCacheContext;
+
+/**
+ * Determines if an entity is being viewed in the Layout Builder UI.
+ *
+ * Cache context ID: 'route.name.is_layout_builder_ui'.
+ */
+class LayoutBuilderUiCacheContext extends RouteNameCacheContext {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getLabel() {
+    return t('Layout Builder user interface');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getContext() {
+    return 'is_layout_builder_ui.' . (int) (strpos($this->routeMatch->getRouteName(), 'layout_builder.') !== 0);
+  }
+
+}
diff --git a/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php b/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php
index 795a300b088b1c5a0ceebef24d09a1bd081cdbee..da6bf6f3dc3bee76355507247c177eff56c2fb81 100644
--- a/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php
+++ b/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php
@@ -11,6 +11,7 @@
 use Drupal\layout_builder\Access\LayoutPreviewAccessAllowed;
 use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent;
 use Drupal\layout_builder\LayoutBuilderEvents;
+use Drupal\views\Plugin\Block\ViewsBlock;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -90,6 +91,15 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) {
     if ($access->isAllowed()) {
       $event->addCacheableDependency($block);
 
+      // @todo Revisit after https://www.drupal.org/node/3027653, as this will
+      //   provide a better way to remove contextual links from Views blocks.
+      //   Currently, doing this requires setting
+      //   \Drupal\views\ViewExecutable::$showAdminLinks() to false before the
+      //   Views block is built.
+      if ($block instanceof ViewsBlock && $event->inPreview()) {
+        $block->getViewExecutable()->setShowAdminLinks(FALSE);
+      }
+
       $content = $block->build();
       $is_content_empty = Element::isEmpty($content);
       $is_placeholder_ready = $event->inPreview() && $block instanceof PreviewFallbackInterface;
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_views_test/config/install/views.view.test_block_view.yml b/core/modules/layout_builder/tests/modules/layout_builder_views_test/config/install/views.view.test_block_view.yml
index c666e89d68ffd35f7728739c709784832c0df8a9..3b1e9315ff8279b141e812edefa76643deea9a77 100644
--- a/core/modules/layout_builder/tests/modules/layout_builder_views_test/config/install/views.view.test_block_view.yml
+++ b/core/modules/layout_builder/tests/modules/layout_builder_views_test/config/install/views.view.test_block_view.yml
@@ -1,6 +1,8 @@
 langcode: en
 status: true
 dependencies:
+  config:
+    - core.entity_view_mode.node.teaser
   module:
     - node
     - user
@@ -175,3 +177,30 @@ display:
         - 'user.node_grants:view'
         - user.permissions
       tags: {  }
+  block_2:
+    display_plugin: block
+    id: block_2
+    display_title: 'Teaser block'
+    position: 2
+    display_options:
+      display_extenders: {  }
+      display_description: ''
+      style:
+        type: default
+        options: {  }
+      defaults:
+        style: false
+        row: false
+      row:
+        type: 'entity:node'
+        options:
+          relationship: none
+          view_mode: teaser
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }
diff --git a/core/modules/layout_builder/tests/src/FunctionalJavascript/ContextualLinksTest.php b/core/modules/layout_builder/tests/src/FunctionalJavascript/ContextualLinksTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..db398a096f41c7d54ce337eb99d7c060293ecf68
--- /dev/null
+++ b/core/modules/layout_builder/tests/src/FunctionalJavascript/ContextualLinksTest.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace Drupal\Tests\layout_builder\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
+
+/**
+ * Test contextual links compatibility with the Layout Builder.
+ *
+ * @group layout_builder
+ */
+class ContextualLinksTest extends WebDriverTestBase {
+
+  use AssertPageCacheContextsAndTagsTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'views',
+    'views_ui',
+    'layout_builder',
+    'layout_builder_views_test',
+    'layout_test',
+    'layout_builder_test_css_transitions',
+    'block',
+    'node',
+    'contextual',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $user = $this->drupalCreateUser([
+      'configure any layout',
+      'administer node display',
+      'administer node fields',
+      'access contextual links',
+      'administer nodes',
+      'bypass node access',
+      'administer views',
+    ]);
+    $user->save();
+    $this->drupalLogin($user);
+    $this->createContentType(['type' => 'bundle_with_section_field']);
+
+    $this->createNode([
+      'type' => 'bundle_with_section_field',
+      'body' => [
+        [
+          'value' => 'The node body',
+        ],
+      ],
+    ]);
+  }
+
+  /**
+   * Tests that the contextual links inside Layout Builder are removed.
+   */
+  public function testContextualLinks() {
+    $page = $this->getSession()->getPage();
+
+    $field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
+
+    // Enable Layout Builder and overrides.
+    $this->drupalPostForm(
+      "$field_ui_prefix/display/default",
+      ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
+      'Save'
+    );
+
+    $this->drupalGet('node/1/layout');
+
+    // Add a block that includes an entity contextual link.
+    $this->addBlock('Test Block View: Teaser block');
+
+    // Add a block that includes a views contextual link.
+    $this->addBlock('Recent content');
+
+    // Ensure the contextual links are correct before the layout is saved.
+    $this->assertCorrectContextualLinksInUi();
+
+    // Ensure the contextual links are correct when the Layout Builder is loaded
+    // after being saved.
+    $page->hasButton('Save layout');
+    $page->pressButton('Save layout');
+    $this->drupalGet('node/1/layout');
+    $this->assertCorrectContextualLinksInUi();
+
+    $this->drupalGet('node/1');
+    $this->assertCorrectContextualLinksInNode();
+  }
+
+  /**
+   * Adds block to the layout via Layout Builder's UI.
+   *
+   * @param string $block_name
+   *   The block name as it appears in the Add Block form.
+   */
+  protected function addBlock($block_name) {
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+
+    $assert_session->linkExists('Add Block');
+    $page->clickLink('Add Block');
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertNotEmpty($assert_session->waitForElementVisible('css', "#drupal-off-canvas a:contains('$block_name')"));
+    $page->clickLink($block_name);
+    $this->assertNotEmpty($assert_session->waitForElementVisible('css', '[data-drupal-selector=\'edit-actions-submit\']'));
+    $page->pressButton('Add Block');
+    $this->waitForNoElement('#drupal-off-canvas');
+    $assert_session->assertWaitOnAjaxRequest();
+  }
+
+  /**
+   * Asserts the contextual links are correct in Layout Builder UI.
+   */
+  protected function assertCorrectContextualLinksInUi() {
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+    $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.block-views-blocktest-block-view-block-2'));
+
+    $layout_builder_specific_contextual_links = $page->findAll('css', '[data-contextual-id*=\'layout_builder_block:\']');
+    $this->assertNotEmpty($layout_builder_specific_contextual_links);
+
+    // Confirms Layout Builder contextual links are the only contextual links
+    // inside the Layout Builder UI.
+    $this->assertSameSize($layout_builder_specific_contextual_links, $page->findAll('css', '#layout-builder [data-contextual-id]'));
+  }
+
+  /**
+   * Asserts the contextual links are correct on the canonical entity route.
+   */
+  protected function assertCorrectContextualLinksInNode() {
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+
+    $this->assertNotEmpty($assert_session->waitForElementVisible('css', '[data-contextual-id]'));
+
+    // Ensure that no Layout Builder contextual links are visible on node view.
+    $this->assertEmpty($page->findAll('css', '[data-contextual-id*=\'layout_builder_block:\']'));
+
+    // Ensure that the contextual links that are hidden in Layout Builder UI
+    // are visible on node view.
+    $this->assertNotEmpty($page->findAll('css', '.layout-content [data-contextual-id]'));
+  }
+
+  /**
+   * Waits for an element to be removed from the page.
+   *
+   * @param string $selector
+   *   CSS selector.
+   * @param int $timeout
+   *   (optional) Timeout in milliseconds, defaults to 10000.
+   *
+   * @todo Remove in https://www.drupal.org/node/2892440.
+   */
+  protected function waitForNoElement($selector, $timeout = 10000) {
+    $condition = "(typeof jQuery !== 'undefined' && jQuery('$selector').length === 0)";
+    $this->assertJsCondition($condition, $timeout);
+  }
+
+}
diff --git a/core/modules/views/src/Plugin/Block/ViewsBlockBase.php b/core/modules/views/src/Plugin/Block/ViewsBlockBase.php
index 9ed369c045dc8a680f730bb7758f49624f0d6e10..92691c3b15b288d949087ec65b96e3476770f5f9 100644
--- a/core/modules/views/src/Plugin/Block/ViewsBlockBase.php
+++ b/core/modules/views/src/Plugin/Block/ViewsBlockBase.php
@@ -213,4 +213,18 @@ protected function addContextualLinks(&$output, $block_type = 'block') {
     }
   }
 
+  /**
+   * Gets the view executable.
+   *
+   * @return \Drupal\views\ViewExecutable
+   *   The view executable.
+   *
+   * @todo revisit after https://www.drupal.org/node/3027653. This method was
+   *   added in https://www.drupal.org/node/3002608, but should not be
+   *   necessary once block plugins can determine if they are being previewed.
+   */
+  public function getViewExecutable() {
+    return $this->view;
+  }
+
 }