diff --git a/core/modules/system/tests/modules/js_message_test/js_message_test.routing.yml b/core/modules/system/tests/modules/js_message_test/js_message_test.routing.yml
index 4c325e84611ec4018ee5c5eb8dbc507132d304f2..a3e5f44d009e7b6ddc7da02e4e09ae1c1b22ae74 100644
--- a/core/modules/system/tests/modules/js_message_test/js_message_test.routing.yml
+++ b/core/modules/system/tests/modules/js_message_test/js_message_test.routing.yml
@@ -5,3 +5,11 @@ js_message_test.links:
     _title: 'JsMessageLinks'
   requirements:
     _access: 'TRUE'
+
+js_message_test.links_with_system_messages:
+  path: '/js_message_test_link_with_system_messages'
+  defaults:
+    _controller: '\Drupal\js_message_test\Controller\JSMessageTestController::messageLinksWithSystemMessages'
+    _title: 'JsMessageLinks'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/system/tests/modules/js_message_test/src/Controller/JSMessageTestController.php b/core/modules/system/tests/modules/js_message_test/src/Controller/JSMessageTestController.php
index c6cd8b4670f69a0e6f468676668cb7cedb76d9ae..b242656daeba2394000d2ca969c62710a98e0ba8 100644
--- a/core/modules/system/tests/modules/js_message_test/src/Controller/JSMessageTestController.php
+++ b/core/modules/system/tests/modules/js_message_test/src/Controller/JSMessageTestController.php
@@ -29,6 +29,22 @@ public static function getMessagesSelectors() {
     return ['', '[data-drupal-messages-other]'];
   }
 
+  /**
+   * Displays links to show messages via JavaScript with messages from backend.
+   *
+   * @return array
+   *   Render array for links.
+   */
+  public function messageLinksWithSystemMessages() {
+    // Add PHP rendered messages to the page.
+    $messenger = \Drupal::messenger();
+    $messenger->addStatus('PHP Status');
+    $messenger->addWarning('PHP Warning');
+    $messenger->addError('PHP Error');
+
+    return $this->messageLinks();
+  }
+
   /**
    * Displays links to show messages via JavaScript.
    *
diff --git a/core/modules/system/tests/modules/olivero_test/olivero_test.module b/core/modules/system/tests/modules/olivero_test/olivero_test.module
index 2f6ccb4420315e8de5198df323f8a6c26a4bda36..5c2c0c4a44e727c60c28c5b882dcfa71c48bd37b 100644
--- a/core/modules/system/tests/modules/olivero_test/olivero_test.module
+++ b/core/modules/system/tests/modules/olivero_test/olivero_test.module
@@ -15,25 +15,6 @@ function olivero_test_preprocess_field_multiple_value_form(&$variables) {
   }
 }
 
-/**
- * Implements hook_preprocess().
- */
-function olivero_test_preprocess_page(&$variables) {
-  $route = \Drupal::routeMatch()->getRouteName();
-
-  switch ($route) {
-    case 'js_message_test.links':
-      $messenger = \Drupal::messenger();
-      $messenger->addStatus('PHP Status');
-      $messenger->addWarning('PHP Warning');
-      $messenger->addError('PHP Error');
-      break;
-
-    default:
-      break;
-  }
-}
-
 /**
  * Implements hook_preprocess_html().
  */
diff --git a/core/profiles/nightwatch_testing/nightwatch_testing.info.yml b/core/profiles/nightwatch_testing/nightwatch_testing.info.yml
index 6e3f05ad741c1ae080df3a99375f692d3e95cb91..c13d545635bacfdc90f51d9815637fd7d275cfcc 100644
--- a/core/profiles/nightwatch_testing/nightwatch_testing.info.yml
+++ b/core/profiles/nightwatch_testing/nightwatch_testing.info.yml
@@ -5,3 +5,4 @@ version: VERSION
 hidden: true
 install:
   - js_testing_log_test
+  - nightwatch_theme_install_utility
diff --git a/core/tests/Drupal/Nightwatch/Tests/Claro/claroDrupalMessageTest.js b/core/tests/Drupal/Nightwatch/Tests/Claro/claroDrupalMessageTest.js
new file mode 100644
index 0000000000000000000000000000000000000000..e4366650142f32bb7b2ec0a2c0ec34ccef4e1860
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/Tests/Claro/claroDrupalMessageTest.js
@@ -0,0 +1,101 @@
+const mainContent = '.region-content';
+const mainMessagesContainer =
+  '[data-drupal-messages] > .messages-list__wrapper';
+const secondaryMessagesContainer = '[data-drupal-messages-other]';
+
+const mainButtons = {
+  addStatus: '#add--status',
+  removeStatus: '#remove--status',
+  addError: '#add--error',
+  removeError: '#remove--error',
+  addWarning: '#add--warning',
+  removeWarning: '#remove--warning',
+  clearAll: '#clear-all',
+};
+
+const secondaryButtons = {
+  addStatus: '[id="add-[data-drupal-messages-other]-status"]',
+  removeStatus: '[id="remove-[data-drupal-messages-other]-status"]',
+  addError: '[id="add-[data-drupal-messages-other]-error"]',
+  removeError: '[id="remove-[data-drupal-messages-other]-error"]',
+  addWarning: '[id="add-[data-drupal-messages-other]-warning"]',
+  removeWarning: '[id="remove-[data-drupal-messages-other]-warning"]',
+};
+
+module.exports = {
+  '@tags': ['core', 'claro'],
+  before(browser) {
+    browser
+      .drupalInstall()
+      .drupalInstallModule('js_message_test')
+      .drupalEnableTheme('claro');
+  },
+  after(browser) {
+    browser.drupalUninstall();
+  },
+  'Verify default placement of javascript-created messages': (browser) => {
+    browser
+      .drupalRelativeURL('/js_message_test_link_with_system_messages')
+      .waitForElementVisible(mainContent)
+      .assert.elementPresent(mainMessagesContainer)
+
+      // We should load 3 messages on page load from \Drupal::messenger()
+      .assert.elementCount(`${mainMessagesContainer} > .messages-list__item`, 3)
+
+      // We should have one message of each type
+      .assert.elementCount(`${mainMessagesContainer} > .messages--status`, 1)
+      .assert.elementCount(`${mainMessagesContainer} > .messages--warning`, 1)
+      .assert.elementCount(`${mainMessagesContainer} > .messages--error`, 1)
+
+      // Trigger new messages via javascript
+      .click(mainButtons.addStatus)
+      .click(mainButtons.addWarning)
+      .click(mainButtons.addError)
+
+      // We should have 6 total messages
+      .assert.elementCount(`${mainMessagesContainer} > .messages-list__item`, 6)
+
+      // We should have 2 messages of each type
+      .assert.elementCount(`${mainMessagesContainer} > .messages--status`, 2)
+      .assert.elementCount(`${mainMessagesContainer} > .messages--warning`, 2)
+      .assert.elementCount(`${mainMessagesContainer} > .messages--error`, 2);
+  },
+
+  'Verify customized placement of javascript-created messages': (browser) => {
+    browser
+      .drupalRelativeURL('/js_message_test_link_with_system_messages')
+      .waitForElementVisible(mainContent)
+      .assert.elementPresent(secondaryMessagesContainer)
+
+      // We should load 3 messages on page load from \Drupal::messenger()
+      .assert.elementCount(
+        `${secondaryMessagesContainer} > .messages-list__item`,
+        0,
+      )
+
+      // Trigger new messages via javascript
+      .click(secondaryButtons.addStatus)
+      .click(secondaryButtons.addWarning)
+      .click(secondaryButtons.addError)
+
+      // We should have 6 total messages
+      .assert.elementCount(
+        `${secondaryMessagesContainer} > .messages-list__item`,
+        3,
+      )
+
+      // We should have 2 messages of each type
+      .assert.elementCount(
+        `${secondaryMessagesContainer} > .messages--status`,
+        1,
+      )
+      .assert.elementCount(
+        `${secondaryMessagesContainer} > .messages--warning`,
+        1,
+      )
+      .assert.elementCount(
+        `${secondaryMessagesContainer} > .messages--error`,
+        1,
+      );
+  },
+};
diff --git a/core/tests/Drupal/Nightwatch/Tests/Olivero/oliveroDrupalMessageTest.js b/core/tests/Drupal/Nightwatch/Tests/Olivero/oliveroDrupalMessageTest.js
index 958f33381f03133b781efd0cc853f18d071dbf54..9e145eded55c3bf1aa2f90485b6efe295f23663e 100644
--- a/core/tests/Drupal/Nightwatch/Tests/Olivero/oliveroDrupalMessageTest.js
+++ b/core/tests/Drupal/Nightwatch/Tests/Olivero/oliveroDrupalMessageTest.js
@@ -35,7 +35,7 @@ module.exports = {
   },
   'Verify default placement of javascript-created messages': (browser) => {
     browser
-      .drupalRelativeURL('/js_message_test_link')
+      .drupalRelativeURL('/js_message_test_link_with_system_messages')
       .waitForElementVisible(mainContent)
       .assert.elementPresent(mainMessagesContainer)
 
@@ -63,7 +63,7 @@ module.exports = {
 
   'Verify customized placement of javascript-created messages': (browser) => {
     browser
-      .drupalRelativeURL('/js_message_test_link')
+      .drupalRelativeURL('/js_message_test_link_with_system_messages')
       .waitForElementVisible(mainContent)
       .assert.elementPresent(secondaryMessagesContainer)
 
diff --git a/core/themes/claro/js/messages.js b/core/themes/claro/js/messages.js
index 196568742af72572ccd9f382b41e9485a4beda74..26632ae6bce1a28e1b58b576826117c6c9b29fa7 100644
--- a/core/themes/claro/js/messages.js
+++ b/core/themes/claro/js/messages.js
@@ -25,7 +25,10 @@
     const messagesTypes = Drupal.Message.getMessageTypeLabels();
     const messageWrapper = document.createElement('div');
 
-    messageWrapper.setAttribute('class', `messages messages--${type}`);
+    messageWrapper.setAttribute(
+      'class',
+      `messages messages--${type} messages-list__item`,
+    );
     messageWrapper.setAttribute(
       'role',
       type === 'error' || type === 'warning' ? 'alert' : 'status',
diff --git a/core/themes/claro/templates/misc/status-messages.html.twig b/core/themes/claro/templates/misc/status-messages.html.twig
index 91207a54ccd327b48132f9de100b51d2ddfd88ff..7708a745b2fcb0ecb5bd45222b9e4118634a7dab 100644
--- a/core/themes/claro/templates/misc/status-messages.html.twig
+++ b/core/themes/claro/templates/misc/status-messages.html.twig
@@ -23,50 +23,52 @@
  */
 #}
 <div data-drupal-messages class="messages-list">
-{% for type, messages in message_list %}
-  {%
-    set classes = [
-      'messages-list__item',
-      'messages',
-      'messages--' ~ type,
-    ]
-  %}
-  {%
-    set is_message_with_title = status_headings[type]
-  %}
-  {%
-    set is_message_with_icon = type in ['error', 'status', 'warning']
-  %}
+  <div class="messages-list__wrapper">
+    {% for type, messages in message_list %}
+      {%
+        set classes = [
+          'messages-list__item',
+          'messages',
+          'messages--' ~ type,
+        ]
+      %}
+      {%
+        set is_message_with_title = status_headings[type]
+      %}
+      {%
+        set is_message_with_icon = type in ['error', 'status', 'warning']
+      %}
 
-  <div role="contentinfo" aria-labelledby="{{ title_ids[type] }}"{{ attributes.addClass(classes)|without('role', 'aria-label') }}>
-    {% if type == 'error' %}
-      <div role="alert">
-    {% endif %}
-      {% if is_message_with_title or is_message_with_icon %}
-        <div class="messages__header">
-          {% if is_message_with_title %}
-            <h2 id="{{ title_ids[type] }}" class="messages__title">
-              {{ status_headings[type] }}
-            </h2>
+      <div role="contentinfo" aria-labelledby="{{ title_ids[type] }}"{{ attributes.addClass(classes)|without('role', 'aria-label') }}>
+        {% if type == 'error' %}
+          <div role="alert">
+        {% endif %}
+          {% if is_message_with_title or is_message_with_icon %}
+            <div class="messages__header">
+              {% if is_message_with_title %}
+                <h2 id="{{ title_ids[type] }}" class="messages__title">
+                  {{ status_headings[type] }}
+                </h2>
+              {% endif %}
+            </div>
           {% endif %}
-        </div>
-      {% endif %}
-      <div class="messages__content">
-        {% if messages|length > 1 %}
-          <ul class="messages__list">
-            {% for message in messages %}
-              <li class="messages__item">{{ message }}</li>
-            {% endfor %}
-          </ul>
-        {% else %}
-          {{ messages|first }}
+          <div class="messages__content">
+            {% if messages|length > 1 %}
+              <ul class="messages__list">
+                {% for message in messages %}
+                  <li class="messages__item">{{ message }}</li>
+                {% endfor %}
+              </ul>
+            {% else %}
+              {{ messages|first }}
+            {% endif %}
+          </div>
+        {% if type == 'error' %}
+          </div>
         {% endif %}
       </div>
-    {% if type == 'error' %}
-      </div>
-    {% endif %}
+      {# Remove type specific classes. #}
+      {% set attributes = attributes.removeClass(classes) %}
+    {% endfor %}
   </div>
-  {# Remove type specific classes. #}
-  {% set attributes = attributes.removeClass(classes) %}
-{% endfor %}
 </div>