diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 9a1794a2ad8936b4e254b5819407461e5a0fd5c2..5d14f31d1f359645a4ebecbe3febc2b2a699d503 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -500,25 +500,6 @@ function template_preprocess_datetime_wrapper(&$variables) {
   $variables['content'] = $element['#children'];
 }
 
-/**
- * Prepares variables for status message templates.
- *
- * Default template: status-messages.html.twig.
- *
- * @param array $variables
- *   An associative array containing:
- *   - display: (optional) May have a value of 'status' or 'error' when only
- *     displaying messages of that specific type.
- */
-function template_preprocess_status_messages(&$variables) {
-  $variables['message_list'] = drupal_get_messages($variables['display']);
-  $variables['status_headings'] = array(
-    'status' => t('Status message'),
-    'error' => t('Error message'),
-    'warning' => t('Warning message'),
-  );
-}
-
 /**
  * Prepares variables for links templates.
  *
@@ -1376,7 +1357,6 @@ function template_preprocess_page(&$variables) {
   $site_config = \Drupal::config('system.site');
 
   // Move some variables to the top level for themer convenience and template cleanliness.
-  $variables['show_messages'] = $variables['page']['#show_messages'];
   $variables['title'] = $variables['page']['#title'];
 
   foreach (system_region_list(\Drupal::theme()->getActiveTheme()->getName()) as $region_key => $region_name) {
@@ -1414,13 +1394,6 @@ function template_preprocess_page(&$variables) {
   if ($node = \Drupal::routeMatch()->getParameter('node')) {
     $variables['node'] = $node;
   }
-
-  // Prepare render array for messages. drupal_get_messages() is called later,
-  // when this variable is rendered in a theme function or template file.
-  $variables['messages'] = array(
-    '#theme' => 'status_messages',
-    '#access' => $variables['show_messages'],
-  );
 }
 
 /**
@@ -1763,7 +1736,7 @@ function drupal_common_theme() {
       'render element' => 'element',
     ),
     'status_messages' => array(
-      'variables' => array('display' => NULL),
+      'variables' => ['status_headings' => [], 'message_list' => NULL],
     ),
     'links' => array(
       'variables' => array('links' => array(), 'attributes' => array('class' => array('links')), 'heading' => array(), 'set_active_class' => FALSE),
diff --git a/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php b/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php
index e3f31b961b25247456b183bf05b26df0f0e2ca39..fe117e173083807db1af21f27b564b5ce7d3e179 100644
--- a/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php
+++ b/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php
@@ -35,7 +35,7 @@ trait CommandWithAttachedAssetsTrait {
   protected function getRenderedContent() {
     $this->attachedAssets = new AttachedAssets();
     if (is_array($this->content)) {
-      $html = \Drupal::service('renderer')->render($this->content);
+      $html = \Drupal::service('renderer')->renderRoot($this->content);
       $this->attachedAssets = AttachedAssets::createFromRenderArray($this->content);
       return $html;
     }
diff --git a/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php b/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..8435960f92b89736a2b5ced2ae70402331043dfa
--- /dev/null
+++ b/core/lib/Drupal/Core/Block/MessagesBlockPluginInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Block\MessagesBlockPluginInterface.
+ */
+
+namespace Drupal\Core\Block;
+
+/**
+ * The interface for "messages" (#type => status_messages) blocks.
+ *
+ * @see drupal_set_message()
+ * @see drupal_get_message()
+ * @see \Drupal\Core\Render\Element\StatusMessages
+ * @see \Drupal\block\Plugin\DisplayVariant\BlockPageVariant
+ *
+ * @ingroup block_api
+ */
+interface MessagesBlockPluginInterface extends BlockPluginInterface { }
diff --git a/core/lib/Drupal/Core/Display/Annotation/PageDisplayVariant.php b/core/lib/Drupal/Core/Display/Annotation/PageDisplayVariant.php
index bc07297c57ef67321613798f3a7bd4b06a319710..1d5905d4208cd0556eb611a35b003600ff7c768d 100644
--- a/core/lib/Drupal/Core/Display/Annotation/PageDisplayVariant.php
+++ b/core/lib/Drupal/Core/Display/Annotation/PageDisplayVariant.php
@@ -11,7 +11,10 @@
  * Defines a page display variant annotation object.
  *
  * Page display variants are a specific type of display variant, intended to
- * render the main content of a page.
+ * render entire pages. They must render the crucial parts of a page, which are:
+ * - the title
+ * - the main content
+ * - any messages (#type => status_messages)
  *
  * @see \Drupal\Core\Display\VariantInterface
  * @see \Drupal\Core\Display\PageVariantInterface
diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php
index 9529c8a04d1528ec112d957b1b48574bc338796d..0513eb512a2c90f1c6dc658812b2bb715fb7e696 100644
--- a/core/lib/Drupal/Core/Extension/ThemeHandler.php
+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -465,6 +465,7 @@ public function rebuildThemeData() {
         'secondary_menu' => 'Secondary menu',
         'footer' => 'Footer',
         'highlighted' => 'Highlighted',
+        'messages' => 'Messages',
         'help' => 'Help',
         'page_top' => 'Page top',
         'page_bottom' => 'Page bottom',
diff --git a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php
index f528409750c87efcf26800b61ccaee10b0dbb0d8..bf05a3df28343504e2930ad59bee76f7609be8a1 100644
--- a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php
+++ b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php
@@ -49,6 +49,12 @@ public function renderBarePage(array $content, $title, $page_theme_property, arr
       ] + $page_additions,
     ];
 
+    // For backwards compatibility.
+    // @todo In Drupal 9, add a $show_messages function parameter.
+    if (!isset($page_additions['#show_messages']) || $page_additions['#show_messages'] === TRUE) {
+      $html['page']['messages'] = ['#type' => 'status_messages'];
+    }
+
     // We must first render the contents of the html.html.twig template, see
     // \Drupal\Core\Render\MainContent\HtmlRenderer::renderResponse() for more
     // information about this; the exact same pattern is used there and
diff --git a/core/lib/Drupal/Core/Render/Element/Page.php b/core/lib/Drupal/Core/Render/Element/Page.php
index b65446a4729413fc462a5710e40f12cb097c67f1..b73c808d301d29ea1deeba0a314937c9345e4207 100644
--- a/core/lib/Drupal/Core/Render/Element/Page.php
+++ b/core/lib/Drupal/Core/Render/Element/Page.php
@@ -21,7 +21,6 @@ class Page extends RenderElement {
    */
   public function getInfo() {
     return array(
-      '#show_messages' => TRUE,
       '#theme' => 'page',
       '#title' => '',
     );
diff --git a/core/lib/Drupal/Core/Render/Element/StatusMessages.php b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
new file mode 100644
index 0000000000000000000000000000000000000000..febf01816d8caef1ace575ffa85ce84500ad9fcc
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Element\StatusMessages.
+ */
+
+namespace Drupal\Core\Render\Element;
+
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Site\Settings;
+
+/**
+ * Provides a messages element.
+ *
+ * @RenderElement("status_messages")
+ */
+class StatusMessages extends RenderElement {
+
+  /**
+   * {@inheritdoc}
+   *
+   * Generate the placeholder in a #pre_render callback, because the hash salt
+   * needs to be accessed, which may not yet be available when this is called.
+   */
+  public function getInfo() {
+    return [
+      // May have a value of 'status' or 'error' when only displaying messages
+      // of that specific type.
+      '#display' => NULL,
+      '#pre_render' => [
+        get_class() . '::generatePlaceholder',
+      ],
+    ];
+  }
+
+  /**
+   * #pre_render callback to generate a placeholder.
+   *
+   * Ensures the same token is used for all instances, hence resulting in the
+   * same placeholder for all places rendering the status messages for this
+   * request (e.g. in multiple blocks). This ensures we can put the rendered
+   * messages in all placeholders in one go.
+   * Also ensures the same context key is used for the #post_render_cache
+   * property, this ensures that if status messages are rendered multiple times,
+   * their individual (but identical!) #post_render_cache properties are merged,
+   * ensuring the callback is only invoked once.
+   *
+   * @see ::renderMessages()
+
+   * @param array $element
+   *   A renderable array.
+   *
+   * @return array
+   *   The updated renderable array containing the placeholder.
+   */
+  public static function generatePlaceholder(array $element) {
+    $plugin_id = 'status_messages';
+
+    $callback = get_class() . '::renderMessages';
+    try {
+      $hash_salt = Settings::getHashSalt();
+    }
+    catch (\RuntimeException $e) {
+      // Status messages are also shown during the installer, at which time no
+      // hash salt is defined yet.
+      $hash_salt = Crypt::randomBytes(8);
+    }
+    $key = $plugin_id . $element['#display'];
+    $context = [
+      'display' => $element['#display'],
+      'token' => Crypt::hmacBase64($key, $hash_salt),
+    ];
+    $placeholder = static::renderer()->generateCachePlaceholder($callback, $context);
+    $element['#post_render_cache'] = [
+      $callback => [
+        $key => $context,
+      ],
+    ];
+    $element['#markup'] = $placeholder;
+
+    return $element;
+  }
+
+  /**
+   * #post_render_cache callback; replaces placeholder with messages.
+   *
+   * Note: this is designed to replace all #post_render_cache placeholders for
+   *   messages in a single #post_render_cache callback; hence all placeholders
+   *   must be identical.
+   *
+   * @see ::getInfo()
+   *
+   * @param array $element
+   *   The renderable array that contains the to be replaced placeholder.
+   * @param array $context
+   *   An array with any context information.
+   *
+   * @return array
+   *   A renderable array containing the messages.
+   */
+  public static function renderMessages(array $element, array $context) {
+    $renderer = static::renderer();
+
+    // Render the messages.
+    $messages = [
+      '#theme' => 'status_messages',
+      // @todo Improve when https://www.drupal.org/node/2278383 lands.
+      '#message_list' => drupal_get_messages($context['display']),
+      '#status_headings' => [
+        'status' => t('Status message'),
+        'error' => t('Error message'),
+        'warning' => t('Warning message'),
+      ],
+    ];
+    $markup = $renderer->render($messages);
+
+    // Replace the placeholder.
+    $callback = get_class() . '::renderMessages';
+    $placeholder = $renderer->generateCachePlaceholder($callback, $context);
+    $element['#markup'] = str_replace($placeholder, $markup, $element['#markup']);
+    $element = $renderer->mergeBubbleableMetadata($element, $messages);
+
+    return $element;
+  }
+
+  /**
+   * Wraps the renderer.
+   *
+   * @return \Drupal\Core\Render\RendererInterface
+   */
+  protected static function renderer() {
+    return \Drupal::service('renderer');
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php b/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php
index 6cddc0ceac8586304579be01f0c0a064aae846c3..6900b6e857f36103855f544aa7a879c1aa26451c 100644
--- a/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php
@@ -71,7 +71,7 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
     // replace the element making the Ajax call. The default 'replaceWith'
     // behavior can be changed with #ajax['method'].
     $response->addCommand(new InsertCommand(NULL, $html));
-    $status_messages = array('#theme' => 'status_messages');
+    $status_messages = array('#type' => 'status_messages');
     $output = $this->drupalRenderRoot($status_messages);
     if (!empty($output)) {
       $response->addCommand(new PrependCommand(NULL, $output));
diff --git a/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php b/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php
index 7772875b14eae84452fcdd290e5988e4e385ac96..66f908605c4b086771eeabcdc3a5786b3916f5e9 100644
--- a/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php
+++ b/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php
@@ -39,7 +39,13 @@ public function setMainContent(array $main_content) {
    */
   public function build() {
     $build = [
-      'content' => $this->mainContent,
+      'content' => [
+        'main_content' => $this->mainContent,
+        'messages' => [
+          '#type' => 'status_messages',
+          '#weight' => -1000,
+        ],
+      ],
     ];
     return $build;
   }
diff --git a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
index bf91a04fd4e947e1e944932d36326ae77c05a105..798805e6840b64b07a797af9fa3ac432772238b1 100644
--- a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
+++ b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
@@ -11,6 +11,7 @@
 use Drupal\block\Event\BlockContextEvent;
 use Drupal\block\Event\BlockEvents;
 use Drupal\Core\Block\MainContentBlockPluginInterface;
+use Drupal\Core\Block\MessagesBlockPluginInterface;
 use Drupal\Core\Display\PageVariantInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\EntityViewBuilderInterface;
@@ -22,6 +23,14 @@
 /**
  * Provides a page display variant that decorates the main content with blocks.
  *
+ * To ensure essential information is displayed, each essential part of a page
+ * has a corresponding block plugin interface, so that BlockPageVariant can
+ * automatically provide a fallback in case no block for each of these
+ * interfaces is placed.
+ *
+ * @see \Drupal\Core\Block\MainContentBlockPluginInterface
+ * @see \Drupal\Core\Block\MessagesBlockPluginInterface
+ *
  * @PageDisplayVariant(
  *   id = "block_page",
  *   admin_label = @Translation("Page with blocks")
@@ -110,8 +119,9 @@ public function setMainContent(array $main_content) {
    * {@inheritdoc}
    */
   public function build() {
-    // Track whether a block that shows the main content is displayed or not.
+    // Track whether blocks showing the main content and messages are displayed.
     $main_content_block_displayed = FALSE;
+    $messages_block_displayed = FALSE;
 
     $build = [
       '#cache' => [
@@ -128,6 +138,9 @@ public function build() {
           $block_plugin->setMainContent($this->mainContent);
           $main_content_block_displayed = TRUE;
         }
+        elseif ($block_plugin instanceof MessagesBlockPluginInterface) {
+          $messages_block_displayed = TRUE;
+        }
         $build[$region][$key] = $this->blockViewBuilder->view($block);
       }
       if (!empty($build[$region])) {
@@ -144,6 +157,14 @@ public function build() {
       $build['content']['system_main'] = $this->mainContent;
     }
 
+    // If no block displays status messages, still render them.
+    if (!$messages_block_displayed) {
+      $build['content']['messages'] = [
+        '#weight' => -1000,
+        '#type' => 'status_messages',
+      ];
+    }
+
     return $build;
   }
 
diff --git a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
index c39d744a8c07185e31321cd4a120afbd5ea067a2..bb5390ce2e0b0401d324fa09af7162f898ad0264 100644
--- a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
+++ b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
@@ -70,23 +70,28 @@ public function setUpDisplayVariant($configuration = array(), $definition = arra
   public function providerBuild() {
     $blocks_config = array(
       'block1' => array(
-        'top', FALSE,
+        // region, is main content block, is messages block
+        'top', FALSE, FALSE,
       ),
       // Test multiple blocks in the same region.
       'block2' => array(
-        'bottom', FALSE,
+        'bottom', FALSE, FALSE,
       ),
       'block3' => array(
-        'bottom', FALSE,
+        'bottom', FALSE, FALSE,
       ),
       // Test a block implementing MainContentBlockPluginInterface.
       'block4' => array(
-        'center', TRUE,
+        'center', TRUE, FALSE,
+      ),
+      // Test a block implementing MessagesBlockPluginInterface.
+      'block5' => array(
+        'center', FALSE, TRUE,
       ),
     );
 
     $test_cases = [];
-    $test_cases[] = [$blocks_config, 4,
+    $test_cases[] = [$blocks_config, 5,
       [
         '#cache' => [
           'tags' => [
@@ -100,6 +105,7 @@ public function providerBuild() {
         // The main content was rendered via a block.
         'center' => [
           'block4' => [],
+          'block5' => [],
           '#sorted' => TRUE,
         ],
         'bottom' => [
@@ -109,6 +115,37 @@ public function providerBuild() {
         ],
       ],
     ];
+    unset($blocks_config['block5']);
+    $test_cases[] = [$blocks_config, 4,
+      [
+        '#cache' => [
+          'tags' => [
+            'config:block_list',
+          ],
+        ],
+        'top' => [
+          'block1' => [],
+          '#sorted' => TRUE,
+        ],
+        'center' => [
+          'block4' => [],
+          '#sorted' => TRUE,
+        ],
+        'bottom' => [
+          'block2' => [],
+          'block3' => [],
+          '#sorted' => TRUE,
+        ],
+        // The messages are rendered via the fallback in case there is no block
+        // rendering the main content.
+        'content' => [
+          'messages' => [
+            '#weight' => -1000,
+            '#type' => 'status_messages',
+          ],
+        ],
+      ],
+    ];
     unset($blocks_config['block4']);
     $test_cases[] = [$blocks_config, 3,
       [
@@ -126,10 +163,14 @@ public function providerBuild() {
           'block3' => [],
           '#sorted' => TRUE,
         ],
-        // The main content was rendered via the fallback in case there is no
-        // block rendering the main content.
+        // The main content & messages are rendered via the fallback in case
+        // there are no blocks rendering them.
         'content' => [
           'system_main' => ['#markup' => 'Hello kittens!'],
+          'messages' => [
+            '#weight' => -1000,
+            '#type' => 'status_messages',
+          ],
         ],
       ],
     ];
@@ -150,11 +191,12 @@ public function testBuild(array $blocks_config, $visible_block_count, array $exp
     $blocks = ['top' => [], 'center' => [], 'bottom' => []];
     $block_plugin = $this->getMock('Drupal\Core\Block\BlockPluginInterface');
     $main_content_block_plugin = $this->getMock('Drupal\Core\Block\MainContentBlockPluginInterface');
+    $messages_block_plugin = $this->getMock('Drupal\Core\Block\MessagesBlockPluginInterface');
     foreach ($blocks_config as $block_id => $block_config) {
       $block = $this->getMock('Drupal\block\BlockInterface');
       $block->expects($this->atLeastOnce())
         ->method('getPlugin')
-        ->willReturn($block_config[1] ? $main_content_block_plugin : $block_plugin);
+        ->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : $block_plugin));
       $blocks[$block_config[0]][$block_id] = $block;
     }
 
@@ -187,6 +229,10 @@ public function testBuildWithoutMainContent() {
       ],
       'content' => [
         'system_main' => [],
+        'messages' => [
+          '#weight' => -1000,
+          '#type' => 'status_messages',
+        ],
       ],
     ];
     $this->assertSame($expected, $display_variant->build());
diff --git a/core/modules/editor/src/Form/EditorImageDialog.php b/core/modules/editor/src/Form/EditorImageDialog.php
index e3d592ad9c0954440f173a5501b026bc56ba47d8..b2fed135f1b6aca4a9c6d8b862dbc2fbbdc8d021 100644
--- a/core/modules/editor/src/Form/EditorImageDialog.php
+++ b/core/modules/editor/src/Form/EditorImageDialog.php
@@ -227,7 +227,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     if ($form_state->getErrors()) {
       unset($form['#prefix'], $form['#suffix']);
       $form['status_messages'] = [
-        '#theme' => 'status_messages',
+        '#type' => 'status_messages',
         '#weight' => -10,
       ];
       $response->addCommand(new HtmlCommand('#editor-image-dialog-form', $form));
diff --git a/core/modules/editor/src/Form/EditorLinkDialog.php b/core/modules/editor/src/Form/EditorLinkDialog.php
index feb1d1fced884cd57d15290b08f1e8f4d6fced51..c5763894da05a23281e702738f231633e2b12b8e 100644
--- a/core/modules/editor/src/Form/EditorLinkDialog.php
+++ b/core/modules/editor/src/Form/EditorLinkDialog.php
@@ -86,7 +86,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     if ($form_state->getErrors()) {
       unset($form['#prefix'], $form['#suffix']);
       $form['status_messages'] = [
-        '#theme' => 'status_messages',
+        '#type' => 'status_messages',
         '#weight' => -10,
       ];
       $response->addCommand(new HtmlCommand('#editor-link-dialog-form', $form));
diff --git a/core/modules/file/src/Controller/FileWidgetAjaxController.php b/core/modules/file/src/Controller/FileWidgetAjaxController.php
index e449caf159136ebfec44d59b7bd6947ee1f81e9e..90176f8da07ef7881918b9a56775661bae38ba6f 100644
--- a/core/modules/file/src/Controller/FileWidgetAjaxController.php
+++ b/core/modules/file/src/Controller/FileWidgetAjaxController.php
@@ -38,8 +38,8 @@ public function upload(Request $request) {
       // Invalid request.
       drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error');
       $response = new AjaxResponse();
-      $status_messages = array('#theme' => 'status_messages');
-      return $response->addCommand(new ReplaceCommand(NULL, drupal_render($status_messages)));
+      $status_messages = array('#type' => 'status_messages');
+      return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($status_messages)));
     }
 
     try {
@@ -53,8 +53,8 @@ public function upload(Request $request) {
       // Invalid form_build_id.
       drupal_set_message(t('An unrecoverable error occurred. Use of this form has expired. Try reloading the page and submitting again.'), 'error');
       $response = new AjaxResponse();
-      $status_messages = array('#theme' => 'status_messages');
-      return $response->addCommand(new ReplaceCommand(NULL, drupal_render($status_messages)));
+      $status_messages = array('#type' => 'status_messages');
+      return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($status_messages)));
     }
 
     // Get the current element and count the number of files.
@@ -76,9 +76,9 @@ public function upload(Request $request) {
       $form['#suffix'] .= '<span class="ajax-new-content"></span>';
     }
 
-    $status_messages = array('#theme' => 'status_messages');
-    $form['#prefix'] .= drupal_render($status_messages);
-    $output = drupal_render($form);
+    $status_messages = array('#type' => 'status_messages');
+    $form['#prefix'] .= $this->renderer->renderRoot($status_messages);
+    $output = $this->renderer->renderRoot($form);
 
     $response = new AjaxResponse();
     $response->setAttachments($form['#attached']);
diff --git a/core/modules/quickedit/src/QuickEditController.php b/core/modules/quickedit/src/QuickEditController.php
index 3747609e33fb141c2217f5f406ccc2cbde51508c..383f61b8151b534ea025f35bf6c9dacd02839341 100644
--- a/core/modules/quickedit/src/QuickEditController.php
+++ b/core/modules/quickedit/src/QuickEditController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Form\FormState;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\user\PrivateTempStoreFactory;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\JsonResponse;
@@ -47,6 +48,13 @@ class QuickEditController extends ControllerBase {
    */
   protected $editorSelector;
 
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
   /**
    * Constructs a new QuickEditController.
    *
@@ -56,11 +64,14 @@ class QuickEditController extends ControllerBase {
    *   The in-place editing metadata generator.
    * @param \Drupal\quickedit\EditorSelectorInterface $editor_selector
    *   The in-place editor selector.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
    */
-  public function __construct(PrivateTempStoreFactory $temp_store_factory, MetadataGeneratorInterface $metadata_generator, EditorSelectorInterface $editor_selector) {
+  public function __construct(PrivateTempStoreFactory $temp_store_factory, MetadataGeneratorInterface $metadata_generator, EditorSelectorInterface $editor_selector, RendererInterface $renderer) {
     $this->tempStoreFactory = $temp_store_factory;
     $this->metadataGenerator = $metadata_generator;
     $this->editorSelector = $editor_selector;
+    $this->renderer = $renderer;
   }
 
   /**
@@ -70,7 +81,8 @@ public static function create(ContainerInterface $container) {
     return new static(
       $container->get('user.private_tempstore'),
       $container->get('quickedit.metadata.generator'),
-      $container->get('quickedit.editor.selector')
+      $container->get('quickedit.editor.selector'),
+      $container->get('renderer')
     );
   }
 
@@ -204,7 +216,7 @@ public function fieldForm(EntityInterface $entity, $field_name, $langcode, $view
       $response->addCommand(new FieldFormSavedCommand($output, $other_view_modes));
     }
     else {
-      $output = drupal_render($form);
+      $output = $this->renderer->renderRoot($form);
       // When working with a hidden form, we don't want its CSS/JS to be loaded.
       if ($request->request->get('nocssjs') !== 'true') {
         $response->setAttachments($form['#attached']);
@@ -214,9 +226,9 @@ public function fieldForm(EntityInterface $entity, $field_name, $langcode, $view
       $errors = $form_state->getErrors();
       if (count($errors)) {
         $status_messages = array(
-          '#theme' => 'status_messages'
+          '#type' => 'status_messages'
         );
-        $response->addCommand(new FieldFormValidationErrorsCommand(drupal_render($status_messages)));
+        $response->addCommand(new FieldFormValidationErrorsCommand($this->renderer->renderRoot($status_messages)));
       }
     }
 
@@ -263,7 +275,7 @@ protected function renderField(EntityInterface $entity, $field_name, $langcode,
       $output = $this->moduleHandler()->invoke($module, 'quickedit_render_field', $args);
     }
 
-    return drupal_render($output);
+    return $this->renderer->renderRoot($output);
   }
 
   /**
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index 89930adbb4ab323174471cb923a44450f598ba49..14b839b19beb25c9c769a5d09ba0bb3ae43069f3 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -560,7 +560,7 @@ protected function registerStreamWrapper($scheme, $class, $type = StreamWrapperI
    *   The rendered string output (typically HTML).
    */
   protected function render(array &$elements) {
-    $content = drupal_render($elements);
+    $content = $this->container->get('renderer')->renderRoot($elements);
     drupal_process_attached($elements);
     $this->setRawContent($content);
     $this->verbose('<pre style="white-space: pre-wrap">' . String::checkPlain($content));
diff --git a/core/modules/system/core.api.php b/core/modules/system/core.api.php
index 407e439ee7dd3653e26ad8ea97ca3949ac6a4b1d..090d9142a782cc05dc820766e1092fa045663aaf 100644
--- a/core/modules/system/core.api.php
+++ b/core/modules/system/core.api.php
@@ -1991,7 +1991,7 @@ function hook_display_variant_plugin_alter(array &$definitions) {
  * 'wrapper' method and return HTML markup. This is not the case if you return
  * commands, but if you would like to show status messages, you can add
  * @code
- * array('#theme' => 'status_messages')
+ * array('#type' => 'status_messages')
  * @endcode
  * to a render array, use drupal_render() to render it, and add a command to
  * place the messages in an appropriate location.
diff --git a/core/modules/system/form.api.php b/core/modules/system/form.api.php
index 9ec09e0dfd1b3686e35196fbcbe5ea5de08eceb8..765ce6094e4231522180570a820f0d5edab35e28 100644
--- a/core/modules/system/form.api.php
+++ b/core/modules/system/form.api.php
@@ -153,8 +153,8 @@ function callback_batch_finished($success, $results, $operations) {
  */
 function hook_ajax_render_alter(array &$data) {
   // Inject any new status messages into the content area.
-  $status_messages = array('#theme' => 'status_messages');
-  $command = new \Drupal\Core\Ajax\PrependCommand('#block-system-main .content', drupal_render($status_messages));
+  $status_messages = array('#type' => 'status_messages');
+  $command = new \Drupal\Core\Ajax\PrependCommand('#block-system-main .content', \Drupal::service('renderer')->renderRoot($status_messages));
   $data[] = $command->render();
 }
 
diff --git a/core/modules/system/src/Controller/FormAjaxController.php b/core/modules/system/src/Controller/FormAjaxController.php
index 8f122e9c5c59c61c13984d214ccb8be023efdab9..f1a39f6054b16d211b1d80324dcc89fe939cb143 100644
--- a/core/modules/system/src/Controller/FormAjaxController.php
+++ b/core/modules/system/src/Controller/FormAjaxController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Ajax\UpdateBuildIdCommand;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\system\FileAjaxForm;
 use Drupal\Core\Form\FormBuilderInterface;
 use Psr\Log\LoggerInterface;
@@ -37,18 +38,27 @@ class FormAjaxController implements ContainerInjectionInterface {
    */
   protected $formBuilder;
 
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
   /**
    * Constructs a FormAjaxController object.
    *
    * @param \Psr\Log\LoggerInterface $logger
    *   A logger instance.
-   *
    * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
    *   The form builder.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
    */
-  public function __construct(LoggerInterface $logger, FormBuilderInterface $form_builder) {
+  public function __construct(LoggerInterface $logger, FormBuilderInterface $form_builder, RendererInterface $renderer) {
     $this->logger = $logger;
     $this->formBuilder = $form_builder;
+    $this->renderer = $renderer;
   }
 
   /**
@@ -57,7 +67,8 @@ public function __construct(LoggerInterface $logger, FormBuilderInterface $form_
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('logger.factory')->get('ajax'),
-      $container->get('form_builder')
+      $container->get('form_builder'),
+      $container->get('renderer')
     );
   }
 
diff --git a/core/modules/system/src/Plugin/Block/SystemMessagesBlock.php b/core/modules/system/src/Plugin/Block/SystemMessagesBlock.php
new file mode 100644
index 0000000000000000000000000000000000000000..b7247801f3710168433fbba15755db6149e69a0c
--- /dev/null
+++ b/core/modules/system/src/Plugin/Block/SystemMessagesBlock.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Plugin\Block\SystemMessagesBlock.
+ */
+
+namespace Drupal\system\Plugin\Block;
+
+use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Block\MessagesBlockPluginInterface;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides a block to display the messages.
+ *
+ * @see drupal_set_message()
+ *
+ * @Block(
+ *   id = "system_messages_block",
+ *   admin_label = @Translation("Messages")
+ * )
+ */
+class SystemMessagesBlock extends BlockBase implements MessagesBlockPluginInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return array(
+      'label_display' => FALSE,
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    return ['#type' => 'status_messages'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    // @see ::isCacheable()
+    $form['cache']['#description'] = $this->t('This block is cacheable forever, it is not configurable.');
+    $form['cache']['max_age']['#value'] = Cache::PERMANENT;
+    $form['cache']['max_age']['#disabled'] = TRUE;
+    // Don't allow cache contexts to be configured, this block is globally
+    // cacheable.
+    $form['cache']['contexts']['#access'] = FALSE;
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isCacheable() {
+    // The messages are session-specific and hence aren't cacheable, but the
+    // block itself *is* cacheable because it uses a #post_render_cache callback
+    // and hence the block has a globally cacheable render array.
+    return TRUE;
+  }
+
+}
diff --git a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
index da74be826d6853211040a73f3c5e93a74528f0dd..f4d30eceec39b4fe37e6d95558d7f4019a761396 100644
--- a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
+++ b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
@@ -91,6 +91,7 @@ function testPageCacheTags() {
       'config:block.block.bartik_powered',
       'config:block.block.bartik_main_menu',
       'config:block.block.bartik_account_menu',
+      'config:block.block.bartik_messages',
       'node_view',
       'node:' . $node_1->id(),
       'user:' . $author_1->id(),
@@ -116,6 +117,7 @@ function testPageCacheTags() {
       'config:block.block.bartik_powered',
       'config:block.block.bartik_main_menu',
       'config:block.block.bartik_account_menu',
+      'config:block.block.bartik_messages',
       'node_view',
       'node:' . $node_2->id(),
       'user:' . $author_2->id(),
diff --git a/core/modules/system/src/Tests/Theme/MessageTest.php b/core/modules/system/src/Tests/Theme/MessageTest.php
index 3e8da82744ae8fd9e4e0549b9be0f19f6a83d0a8..db27d2a2f48e14b848fa368259102b270b0e9979 100644
--- a/core/modules/system/src/Tests/Theme/MessageTest.php
+++ b/core/modules/system/src/Tests/Theme/MessageTest.php
@@ -32,7 +32,7 @@ function testMessages() {
     drupal_set_message('An error occurred', 'error');
     drupal_set_message('But then something nice happened');
     $messages = array(
-      '#theme' => 'status_messages',
+      '#type' => 'status_messages',
     );
     $this->render($messages);
     $this->assertRaw('messages messages--error');
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index cf175ca251c00fe8195ddcea280c3e6ef7572330..d73ba633c220646e81788a50fd5b84ddf6c0ebca 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -168,6 +168,9 @@ function system_theme() {
       'render element' => 'elements',
       'base hook' => 'block',
     ),
+    'block__system_messages_block' => array(
+      'base hook' => 'block',
+    ),
     'block__system_menu_block' => array(
       'render element' => 'elements',
       'base hook' => 'block',
diff --git a/core/modules/system/templates/block--system-messages-block.html.twig b/core/modules/system/templates/block--system-messages-block.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..f35af143810977472fd6d8d8501583e6345c9330
--- /dev/null
+++ b/core/modules/system/templates/block--system-messages-block.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the messages block.
+ *
+ * Removes wrapper elements from block so that empty block does not appear when
+ * there are no messages.
+ *
+ * Available variables:
+ * - content: The content of this block.
+ *
+ * @ingroup themeable
+ */
+#}
+{{ content }}
diff --git a/core/modules/system/templates/install-page.html.twig b/core/modules/system/templates/install-page.html.twig
index 24d17faa865614be145cde859de5b016efda4fcc..d362d22c79c7629e5fea797c7ab4237d00124c69 100644
--- a/core/modules/system/templates/install-page.html.twig
+++ b/core/modules/system/templates/install-page.html.twig
@@ -30,7 +30,7 @@
       {% if title %}
         <h1>{{ title }}</h1>
       {% endif %}
-      {{ messages }}
+      {{ page.messages }}
       {{ page.content }}
     </main>
 
diff --git a/core/modules/system/templates/maintenance-page.html.twig b/core/modules/system/templates/maintenance-page.html.twig
index cd0cfd02517f3b75bacde40020d6a7e77dad1e94..f2706210f300a3e7958cfcaec8ae286162012804 100644
--- a/core/modules/system/templates/maintenance-page.html.twig
+++ b/core/modules/system/templates/maintenance-page.html.twig
@@ -41,7 +41,7 @@
       <h1>{{ title }}</h1>
     {% endif %}
 
-    {{ messages }}
+    {{ page.messages }}
 
     {{ page.content }}
   </main>
diff --git a/core/modules/system/templates/page.html.twig b/core/modules/system/templates/page.html.twig
index 627d7c7aeff604ba5ded5eab2a6abeb2cf24854d..eb5c7bf213a67ac4871e4cdd3c5c1c13f16f10e9 100644
--- a/core/modules/system/templates/page.html.twig
+++ b/core/modules/system/templates/page.html.twig
@@ -46,6 +46,7 @@
  * - page.primary_menu: Items for the primary menu region.
  * - page.secondary_menu: Items for the secondary menu region.
  * - page.highlighted: Items for the highlighted content region.
+ * - page.messages: Status messages block can be placed in messages region.
  * - page.help: Dynamic help text, mostly for admin pages.
  * - page.content: The main content of the current page.
  * - page.sidebar_first: Items for the first sidebar.
@@ -96,7 +97,7 @@
 
   {{ page.breadcrumb }}
 
-  {{ messages }}
+  {{ page.messages }}
 
   {{ page.help }}
 
diff --git a/core/modules/views/src/Plugin/views/area/Messages.php b/core/modules/views/src/Plugin/views/area/Messages.php
index e00739fd2dc24e819ae684aad857aea7fb76e498..56bd4e4a2c5c4008be25901fb5f5e3751f353ebe 100644
--- a/core/modules/views/src/Plugin/views/area/Messages.php
+++ b/core/modules/views/src/Plugin/views/area/Messages.php
@@ -32,7 +32,7 @@ protected function defineOptions() {
   public function render($empty = FALSE) {
     if (!$empty || !empty($this->options['empty'])) {
       return array(
-        '#theme' => 'status_messages',
+        '#type' => 'status_messages',
       );
     }
     return array();
diff --git a/core/modules/views/tests/src/Unit/Plugin/area/MessagesTest.php b/core/modules/views/tests/src/Unit/Plugin/area/MessagesTest.php
index 63800213bee54bf333c99dc49c07b68450a3c770..3c18e79966de9934fd1dd0c1e1661ff2582118df 100644
--- a/core/modules/views/tests/src/Unit/Plugin/area/MessagesTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/area/MessagesTest.php
@@ -48,13 +48,13 @@ protected function setUp() {
   public function testRender() {
     // The handler is configured to show with empty views by default, so should
     // appear.
-    $this->assertSame(array('#theme' => 'status_messages'), $this->messagesHandler->render());
+    $this->assertSame(array('#type' => 'status_messages'), $this->messagesHandler->render());
 
     // Turn empty off, and make sure it isn't rendered.
     $this->messagesHandler->options['empty'] = FALSE;
     // $empty parameter passed to render will still be FALSE, so should still
     // appear.
-    $this->assertSame(array('#theme' => 'status_messages'), $this->messagesHandler->render());
+    $this->assertSame(array('#type' => 'status_messages'), $this->messagesHandler->render());
     // Should now be empty as both the empty option and parameter are empty.
     $this->assertSame(array(), $this->messagesHandler->render(TRUE));
   }
diff --git a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
index 6dd609e3c8766d750a0d85f443841e5eb9fff393..17a31c1810ec6fa355f415362030fd91f65c680f 100644
--- a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
+++ b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
@@ -191,6 +191,9 @@ public function getForm(ViewEntityInterface $view, $display_id, $js) {
    *   #markup array.
    */
   protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state) {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
     // This won't override settings already in.
     if (!$form_state->has('rerender')) {
       $form_state->set('rerender', FALSE);
@@ -203,7 +206,7 @@ protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state)
     $form_state->disableCache();
 
     $form = \Drupal::formBuilder()->buildForm($form_class, $form_state);
-    $output = drupal_render($form);
+    $output = $renderer->renderRoot($form);
     drupal_process_attached($form);
 
     // These forms have the title built in, so set the title here:
@@ -220,8 +223,8 @@ protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state)
       $response->setAttachments($form['#attached']);
 
       $display = '';
-      $status_messages = array('#theme' => 'status_messages');
-      if ($messages = drupal_render($status_messages)) {
+      $status_messages = array('#type' => 'status_messages');
+      if ($messages = $renderer->renderRoot($status_messages)) {
         $display = '<div class="views-messages">' . $messages . '</div>';
       }
       $display .= $output;
diff --git a/core/profiles/minimal/config/install/block.block.stark_messages.yml b/core/profiles/minimal/config/install/block.block.stark_messages.yml
new file mode 100644
index 0000000000000000000000000000000000000000..838d8c046981bb10eb710299896142370e0e6bac
--- /dev/null
+++ b/core/profiles/minimal/config/install/block.block.stark_messages.yml
@@ -0,0 +1,17 @@
+id: stark_messages
+theme: stark
+weight: 0
+status: true
+langcode: en
+region: messages
+plugin: system_messages_block
+settings:
+  id: system_messages_block
+  label: 'Status messages'
+  provider: system
+  label_display: '0'
+dependencies:
+  module:
+    - system
+  theme:
+    - stark
diff --git a/core/profiles/standard/config/install/block.block.bartik_messages.yml b/core/profiles/standard/config/install/block.block.bartik_messages.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1d817dda3fa45740f61b2ab9898f204e406c44aa
--- /dev/null
+++ b/core/profiles/standard/config/install/block.block.bartik_messages.yml
@@ -0,0 +1,17 @@
+id: bartik_messages
+theme: bartik
+weight: 0
+status: true
+langcode: en
+region: messages
+plugin: system_messages_block
+settings:
+  id: system_messages_block
+  label: 'Status messages'
+  provider: system
+  label_display: '0'
+dependencies:
+  module:
+    - system
+  theme:
+    - bartik
diff --git a/core/profiles/standard/config/install/block.block.seven_messages.yml b/core/profiles/standard/config/install/block.block.seven_messages.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4a15ee9624c56f060a1c61245555aea870139f82
--- /dev/null
+++ b/core/profiles/standard/config/install/block.block.seven_messages.yml
@@ -0,0 +1,17 @@
+id: seven_messages
+theme: seven
+weight: 0
+status: true
+langcode: en
+region: messages
+plugin: system_messages_block
+settings:
+  id: system_messages_block
+  label: 'Status messages'
+  provider: system
+  label_display: '0'
+dependencies:
+  module:
+    - system
+  theme:
+    - seven
diff --git a/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php b/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php
index 069d370c8933b5d46f2c0c23174e59e55563212a..e959fbae89d2520c997111dfa96edd616893aa0b 100644
--- a/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php
+++ b/core/tests/Drupal/Tests/Core/Controller/AjaxRendererTest.php
@@ -74,8 +74,8 @@ protected function drupalRenderRoot(&$elements, $is_root_call = FALSE) {
     if (isset($elements['#markup'])) {
       return $elements['#markup'];
     }
-    elseif (isset($elements['#theme'])) {
-      return $elements['#theme'];
+    elseif (isset($elements['#type'])) {
+      return $elements['#type'];
     }
     else {
       return 'Markup';
diff --git a/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php b/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php
index e275309ff19e1d7175f305a97daebe8bec35663e..75b59ca7b8c538573c21341a95a10c48736127df 100644
--- a/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php
@@ -125,12 +125,10 @@ public function providerTestGetInfo() {
       'page',
       array(
         '#type' => 'page',
-        '#show_messages' => TRUE,
         '#theme' => 'page',
         '#defaults_loaded' => TRUE,
       ),
       array('page' => array(
-        '#show_messages' => TRUE,
         '#theme' => 'page',
       )),
     );
@@ -141,7 +139,6 @@ public function providerTestGetInfo() {
         '#defaults_loaded' => TRUE,
       ),
       array('page' => array(
-        '#show_messages' => TRUE,
         '#theme' => 'page',
       )),
     );
@@ -150,13 +147,11 @@ public function providerTestGetInfo() {
       'page',
       array(
         '#type' => 'page',
-        '#show_messages' => TRUE,
         '#theme' => 'page',
         '#number' => 597219,
         '#defaults_loaded' => TRUE,
       ),
       array('page' => array(
-        '#show_messages' => TRUE,
         '#theme' => 'page',
       )),
       function ($alter_name, array &$info) {
@@ -188,7 +183,6 @@ public function testGetInfoElementPlugin($plugin_class, $expected_info) {
     $plugin->expects($this->once())
       ->method('getInfo')
       ->willReturn(array(
-        '#show_messages' => TRUE,
         '#theme' => 'page',
       ));
 
@@ -225,7 +219,6 @@ public function providerTestGetInfoElementPlugin() {
       'Drupal\Core\Render\Element\ElementInterface',
       array(
         '#type' => 'page',
-        '#show_messages' => TRUE,
         '#theme' => 'page',
         '#defaults_loaded' => TRUE,
       ),
@@ -235,7 +228,6 @@ public function providerTestGetInfoElementPlugin() {
       'Drupal\Core\Render\Element\FormElementInterface',
       array(
         '#type' => 'page',
-        '#show_messages' => TRUE,
         '#theme' => 'page',
         '#input' => TRUE,
         '#value_callback' => array('TestElementPlugin', 'valueCallback'),
diff --git a/core/themes/bartik/bartik.info.yml b/core/themes/bartik/bartik.info.yml
index 37a2c68896477d2b62d5db19b6dd9b82a85365f1..c96aae7d0ae7e888873534661488e794c7a671f7 100644
--- a/core/themes/bartik/bartik.info.yml
+++ b/core/themes/bartik/bartik.info.yml
@@ -19,6 +19,7 @@ regions:
   help: Help
   page_top: 'Page top'
   page_bottom: 'Page bottom'
+  messages: Messages
   featured_top: 'Featured top'
   breadcrumb: Breadcrumb
   content: Content
diff --git a/core/themes/bartik/templates/maintenance-page.html.twig b/core/themes/bartik/templates/maintenance-page.html.twig
index 3e41387f80d548a491d8db351909f1d63ff7172a..c4eae0113a637eb98fe53097b38a830319137fbf 100644
--- a/core/themes/bartik/templates/maintenance-page.html.twig
+++ b/core/themes/bartik/templates/maintenance-page.html.twig
@@ -39,13 +39,7 @@
               <h1 class="title" id="page-title">{{ title }}</h1>
             {% endif %}
             {{ page.content }}
-            {% if messages %}
-              <div id="messages">
-                <div class="section clearfix">
-                  {{ messages }}
-                </div>
-              </div>
-            {% endif %}
+            {{ page.messages }}
           </section>
         </main>
       </div>
diff --git a/core/themes/bartik/templates/page.html.twig b/core/themes/bartik/templates/page.html.twig
index 8429b447189b9cd7600b3dfe0f6715c1f360ed3f..04ffe94c576bc813a484338259b96de51b5a96aa 100644
--- a/core/themes/bartik/templates/page.html.twig
+++ b/core/themes/bartik/templates/page.html.twig
@@ -38,7 +38,6 @@
  * - title: The page title, for use in the actual content.
  * - title_suffix: Additional output populated by modules, intended to be
  *   displayed after the main title tag that appears in the template.
- * - messages: Status and error messages. Should be displayed prominently.
  * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the
  *   view and edit tabs when displaying a node).
  * - action_links: Actions local to the page, such as "Add menu" on the menu
@@ -50,6 +49,7 @@
  *
  * Regions:
  * - page.header: Items for the header region.
+ * - page.messages: Status and error messages. Should be displayed prominently.
  * - page.primary_menu: Items for the primary menu region.
  * - page.secondary_menu: Items for the secondary menu region.
  * - page.featured_top: Items for the featured top region.
@@ -109,11 +109,7 @@
         {{ page.primary_menu }}
       </div>
     </header>
-    {% if messages %}
-      <div id="messages">
-        <div class="section clearfix">{{ messages }}</div>
-      </div>
-    {% endif %}
+    {{ page.messages }}
     {% if page.featured_top %}
       <div class="featured-top">
         <aside class="featured-top__inner clearfix" role="complementary">
diff --git a/core/themes/bartik/templates/status-messages.html.twig b/core/themes/bartik/templates/status-messages.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..a3b5d49bedf8bfb19d1551cbab65b0afe40d3344
--- /dev/null
+++ b/core/themes/bartik/templates/status-messages.html.twig
@@ -0,0 +1,33 @@
+{% extends "@classy/misc/status-messages.html.twig" %}
+{#
+/**
+ * @file
+ * Default theme implementation for status messages.
+ *
+ * Displays status, error, and warning messages, grouped by type.
+ *
+ * An invisible heading identifies the messages for assistive technology.
+ * Sighted users see a colored box. See http://www.w3.org/TR/WCAG-TECHS/H69.html
+ * for info.
+ *
+ * Add an ARIA label to the contentinfo area so that assistive technology
+ * user agents will better describe this landmark.
+ *
+ * Available variables:
+ * - message_list: List of messages to be displayed, grouped by type.
+ * - status_headings: List of all status types.
+ * - display: (optional) May have a value of 'status' or 'error' when only
+ *   displaying messages of that specific type.
+ *
+ * @see template_preprocess_status_messages()
+ */
+#}
+{% block messages %}
+  {% if message_list is not empty %}
+    <div id="messages">
+      <div class="section clearfix">
+        {{ parent() }}
+      </div>
+    </div>
+  {% endif %}
+{% endblock messages %}
diff --git a/core/themes/classy/templates/layout/maintenance-page.html.twig b/core/themes/classy/templates/layout/maintenance-page.html.twig
index fd780d457e8db6259929892691675e0c884be805..eaae5ab4b0a9661474e31f1b8147e08db447d150 100644
--- a/core/themes/classy/templates/layout/maintenance-page.html.twig
+++ b/core/themes/classy/templates/layout/maintenance-page.html.twig
@@ -41,7 +41,7 @@
       <h1>{{ title }}</h1>
     {% endif %}
 
-    {{ messages }}
+    {{ page.messages }}
 
     {{ page.content }}
   </main>
diff --git a/core/themes/classy/templates/layout/page.html.twig b/core/themes/classy/templates/layout/page.html.twig
index 627d7c7aeff604ba5ded5eab2a6abeb2cf24854d..437a11c10f52eedde4acfca9b0a1337d6cd36706 100644
--- a/core/themes/classy/templates/layout/page.html.twig
+++ b/core/themes/classy/templates/layout/page.html.twig
@@ -31,7 +31,6 @@
  * - title: The page title, for use in the actual content.
  * - title_suffix: Additional output populated by modules, intended to be
  *   displayed after the main title tag that appears in the template.
- * - messages: Status and error messages. Should be displayed prominently.
  * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the
  *   view and edit tabs when displaying a node).
  * - action_links: Actions local to the page, such as "Add menu" on the menu
@@ -43,6 +42,7 @@
  *
  * Regions:
  * - page.header: Items for the header region.
+ * - page.messages: Status and error messages. Should be displayed prominently.
  * - page.primary_menu: Items for the primary menu region.
  * - page.secondary_menu: Items for the secondary menu region.
  * - page.highlighted: Items for the highlighted content region.
@@ -96,7 +96,7 @@
 
   {{ page.breadcrumb }}
 
-  {{ messages }}
+  {{ page.messages }}
 
   {{ page.help }}
 
diff --git a/core/themes/classy/templates/misc/status-messages.html.twig b/core/themes/classy/templates/misc/status-messages.html.twig
index a2f36311ff8463fd0624eb008ee5625e8860c330..fb1ebe33de5a653c79b19ef6e9a145a836dd6f4e 100644
--- a/core/themes/classy/templates/misc/status-messages.html.twig
+++ b/core/themes/classy/templates/misc/status-messages.html.twig
@@ -25,6 +25,7 @@
  * @ingroup themeable
  */
 #}
+{% block messages %}
 {% for type, messages in message_list %}
   {%
     set classes = [
@@ -55,3 +56,4 @@
   {# Remove type specific classes. #}
   {{ attributes.removeClass(classes) }}
 {% endfor %}
+{% endblock messages %}
diff --git a/core/themes/seven/seven.info.yml b/core/themes/seven/seven.info.yml
index 945ceacd95fbcc94f7baad003f77b2c6fb01f0be..0f5248c84fc46671100b0d998ea85a94c5cddc44 100644
--- a/core/themes/seven/seven.info.yml
+++ b/core/themes/seven/seven.info.yml
@@ -19,6 +19,7 @@ quickedit_stylesheets:
   - css/components/quickedit.css
 regions:
   content: Content
+  messages: Messages
   help: Help
   page_top: 'Page top'
   page_bottom: 'Page bottom'
diff --git a/core/themes/seven/templates/install-page.html.twig b/core/themes/seven/templates/install-page.html.twig
index 9f4db8d39142c8052b38d9a6a048486c8bfaac77..0e22c947a25490e27067e0b7c63a2a6e5a882c03 100644
--- a/core/themes/seven/templates/install-page.html.twig
+++ b/core/themes/seven/templates/install-page.html.twig
@@ -27,7 +27,7 @@
     {% if title %}
       <h1>{{ title }}</h1>
     {% endif %}
-    {{ messages }}
+    {{ page.messages }}
     {{ page.content }}
   </main>
 
diff --git a/core/themes/seven/templates/maintenance-page.html.twig b/core/themes/seven/templates/maintenance-page.html.twig
index f2bc53be302e76eb9c91663a47c5eaef0d423755..1bfea5a738b47d8682c29829d7d11550997e30fb 100644
--- a/core/themes/seven/templates/maintenance-page.html.twig
+++ b/core/themes/seven/templates/maintenance-page.html.twig
@@ -27,7 +27,7 @@
     {% if title %}
       <h1>{{ title }}</h1>
     {% endif %}
-    {{ messages }}
+    {{ page.messages }}
     {{ page.content }}
   </main>
 
diff --git a/core/themes/seven/templates/page.html.twig b/core/themes/seven/templates/page.html.twig
index fc3c7d4a0e88c568e5ad7ece3e73be527d07e921..4110987f0f6818926d90bb86bc892adb272ebc94 100644
--- a/core/themes/seven/templates/page.html.twig
+++ b/core/themes/seven/templates/page.html.twig
@@ -32,7 +32,6 @@
  * - title: The page title, for use in the actual content.
  * - title_suffix: Additional output populated by modules, intended to be
  *   displayed after the main title tag that appears in the template.
- * - messages: Status and error messages. Should be displayed prominently.
  * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the
  *   view and edit tabs when displaying a node).
  * - action_links: Actions local to the page, such as "Add menu" on the menu
@@ -45,6 +44,7 @@
  * Regions:
  * - page.page_top: Items for the header region.
  * - page.highlighted: Items for the highlighted content region.
+ * - page.messages: Status and error messages. Should be displayed prominently.
  * - page.help: Dynamic help text, mostly for admin pages.
  * - page.content: The main content of the current page.
  * - page.sidebar_first: Items for the first sidebar.
@@ -79,7 +79,7 @@
 
     <main class="page-content clearfix" role="main">
       <div class="visually-hidden"><a id="main-content" tabindex="-1"></a></div>
-      {{ messages }}
+      {{ page.messages }}
       {% if page.help %}
         <div class="help">
           {{ page.help }}