diff --git a/core/modules/toolbar/tests/src/Functional/ToolbarCacheContextsTest.php b/core/modules/toolbar/tests/src/Functional/ToolbarCacheContextsTest.php
index 26454762ea284310d0b502b527a607f10562468d..0232545473217c8d7fa4f5c3f34db24161b86fac 100644
--- a/core/modules/toolbar/tests/src/Functional/ToolbarCacheContextsTest.php
+++ b/core/modules/toolbar/tests/src/Functional/ToolbarCacheContextsTest.php
@@ -58,6 +58,18 @@ protected function setUp() {
     $this->adminUser2 = $this->drupalCreateUser($this->perms);
   }
 
+  /**
+   * Tests toolbar cache integration.
+   */
+  public function testCacheIntegration() {
+    $this->installExtraModules(['dynamic_page_cache']);
+    $this->drupalLogin($this->adminUser);
+    $this->drupalGet('test-page');
+    $this->assertSame('MISS', $this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache'));
+    $this->drupalGet('test-page');
+    $this->assertSame('HIT', $this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache'));
+  }
+
   /**
    * Tests toolbar cache contexts.
    */
diff --git a/core/modules/user/src/ToolbarLinkBuilder.php b/core/modules/user/src/ToolbarLinkBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f7dedd53c4f60b750e36f65d39b2a2ee63b3965
--- /dev/null
+++ b/core/modules/user/src/ToolbarLinkBuilder.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\user;
+
+use Drupal\Core\Session\AccountProxyInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Url;
+
+/**
+ * ToolbarLinkBuilder fills out the placeholders generated in user_toolbar().
+ */
+class ToolbarLinkBuilder {
+
+  use StringTranslationTrait;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountProxyInterface
+   */
+  protected $account;
+
+  /**
+   * ToolbarHandler constructor.
+   *
+   * @param \Drupal\Core\Session\AccountProxyInterface $account
+   *   The current user.
+   */
+  public function __construct(AccountProxyInterface $account) {
+    $this->account = $account;
+  }
+
+  /**
+   * Lazy builder callback for rendering toolbar links.
+   *
+   * @return array
+   *   A renderable array as expected by the renderer service.
+   */
+  public function renderToolbarLinks() {
+    $links = [
+      'account' => [
+        'title' => $this->t('View profile'),
+        'url' => Url::fromRoute('user.page'),
+        'attributes' => [
+          'title' => $this->t('User account'),
+        ],
+      ],
+      'account_edit' => [
+        'title' => $this->t('Edit profile'),
+        'url' => Url::fromRoute('entity.user.edit_form', ['user' => $this->account->id()]),
+        'attributes' => [
+          'title' => $this->t('Edit user account'),
+        ],
+      ],
+      'logout' => [
+        'title' => $this->t('Log out'),
+        'url' => Url::fromRoute('user.logout'),
+      ],
+    ];
+    $build = [
+      '#theme' => 'links__toolbar_user',
+      '#links' => $links,
+      '#attributes' => [
+        'class' => ['toolbar-menu'],
+      ],
+      '#cache' => [
+        'contexts' => ['user'],
+      ],
+    ];
+
+    return $build;
+  }
+
+  /**
+   * Lazy builder callback for rendering the username.
+   *
+   * @return array
+   *   A renderable array as expected by the renderer service.
+   */
+  public function renderDisplayName() {
+    return [
+      '#markup' => $this->account->getDisplayName(),
+    ];
+  }
+
+}
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 2d7d24a006386d024b3c1aba91975cb98e846dd2..cabe189e5a744ba1843cd8031245a866d86b8ea6 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -9,7 +9,6 @@
 use Drupal\Component\Render\PlainTextOutput;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Asset\AttachedAssetsInterface;
-use Drupal\Core\Cache\Cache;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Render\Element;
@@ -1328,41 +1327,6 @@ function user_cookie_delete($cookie_name) {
 function user_toolbar() {
   $user = \Drupal::currentUser();
 
-  // Add logout & user account links or login link.
-  $links_cache_contexts = [];
-  if ($user->isAuthenticated()) {
-    $links = [
-      'account' => [
-        'title' => t('View profile'),
-        'url' => Url::fromRoute('user.page'),
-        'attributes' => [
-          'title' => t('User account'),
-        ],
-      ],
-      'account_edit' => [
-        'title' => t('Edit profile'),
-        'url' => Url::fromRoute('entity.user.edit_form', ['user' => $user->id()]),
-        'attributes' => [
-          'title' => t('Edit user account'),
-        ],
-      ],
-      'logout' => [
-        'title' => t('Log out'),
-        'url' => Url::fromRoute('user.logout'),
-      ],
-    ];
-    // The "Edit user account" link is per-user.
-    $links_cache_contexts[] = 'user';
-  }
-  else {
-    $links = [
-      'login' => [
-        'title' => t('Log in'),
-        'url' => Url::fromRoute('user.page'),
-      ],
-    ];
-  }
-
   $items['user'] = [
     '#type' => 'toolbar_item',
     'tab' => [
@@ -1374,26 +1338,12 @@ function user_toolbar() {
         'class' => ['toolbar-icon', 'toolbar-icon-user'],
       ],
       '#cache' => [
-        'contexts' => [
-          // Cacheable per user, because the current user's name is shown.
-          'user',
-        ],
+        // Vary cache for anonymous and authenticated users.
+        'contexts' => ['user.roles:anonymous'],
       ],
     ],
     'tray' => [
       '#heading' => t('User account actions'),
-      'user_links' => [
-        '#cache' => [
-          // Cacheable per "authenticated or not", because the links to
-          // display depend on that.
-          'contexts' => Cache::mergeContexts(['user.roles:authenticated'], $links_cache_contexts),
-        ],
-        '#theme' => 'links__toolbar_user',
-        '#links' => $links,
-        '#attributes' => [
-          'class' => ['toolbar-menu'],
-        ],
-      ],
     ],
     '#weight' => 100,
     '#attached' => [
@@ -1403,6 +1353,32 @@ function user_toolbar() {
     ],
   ];
 
+  if ($user->isAnonymous()) {
+    $links = [
+      'login' => [
+        'title' => t('Log in'),
+        'url' => Url::fromRoute('user.page'),
+      ],
+    ];
+    $items['user']['tray']['user_links'] = [
+      '#theme' => 'links__toolbar_user',
+      '#links' => $links,
+      '#attributes' => [
+        'class' => ['toolbar-menu'],
+      ],
+    ];
+  }
+  else {
+    $items['user']['tab']['#title'] = [
+      '#lazy_builder' => ['user.toolbar_link_builder:renderDisplayName', []],
+      '#create_placeholder' => TRUE,
+    ];
+    $items['user']['tray']['user_links'] = [
+      '#lazy_builder' => ['user.toolbar_link_builder:renderToolbarLinks', []],
+      '#create_placeholder' => TRUE,
+    ];
+  }
+
   return $items;
 }
 
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index aede2f462ba695e80750c320cfa1e326834f0ed0..058daa03bb22056195d1978e63b65d0debe6332b 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -68,3 +68,6 @@ services:
     arguments: ['@current_user', '@entity.manager']
     tags:
       - { name: 'context_provider' }
+  user.toolbar_link_builder:
+    class: Drupal\user\ToolbarLinkBuilder
+    arguments: ['@current_user']