diff --git a/core/lib/Drupal/Core/Render/Element/Link.php b/core/lib/Drupal/Core/Render/Element/Link.php
index e93deb23d7f8b8bcd5e61bbc27c024a9edd4f6a0..2a00e0526b6872640f18057a2e67585e00298df5 100644
--- a/core/lib/Drupal/Core/Render/Element/Link.php
+++ b/core/lib/Drupal/Core/Render/Element/Link.php
@@ -68,9 +68,12 @@ public static function preRenderLink($element) {
     $element += ['#options' => []];
     // However, within the scope of renderable elements, #attributes is a valid
     // way to specify attributes, too. Take them into account, but do not
-    // override attributes from #options.
+    // override attributes from #options. Merge class as a string or array.
     if (isset($element['#attributes'])) {
       $element['#options'] += ['attributes' => []];
+      $element_class = $element['#attributes']['class'] ?? [];
+      $option_class = $element['#options']['attributes']['class'] ?? [];
+      $element['#options']['attributes']['class'] = array_merge((array) $option_class, (array) $element_class);
       $element['#options']['attributes'] += $element['#attributes'];
     }
 
diff --git a/core/tests/Drupal/KernelTests/Core/Render/Element/RenderElementTypesTest.php b/core/tests/Drupal/KernelTests/Core/Render/Element/RenderElementTypesTest.php
index b381db560e9a8d7dfa1eb4a343cbf66fb94a1879..0af94a8d8dc34f39c8a5ed73d4c1e2e6bc79cb84 100644
--- a/core/tests/Drupal/KernelTests/Core/Render/Element/RenderElementTypesTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Render/Element/RenderElementTypesTest.php
@@ -237,4 +237,99 @@ public function testSystemCompactLink(): void {
     $this->assertNotEmpty($result, '"' . $element['name'] . '" is rendered correctly.');
   }
 
+  /**
+   * Tests system #type 'link'.
+   */
+  public function testLink(): void {
+    $elements = [
+      [
+        'name' => "#type 'link' simple anchor tag",
+        'value' => [
+          '#type' => 'link',
+          '#title' => 'title',
+          '#url' => Url::fromUri('https://www.drupal.org'),
+        ],
+        'expected' => '//a[@href="https://www.drupal.org" and text()="title"]',
+      ],
+      [
+        'name' => "#type 'link' anchor tag with extra classes in ['#attributes']",
+        'value' => [
+          '#type' => 'link',
+          '#title' => 'title',
+          '#url' => Url::fromUri('https://www.drupal.org'),
+          '#attributes' => [
+            'class' => ['attributes-class'],
+          ],
+          '#options' => [],
+        ],
+        'expected' => '//a[@href="https://www.drupal.org" and @class="attributes-class" and text()="title"]',
+      ],
+      [
+        'name' => "#type 'link' anchor tag with extra classes in ['#options']['attributes']",
+        'value' => [
+          '#type' => 'link',
+          '#title' => 'title',
+          '#url' => Url::fromUri('https://www.drupal.org'),
+          '#attributes' => [],
+          '#options' => [
+            'attributes' => [
+              'class' => ['options-attributes-class'],
+            ],
+          ],
+        ],
+        'expected' => '//a[@href="https://www.drupal.org" and @class="options-attributes-class" and text()="title"]',
+      ],
+      [
+        'name' => "#type 'link' anchor tag with extra classes in both ['#attributes'] and ['#options']['attributes']",
+        'value' => [
+          '#type' => 'link',
+          '#title' => 'title',
+          '#url' => Url::fromUri('https://www.drupal.org'),
+          '#attributes' => [
+            'class' => ['attributes-class'],
+          ],
+          '#options' => [
+            'attributes' => [
+              'class' => ['options-attributes-class'],
+            ],
+          ],
+        ],
+        'expected' => '//a[@href="https://www.drupal.org" and @class="options-attributes-class attributes-class" and text()="title"]',
+      ],
+      [
+        'name' => "#type 'link' anchor tag with extra classes in both ['#attributes'] and ['#options']['attributes'] as strings",
+        'value' => [
+          '#type' => 'link',
+          '#title' => 'title',
+          '#url' => Url::fromUri('https://www.drupal.org'),
+          '#attributes' => [
+            'class' => 'attributes-class',
+          ],
+          '#options' => [
+            'attributes' => [
+              'class' => 'options-attributes-class',
+            ],
+          ],
+        ],
+        'expected' => '//a[@href="https://www.drupal.org" and contains(@class,"options-attributes-class") and contains(@class,"attributes-class") and text()="title"]',
+      ],
+      [
+        'name' => "#type 'link' anchor tag with extra classes in Url object ['#options']['attributes'] which are ignored",
+        'value' => [
+          '#type' => 'link',
+          '#title' => 'title',
+          '#url' => Url::fromUri('https://www.drupal.org')->setOption('attributes', ['class' => 'url-options-attributes-class']),
+          '#attributes' => [],
+          '#options' => [],
+        ],
+        'expected' => '//a[@href="https://www.drupal.org" and not(@class) and text()="title"]',
+      ],
+    ];
+    foreach ($elements as $element) {
+      $xml = new \SimpleXMLElement((string) \Drupal::service('renderer')->renderRoot($element['value']));
+      $result = $xml->xpath($element['expected']);
+      $this->assertNotEmpty($result, '"' . $element['name'] . '" input rendered correctly.');
+    }
+  }
+
 }