diff --git a/core/modules/contextual/js/contextual.js b/core/modules/contextual/js/contextual.js
index 7f93a15f4d..5658303b6d 100644
--- a/core/modules/contextual/js/contextual.js
+++ b/core/modules/contextual/js/contextual.js
@@ -163,7 +163,7 @@
       // Update all contextual links placeholders whose HTML is cached.
       var uncachedIDs = _.filter(ids, function initIfCached(contextualID) {
         var html = storage.getItem('Drupal.contextual.' + contextualID);
-        if (html !== null) {
+        if (html && html.length) {
           // Initialize after the current execution cycle, to make the AJAX
           // request for retrieving the uncached contextual links as soon as
           // possible, but also to ensure that other Drupal behaviors have had
@@ -192,7 +192,7 @@
               // If the rendered contextual links are empty, then the current
               // user does not have permission to access the associated links:
               // don't render anything.
-              if (html.length > 0) {
+              if (html && html.length) {
                 // Update the placeholders to contain its rendered contextual
                 // links. Usually there will only be one placeholder, but it's
                 // possible for multiple identical placeholders exist on the
diff --git a/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php b/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php
new file mode 100644
index 0000000000..322da68a10
--- /dev/null
+++ b/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\Tests\contextual\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\user\Entity\Role;
+use Drupal\user\RoleInterface;
+
+/**
+ * Tests the UI for correct contextual links.
+ *
+ * @group contextual
+ */
+class ContextualLinksTest extends JavascriptTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['block', 'contextual'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->placeBlock('system_branding_block', ['id' => 'branding']);
+  }
+
+  /**
+   * Tests the visibility of contextual links.
+   */
+  public function testContextualLinksVisibility() {
+    $this->drupalLogin($this->drupalCreateUser([
+      'access contextual links'
+    ]));
+
+    $this->drupalGet('user');
+    $this->assertSession()->elementNotExists('css', '.contextual button');
+
+    // Ensure visibility remains correct after cached paged load.
+    $this->drupalGet('user');
+    $this->assertSession()->elementNotExists('css', '.contextual button');
+
+    // Grant Permissions
+    $this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), [
+      'access contextual links',
+      'administer blocks',
+    ]);
+
+    $this->drupalGet('user');
+    $this->assertSession()->waitForElement('css', '.contextual button');
+
+    // Ensure visibility remains correct after cached paged load.
+    $this->drupalGet('user');
+    $this->assertSession()->waitForElement('css', '.contextual button');
+  }
+}
