diff --git a/core/modules/layout_discovery/layout_discovery.module b/core/modules/layout_discovery/layout_discovery.module
index 3cffee9..31d8a72 100644
--- a/core/modules/layout_discovery/layout_discovery.module
+++ b/core/modules/layout_discovery/layout_discovery.module
@@ -5,6 +5,9 @@
  * Provides hook implementations for Layout Discovery.
  */
 
+use Drupal\Core\Extension\Extension;
+use Drupal\Core\Render\Element;
+
 /**
  * Implements hook_help().
  */
@@ -37,3 +40,64 @@ function template_preprocess_layout(&$variables) {
   $variables['settings'] = isset($variables['content']['#settings']) ? $variables['content']['#settings'] : [];
   $variables['layout'] = isset($variables['content']['#layout']) ? $variables['content']['#layout'] : [];
 }
+
+/**
+ * Implements hook_system_info_alter().
+ */
+function layout_discovery_system_info_alter(array &$info, Extension $file, $type) {
+  if ($file->getType() === 'theme' && _layout_discovery_has_layout($file->getName())) {
+    $layout_definition = \Drupal::service('plugin.manager.core.layout')->getDefinition($file->getName());
+    unset($info['regions'], $info['regions_hidden']);
+    foreach ($layout_definition->getRegions() as $region => $region_info) {
+      $info['regions'][$region] = (string) $region_info['label'];
+      if (isset($region_info['hidden'])) {
+        $info['regions_hidden'][] = $region;
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_element_info_alter().
+ */
+function layout_discovery_element_info_alter(array &$info) {
+  if (isset($info['page'])) {
+    $info['page']['#pre_render'][] = 'layout_discovery_page_pre_render';
+  }
+}
+
+/**
+ * #pre_render callback: Sets the #theme and #attached library for a layout.
+ */
+function layout_discovery_page_pre_render($element) {
+  $theme = \Drupal::theme()->getActiveTheme()->getName();
+  if (_layout_discovery_has_layout($theme)) {
+    $regions = [];
+    foreach (Element::children($element) as $name) {
+      // Move the item from the top-level of $build into a region-specific
+      // section.
+      // @todo Ideally the array structure would remain unchanged, see
+      //   https://www.drupal.org/node/2846393.
+      $regions[$name] = $element[$name];
+      unset($element[$name]);
+    }
+
+    $layout = \Drupal::service('plugin.manager.core.layout')->createInstance($theme);
+    $element['_layout'] = $layout->build($regions);
+    unset($element['#type'], $element['#theme']);
+  }
+  return $element;
+}
+
+/**
+ * Determines if a theme provides a layout.
+ *
+ * @param string $theme
+ *   The machine name of a theme.
+ *
+ * @return bool
+ *   TRUE if the theme has a layout, FALSE otherwise.
+ */
+function _layout_discovery_has_layout($theme) {
+  return (bool) \Drupal::service('plugin.manager.core.layout')->getDefinition($theme, FALSE);
+}
diff --git a/core/modules/layout_discovery/tests/modules/layout_discovery_theme_test/layout_discovery_theme_test.info.yml b/core/modules/layout_discovery/tests/modules/layout_discovery_theme_test/layout_discovery_theme_test.info.yml
new file mode 100644
index 0000000..a8782fc
--- /dev/null
+++ b/core/modules/layout_discovery/tests/modules/layout_discovery_theme_test/layout_discovery_theme_test.info.yml
@@ -0,0 +1,8 @@
+name: 'Layout Discovery theme test'
+type: module
+description: 'Support module for testing theme layouts.'
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+  - layout_discovery
diff --git a/core/modules/layout_discovery/tests/modules/layout_discovery_theme_test/layout_discovery_theme_test.layouts.yml b/core/modules/layout_discovery/tests/modules/layout_discovery_theme_test/layout_discovery_theme_test.layouts.yml
new file mode 100644
index 0000000..0bd1857
--- /dev/null
+++ b/core/modules/layout_discovery/tests/modules/layout_discovery_theme_test/layout_discovery_theme_test.layouts.yml
@@ -0,0 +1,30 @@
+# @todo Move to /core/themes/seven/seven.layouts.yml once the layout system is
+#   not experimental.
+seven:
+  label: 'Seven'
+  theme_hook: page
+  library: seven/global-styling
+  category: 'Theme-specific'
+  default_region: content
+  regions:
+    header:
+      label: Header
+    pre_content:
+      label: Pre-Content
+    breadcrumb:
+      label: Breadcrumb
+    highlighted:
+      label: Highlighted
+    help:
+      label: Help
+    content:
+      label: Content
+    page_top:
+      label: 'Page top'
+      hidden: true
+    page_bottom:
+      label: 'Page bottom'
+      hidden: true
+    sidebar_first:
+      label: 'Sidebar first'
+      hidden: true
diff --git a/core/modules/layout_discovery/tests/modules/layout_discovery_theme_test/layout_discovery_theme_test.module b/core/modules/layout_discovery/tests/modules/layout_discovery_theme_test/layout_discovery_theme_test.module
new file mode 100644
index 0000000..c5497a3
--- /dev/null
+++ b/core/modules/layout_discovery/tests/modules/layout_discovery_theme_test/layout_discovery_theme_test.module
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * Provides hook implementations for layout_discovery_theme_test.
+ */
+
+/**
+ * Implements hook_layout_alter().
+ */
+function layout_discovery_theme_test_layout_alter(&$definitions) {
+  /** @var \Drupal\Core\Layout\LayoutDefinition[] $definitions */
+  if (isset($definitions['seven'])) {
+    $regions = $definitions['seven']->getRegions();
+    $regions['test_region'] = ['label' => 'Test region'];
+    $definitions['seven']->setRegions($regions);
+  }
+}
diff --git a/core/modules/layout_discovery/tests/src/Functional/ThemeLayoutTest.php b/core/modules/layout_discovery/tests/src/Functional/ThemeLayoutTest.php
new file mode 100644
index 0000000..e4dd391
--- /dev/null
+++ b/core/modules/layout_discovery/tests/src/Functional/ThemeLayoutTest.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Drupal\Tests\layout_discovery\Functional;
+
+use Drupal\Core\Url;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests that Layout API integration for themes takes precedence over .info.yml.
+ *
+ * @group layout_discovery
+ */
+class ThemeLayoutTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['block', 'layout_discovery_theme_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->container->get('theme_installer')->install(['seven']);
+    $this->container->get('theme_handler')->setDefault('seven');
+
+    $this->drupalLogin($this->drupalCreateUser(['administer blocks']));
+
+    $this->placeBlock('system_branding_block', ['region' => 'header', 'id' => 'branding']);
+    $this->placeBlock('system_messages_block', ['region' => 'help', 'id' => 'messages']);
+    $this->placeBlock('system_main_block', ['region' => 'content', 'id' => 'main']);
+    $this->placeBlock('system_powered_by_block', ['region' => 'test_region', 'id' => 'powered_by']);
+  }
+
+  /**
+   * Tests that the layout is used.
+   */
+  public function testLayout() {
+    $this->drupalGet(Url::fromRoute('block.admin_display_theme', ['theme' => 'seven']));
+
+    // Assert that the standard regions for Seven theme are present.
+    $this->assertSession()->pageTextContains('Header');
+    $this->assertSession()->pageTextContains('Pre-content');
+    $this->assertSession()->pageTextContains('Breadcrumb');
+    $this->assertSession()->pageTextContains('Highlighted');
+    $this->assertSession()->pageTextContains('Help');
+    $this->assertSession()->pageTextContains('Content');
+    // Assert the region added by layout_discovery_theme_test_layout_alter() is
+    // present.
+    $this->assertSession()->pageTextContains('Test region');
+
+    $this->assertSame('header', $this->assertSession()->selectExists('blocks[branding][region]')->getValue());
+    $this->assertSame('help', $this->assertSession()->selectExists('blocks[messages][region]')->getValue());
+    $this->assertSame('content', $this->assertSession()->selectExists('blocks[main][region]')->getValue());
+    $this->assertSame('test_region', $this->assertSession()->selectExists('blocks[powered_by][region]')->getValue());
+  }
+
+}
