diff --git a/core/lib/Drupal/Core/Layout/Annotation/Layout.php b/core/lib/Drupal/Core/Layout/Annotation/Layout.php
index d2072c2..725aa43 100644
--- a/core/lib/Drupal/Core/Layout/Annotation/Layout.php
+++ b/core/lib/Drupal/Core/Layout/Annotation/Layout.php
@@ -112,6 +112,13 @@ class Layout extends Plugin {
   public $icon;
 
   /**
+   * The icon map.
+   *
+   * @var array optional
+   */
+  public $icon_map;
+
+  /**
    * An associative array of regions in this layout.
    *
    * The key of the array is the machine name of the region, and the value is
diff --git a/core/lib/Drupal/Core/Layout/IconGenerator.php b/core/lib/Drupal/Core/Layout/IconGenerator.php
new file mode 100644
index 0000000..1daa0b0
--- /dev/null
+++ b/core/lib/Drupal/Core/Layout/IconGenerator.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Drupal\Core\Layout;
+
+/**
+ * Generates layout icons from well-formed config.
+ */
+class IconGenerator implements IconGeneratorInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function generateSvgFromIconMap(array $icon_map, $width = 250, $height = 300, $stroke_width = 2, $padding = 5, $fill = 'lightgray', $stroke = 'black') {
+    $build = [
+      '#type' => 'html_tag',
+      '#tag' => 'svg',
+      '#attributes' => [
+        'width' => $width,
+        'height' => $height,
+      ],
+    ];
+
+    $region_rects = [];
+    $num_rows = count($icon_map);
+    foreach ($icon_map as $row => $cols) {
+      $num_cols = count($cols);
+      foreach ($cols as $col => $region) {
+        if (!isset($region_rects[$region])) {
+          // The first instance of a region is always the starting point.
+          $x = $col * ($width / $num_cols);
+          $y = ($row / $num_rows) * $height;
+          $region_rects[$region] = [
+            'x' => $x,
+            'y' => $y,
+            'width' => ($width / $num_cols) - $padding,
+            'height' => ($height / $num_rows) - $padding,
+            'last_col' => $col,
+            'last_row' => $row,
+          ];
+        }
+        else {
+          // Only increase the width/height if we've moved in that direction.
+          if ($region_rects[$region]['last_col'] != $col) {
+            $region_rects[$region]['width'] += ($width / $num_cols);
+            $region_rects[$region]['last_col'] = $col;
+          }
+          if ($region_rects[$region]['last_row'] != $row) {
+            $region_rects[$region]['height'] += ($height / $num_rows);
+            $region_rects[$region]['last_row'] = $row;
+          }
+        }
+      }
+    }
+
+    // Append each polygon to the SVG.
+    foreach ($region_rects as $region => $attributes) {
+      // Group our regions allows for metadata, nested elements, and tooltips.
+      $build[$region] = [
+        '#type' => 'html_tag',
+        '#tag' => 'g',
+      ];
+
+      $build[$region]['title'] = [
+        '#type' => 'html_tag',
+        '#tag' => 'title',
+        '#value' => $region,
+      ];
+
+      // Assemble the rectangle SVG element.
+      $build[$region]['rect'] = [
+        '#type' => 'html_tag',
+        '#tag' => 'rect',
+        '#attributes' => [
+          'x' => $attributes['x'],
+          'y' => $attributes['y'],
+          'width' => $attributes['width'],
+          'height' => $attributes['height'],
+          'fill' => $fill,
+          'stroke' => $stroke,
+          'stroke-width' => $stroke_width,
+        ],
+      ];
+    }
+
+    return $build;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Layout/IconGeneratorInterface.php b/core/lib/Drupal/Core/Layout/IconGeneratorInterface.php
new file mode 100644
index 0000000..711f8c9
--- /dev/null
+++ b/core/lib/Drupal/Core/Layout/IconGeneratorInterface.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\Core\Layout;
+
+/**
+ * Provides an interface for generating layout icons from well-formed config.
+ */
+interface IconGeneratorInterface {
+
+  /**
+   * Generates a SVG based on a Layout's icon map.
+   *
+   * @param array $icon_map
+   *   A two dimensional array representing the visual output of the layout.
+   *   For the following shape:
+   *   |------------------------------|
+   *   |                              |
+   *   |             100%             |
+   *   |                              |
+   *   |-------|--------------|-------|
+   *   |       |              |       |
+   *   |  25%  |      50%     |  25%  |
+   *   |       |              |       |
+   *   |-------|--------------|-------|
+   *   |                              |
+   *   |             100%             |
+   *   |                              |
+   *   |------------------------------|
+   *   The corresponding array would be:
+   *   - [top]
+   *   - [first, second, second, third]
+   *   - [bottom].
+   * @param int $width
+   *   (optional) The width of the generated SVG. Defaults to 250.
+   * @param int $height
+   *   (optional) The height of the generated SVG. Defaults to 300.
+   * @param int $stroke_width
+   *   (optional) The width of region borders. Defaults to 2.
+   * @param int $padding
+   *   (optional) The padding between regions. Any value above 0 is valid.
+   *   Defaults to 5.
+   * @param string $fill
+   *   (optional) The fill color of regions. Defaults to 'lightgray'.
+   * @param string $stroke
+   *   (optional) The color of region borders. Defaults to 'black'.
+   *
+   * @return array
+   *   A render array representing a SVG icon.
+   */
+  public function generateSvgFromIconMap(array $icon_map, $width = 250, $height = 300, $stroke_width = 2, $padding = 5, $fill = 'lightgray', $stroke = 'black');
+
+}
diff --git a/core/lib/Drupal/Core/Layout/LayoutDefinition.php b/core/lib/Drupal/Core/Layout/LayoutDefinition.php
index c804776..cc452a7 100644
--- a/core/lib/Drupal/Core/Layout/LayoutDefinition.php
+++ b/core/lib/Drupal/Core/Layout/LayoutDefinition.php
@@ -86,6 +86,15 @@ class LayoutDefinition extends PluginDefinition implements PluginDefinitionInter
   protected $icon;
 
   /**
+   * An array defining the regions of a layout.
+   *
+   * @var array|null
+   *
+   * @see \Drupal\Core\Layout\IconGeneratorInterface::generateSvgFromIconMap()
+   */
+  protected $icon_map;
+
+  /**
    * An associative array of regions in this layout.
    *
    * The key of the array is the machine name of the region, and the value is
@@ -372,6 +381,77 @@ public function setIconPath($icon) {
   }
 
   /**
+   * Gets the icon map for this layout definition.
+   *
+   * This should not be used if an icon path is specified. See ::getIcon().
+   *
+   * @return array|null
+   *   The icon map, if it exists.
+   */
+  public function getIconMap() {
+    return $this->icon_map;
+  }
+
+  /**
+   * Sets the icon map for this layout definition.
+   *
+   * @param array|null $icon_map
+   *   The icon map.
+   *
+   * @return $this
+   */
+  public function setIconMap($icon_map) {
+    $this->icon_map = $icon_map;
+    return $this;
+  }
+
+  /**
+   * Builds a render array for an icon representing the layout.
+   *
+   * @param int $width
+   *   (optional) The width of the icon. Defaults to 250.
+   * @param int $height
+   *   (optional) The height of the icon. Defaults to 300.
+   * @param int $stroke_width
+   *   (optional) If a generated SVG is used, the width of region borders.
+   *   Defaults to 2.
+   * @param int $padding
+   *   (optional) If a generated SVG is used, the padding between regions. Any
+   *   value above 0 is valid. Defaults to 5.
+   * @param string $fill
+   *   (optional) If a generated SVG is used, the fill color of regions.
+   *   Defaults to 'lightgray'.
+   * @param string $stroke
+   *   (optional) If a generated SVG is used, the color of region borders.
+   *   Defaults to 'black'.
+   *
+   * @return array
+   *   A render array for the icon.
+   */
+  public function getIcon($width = 250, $height = 300, $stroke_width = 2, $padding = 5, $fill = 'lightgray', $stroke = 'black') {
+    $icon = [];
+    if ($icon_path = $this->getIconPath()) {
+      $icon = [
+        '#theme' => 'image',
+        '#uri' => $icon_path,
+        '#width' => $width,
+        '#height' => $height,
+      ];
+    }
+    elseif ($icon_map = $this->getIconMap()) {
+      $icon = $this->getIconGenerator()->generateSvgFromIconMap($icon_map, $width, $height, $stroke_width, $padding, $fill, $stroke);
+    }
+    return $icon;
+  }
+
+  /**
+   * @return \Drupal\Core\Layout\IconGenerator
+   */
+  protected function getIconGenerator() {
+    return \Drupal::service('layout.icon_generator');
+  }
+
+  /**
    * Gets the regions for this layout definition.
    *
    * @return array[]
diff --git a/core/modules/field_layout/src/Form/FieldLayoutEntityDisplayFormTrait.php b/core/modules/field_layout/src/Form/FieldLayoutEntityDisplayFormTrait.php
index 043e5c7..170bcd0 100644
--- a/core/modules/field_layout/src/Form/FieldLayoutEntityDisplayFormTrait.php
+++ b/core/modules/field_layout/src/Form/FieldLayoutEntityDisplayFormTrait.php
@@ -87,6 +87,8 @@ public function form(array $form, FormStateInterface $form_state) {
       '#tree' => TRUE,
     ];
 
+    $form['field_layouts']['settings_wrapper']['icon'] = $layout_plugin->getPluginDefinition()->getIcon();
+
     if ($layout_plugin instanceof PluginFormInterface) {
       $form['field_layouts']['settings_wrapper']['layout_settings'] = [];
       $subform_state = SubformState::createForSubform($form['field_layouts']['settings_wrapper']['layout_settings'], $form, $form_state);
diff --git a/core/modules/layout_discovery/layout_discovery.layouts.yml b/core/modules/layout_discovery/layout_discovery.layouts.yml
index d1b0e5a..755a96b 100644
--- a/core/modules/layout_discovery/layout_discovery.layouts.yml
+++ b/core/modules/layout_discovery/layout_discovery.layouts.yml
@@ -5,6 +5,8 @@ layout_onecol:
   library: layout_discovery/onecol
   category: 'Columns: 1'
   default_region: content
+  icon_map:
+    - [content]
   regions:
     content:
       label: Content
@@ -16,6 +18,10 @@ layout_twocol:
   library: layout_discovery/twocol
   category: 'Columns: 2'
   default_region: first
+  icon_map:
+    - [top]
+    - [first, second]
+    - [bottom]
   regions:
     top:
       label: Top
@@ -33,6 +39,12 @@ layout_twocol_bricks:
   library: layout_discovery/twocol_bricks
   category: 'Columns: 2'
   default_region: middle
+  icon_map:
+    - [top]
+    - [first_above, second_above]
+    - [middle]
+    - [first_below, second_below]
+    - [bottom]
   regions:
     top:
       label: Top
@@ -56,6 +68,10 @@ layout_threecol_25_50_25:
   library: layout_discovery/threecol_25_50_25
   category: 'Columns: 3'
   default_region: second
+  icon_map:
+    - [top]
+    - [first, second, second, third]
+    - [bottom]
   regions:
     top:
       label: Top
@@ -75,6 +91,10 @@ layout_threecol_33_34_33:
   library: layout_discovery/threecol_33_34_33
   category: 'Columns: 3'
   default_region: first
+  icon_map:
+    - [top]
+    - [first, second, third]
+    - [bottom]
   regions:
     top:
       label: Top
diff --git a/core/modules/layout_discovery/layout_discovery.services.yml b/core/modules/layout_discovery/layout_discovery.services.yml
index 1e24db4..6bb4073 100644
--- a/core/modules/layout_discovery/layout_discovery.services.yml
+++ b/core/modules/layout_discovery/layout_discovery.services.yml
@@ -2,3 +2,5 @@ services:
   plugin.manager.core.layout:
     class: Drupal\Core\Layout\LayoutPluginManager
     arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@theme_handler']
+  layout.icon_generator:
+    class: Drupal\Core\Layout\IconGenerator
diff --git a/core/tests/Drupal/KernelTests/Core/Layout/IconGeneratorTest.php b/core/tests/Drupal/KernelTests/Core/Layout/IconGeneratorTest.php
new file mode 100644
index 0000000..bc69478
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Layout/IconGeneratorTest.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\KernelTests\Core\Layout;
+
+use Drupal\Core\Layout\IconGenerator;
+use Drupal\Core\Render\RenderContext;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Layout\IconGenerator
+ * @group Layout
+ */
+class IconGeneratorTest extends KernelTestBase {
+
+  /**
+   * @covers ::generateSvgFromIconMap
+   *
+   * @dataProvider providerTestGenerateSvgFromIconMap
+   */
+  public function testGenerateSvgFromIconMap($icon_map, $expected) {
+    $renderer = $this->container->get('renderer');
+    $icon_generator = new IconGenerator();
+    $build = $icon_generator->generateSvgFromIconMap($icon_map);
+    $output = (string) $renderer->executeInRenderContext(new RenderContext(), function () use ($build, $renderer) {
+      return $renderer->render($build);
+    });
+    $this->assertSame($expected, $output);
+  }
+
+  public function providerTestGenerateSvgFromIconMap() {
+    $data = [];
+    $data['empty'][] = [];
+    $data['empty'][] = <<<'EOD'
+<svg width="250" height="300"></svg>
+
+EOD;
+
+    $data['two_column'][] = [['left', 'right']];
+    $data['two_column'][] = <<<'EOD'
+<svg width="250" height="300"><g><title>left</title>
+<rect x="0" y="0" width="120" height="295" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+<g><title>right</title>
+<rect x="125" y="0" width="120" height="295" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+</svg>
+
+EOD;
+
+    $data['stacked'][] = [
+      ['sidebar', 'top', 'top'],
+      ['sidebar', 'left', 'right'],
+      ['sidebar', 'middle', 'middle'],
+      ['footer_left', 'footer_right'],
+      ['footer_full'],
+    ];
+    $data['stacked'][] = <<<'EOD'
+<svg width="250" height="300"><g><title>sidebar</title>
+<rect x="0" y="0" width="78.333333333333" height="175" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+<g><title>top</title>
+<rect x="83.333333333333" y="0" width="161.66666666667" height="55" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+<g><title>left</title>
+<rect x="83.333333333333" y="60" width="78.333333333333" height="55" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+<g><title>right</title>
+<rect x="166.66666666667" y="60" width="78.333333333333" height="55" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+<g><title>middle</title>
+<rect x="83.333333333333" y="120" width="161.66666666667" height="55" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+<g><title>footer_left</title>
+<rect x="0" y="180" width="120" height="55" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+<g><title>footer_right</title>
+<rect x="125" y="180" width="120" height="55" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+<g><title>footer_full</title>
+<rect x="0" y="240" width="245" height="55" fill="lightgray" stroke="black" stroke-width="2" />
+</g>
+</svg>
+
+EOD;
+
+    return $data;
+  }
+
+}
