diff --git a/core/modules/layout_builder/src/Controller/AddSectionController.php b/core/modules/layout_builder/src/Controller/AddSectionController.php
index d6771082382bfae78569af177548887e8fa7d28c..fa522b547224a920eacfc60b3c279033242ac3e7 100644
--- a/core/modules/layout_builder/src/Controller/AddSectionController.php
+++ b/core/modules/layout_builder/src/Controller/AddSectionController.php
@@ -6,6 +6,7 @@
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
+use Drupal\layout_builder\Section;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 
@@ -63,13 +64,9 @@ public static function create(ContainerInterface $container) {
    *   The controller response.
    */
   public function build(EntityInterface $entity, $delta, $plugin_id) {
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
+    /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
     $field_list = $entity->layout_builder__layout;
-    $field_list->addItem($delta, [
-      'layout' => $plugin_id,
-      'layout_settings' => [],
-      'section' => [],
-    ]);
+    $field_list->insertSection($delta, new Section($plugin_id));
 
     $this->layoutTempstoreRepository->set($entity);
 
diff --git a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
index a4163a40a82151106eeca5b3abcf1fe08e79c44e..959ce1d5b414b2df9a9e38198633e44169ede408 100644
--- a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
+++ b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
@@ -10,8 +10,8 @@
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\Url;
 use Drupal\layout_builder\LayoutSectionBuilder;
-use Drupal\layout_builder\Field\LayoutSectionItemInterface;
 use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
+use Drupal\layout_builder\Section;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 
@@ -111,20 +111,20 @@ public function layout(EntityInterface $entity, $is_rebuilding = FALSE) {
     $entity_id = $entity->id();
     $entity_type_id = $entity->getEntityTypeId();
 
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
+    /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
     $field_list = $entity->layout_builder__layout;
 
     // For a new layout override, begin with a single section of one column.
-    if (!$is_rebuilding && $field_list->isEmpty()) {
-      $field_list->addItem(0, ['layout' => 'layout_onecol']);
+    if (!$is_rebuilding && $field_list->count() === 0) {
+      $field_list->appendSection(new Section('layout_onecol'));
       $this->layoutTempstoreRepository->set($entity);
     }
 
     $output = [];
     $count = 0;
-    foreach ($field_list as $item) {
+    foreach ($field_list->getSections() as $section) {
       $output[] = $this->buildAddSectionLink($entity_type_id, $entity_id, $count);
-      $output[] = $this->buildAdministrativeSection($item, $entity, $count);
+      $output[] = $this->buildAdministrativeSection($section, $entity, $count);
       $count++;
     }
     $output[] = $this->buildAddSectionLink($entity_type_id, $entity_id, $count);
@@ -179,8 +179,8 @@ protected function buildAddSectionLink($entity_type_id, $entity_id, $delta) {
   /**
    * Builds the render array for the layout section while editing.
    *
-   * @param \Drupal\layout_builder\Field\LayoutSectionItemInterface $item
-   *   The layout section item.
+   * @param \Drupal\layout_builder\Section $section
+   *   The layout section.
    * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity.
    * @param int $delta
@@ -189,12 +189,12 @@ protected function buildAddSectionLink($entity_type_id, $entity_id, $delta) {
    * @return array
    *   The render array for a given section.
    */
-  protected function buildAdministrativeSection(LayoutSectionItemInterface $item, EntityInterface $entity, $delta) {
+  protected function buildAdministrativeSection(Section $section, EntityInterface $entity, $delta) {
     $entity_type_id = $entity->getEntityTypeId();
     $entity_id = $entity->id();
 
-    $layout = $this->layoutManager->createInstance($item->layout, $item->layout_settings);
-    $build = $this->builder->buildSectionFromLayout($layout, $item->section);
+    $layout = $section->getLayout();
+    $build = $section->toRenderArray();
     $layout_definition = $layout->getPluginDefinition();
 
     foreach ($layout_definition->getRegions() as $region => $info) {
diff --git a/core/modules/layout_builder/src/Controller/MoveBlockController.php b/core/modules/layout_builder/src/Controller/MoveBlockController.php
index d648416d9e418d0e4c94740197937ca60cf8302d..7a841a252d77cc272630bc5507815b5ad7c16c7f 100644
--- a/core/modules/layout_builder/src/Controller/MoveBlockController.php
+++ b/core/modules/layout_builder/src/Controller/MoveBlockController.php
@@ -69,32 +69,29 @@ public static function create(ContainerInterface $container) {
    *   An AJAX response.
    */
   public function build(EntityInterface $entity, $delta_from, $delta_to, $region_from, $region_to, $block_uuid, $preceding_block_uuid = NULL) {
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
-    $field = $entity->layout_builder__layout->get($delta_from);
-    $section = $field->getSection();
+    /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+    $field_list = $entity->layout_builder__layout;
+    $section = $field_list->getSection($delta_from);
 
-    $block = $section->getBlock($region_from, $block_uuid);
-    $section->removeBlock($region_from, $block_uuid);
+    $component = $section->getComponent($block_uuid);
+    $section->removeComponent($block_uuid);
 
     // If the block is moving from one section to another, update the original
     // section and load the new one.
     if ($delta_from !== $delta_to) {
-      $field->updateFromSection($section);
-      $field = $entity->layout_builder__layout->get($delta_to);
-      $section = $field->getSection();
+      $section = $field_list->getSection($delta_to);
     }
 
     // If a preceding block was specified, insert after that. Otherwise add the
     // block to the front.
+    $component->setRegion($region_to);
     if (isset($preceding_block_uuid)) {
-      $section->insertBlock($region_to, $block_uuid, $block, $preceding_block_uuid);
+      $section->insertAfterComponent($preceding_block_uuid, $component);
     }
     else {
-      $section->addBlock($region_to, $block_uuid, $block);
+      $section->appendComponent($component);
     }
 
-    $field->updateFromSection($section);
-
     $this->layoutTempstoreRepository->set($entity);
     return $this->rebuildLayout($entity);
   }
diff --git a/core/modules/layout_builder/src/Field/LayoutSectionItemInterface.php b/core/modules/layout_builder/src/Field/LayoutSectionItemInterface.php
deleted file mode 100644
index 786b6b4b19acd4f932d7d64c922a46ba99b37613..0000000000000000000000000000000000000000
--- a/core/modules/layout_builder/src/Field/LayoutSectionItemInterface.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-namespace Drupal\layout_builder\Field;
-
-use Drupal\Core\Field\FieldItemInterface;
-use Drupal\layout_builder\Section;
-
-/**
- * Defines an interface for the layout section field item.
- *
- * @internal
- *   Layout Builder is currently experimental and should only be leveraged by
- *   experimental modules and development releases of contributed modules.
- *   See https://www.drupal.org/core/experimental for more information.
- *
- * @property string layout
- * @property array[] layout_settings
- * @property array[] section
- */
-interface LayoutSectionItemInterface extends FieldItemInterface {
-
-  /**
-   * Gets a domain object for the layout section.
-   *
-   * @return \Drupal\layout_builder\Section
-   *   The layout section.
-   */
-  public function getSection();
-
-  /**
-   * Updates the stored value based on the domain object.
-   *
-   * @param \Drupal\layout_builder\Section $section
-   *   The layout section.
-   *
-   * @return $this
-   */
-  public function updateFromSection(Section $section);
-
-}
diff --git a/core/modules/layout_builder/src/Field/LayoutSectionItemList.php b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
index 933ed6eb974e866bc32cdde910e0409e6e75d5e2..c9a6b71136124ccf2ee16a454f05cbcc8d6a1763 100644
--- a/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
+++ b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
@@ -3,6 +3,8 @@
 namespace Drupal\layout_builder\Field;
 
 use Drupal\Core\Field\FieldItemList;
+use Drupal\layout_builder\Section;
+use Drupal\layout_builder\SectionStorageInterface;
 
 /**
  * Defines a item list class for layout section fields.
@@ -11,22 +13,65 @@
  *
  * @see \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem
  */
-class LayoutSectionItemList extends FieldItemList implements LayoutSectionItemListInterface {
+class LayoutSectionItemList extends FieldItemList implements SectionStorageInterface {
 
   /**
    * {@inheritdoc}
    */
-  public function addItem($index, $value) {
-    if ($this->get($index)) {
-      $start = array_slice($this->list, 0, $index);
-      $end = array_slice($this->list, $index);
-      $item = $this->createItem($index, $value);
+  public function insertSection($delta, Section $section) {
+    if ($this->get($delta)) {
+      /** @var \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem $item */
+      $item = $this->createItem($delta);
+      $item->section = $section;
+
+      $start = array_slice($this->list, 0, $delta);
+      $end = array_slice($this->list, $delta);
       $this->list = array_merge($start, [$item], $end);
     }
     else {
-      $item = $this->appendItem($value);
+      $this->appendSection($section);
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function appendSection(Section $section) {
+    $this->appendItem()->section = $section;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSections() {
+    $sections = [];
+    /** @var \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem $item */
+    foreach ($this->list as $delta => $item) {
+      $sections[$delta] = $item->section;
     }
-    return $item;
+    return $sections;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSection($delta) {
+    /** @var \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem $item */
+    if (!$item = $this->get($delta)) {
+      throw new \OutOfBoundsException(sprintf('Invalid delta "%s" for the "%s" entity', $delta, $this->getEntity()->label()));
+    }
+
+    return $item->section;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeSection($delta) {
+    $this->removeItem($delta);
+    return $this;
   }
 
 }
diff --git a/core/modules/layout_builder/src/Field/LayoutSectionItemListInterface.php b/core/modules/layout_builder/src/Field/LayoutSectionItemListInterface.php
deleted file mode 100644
index 81839d17747b6975efe3912847cab60bcafafaf3..0000000000000000000000000000000000000000
--- a/core/modules/layout_builder/src/Field/LayoutSectionItemListInterface.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Drupal\layout_builder\Field;
-
-use Drupal\Core\Field\FieldItemListInterface;
-
-/**
- * Defines a item list class for layout section fields.
- *
- * @internal
- *   Layout Builder is currently experimental and should only be leveraged by
- *   experimental modules and development releases of contributed modules.
- *   See https://www.drupal.org/core/experimental for more information.
- *
- * @see \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem
- */
-interface LayoutSectionItemListInterface extends FieldItemListInterface {
-
-  /**
-   * {@inheritdoc}
-   *
-   * @return \Drupal\layout_builder\Field\LayoutSectionItemInterface|null
-   *   The layout section item, if it exists.
-   */
-  public function get($index);
-
-  /**
-   * Adds a new item to the list.
-   *
-   * If an item exists at the given index, the item at that position and others
-   * after it are shifted backward.
-   *
-   * @param int $index
-   *   The position of the item in the list.
-   * @param mixed $value
-   *   The value of the item to be stored at the specified position.
-   *
-   * @return \Drupal\Core\TypedData\TypedDataInterface
-   *   The item that was appended.
-   *
-   * @todo Move to \Drupal\Core\TypedData\ListInterface directly in
-   *   https://www.drupal.org/node/2907417.
-   */
-  public function addItem($index, $value);
-
-}
diff --git a/core/modules/layout_builder/src/Form/AddBlockForm.php b/core/modules/layout_builder/src/Form/AddBlockForm.php
index 1757606002c2b2a85209cb6fa65f24bf3e908453..83effd622661ae357b70a8122afd3192f0df89e7 100644
--- a/core/modules/layout_builder/src/Form/AddBlockForm.php
+++ b/core/modules/layout_builder/src/Form/AddBlockForm.php
@@ -3,6 +3,7 @@
 namespace Drupal\layout_builder\Form;
 
 use Drupal\layout_builder\Section;
+use Drupal\layout_builder\SectionComponent;
 
 /**
  * Provides a form to add a block.
@@ -29,7 +30,7 @@ protected function submitLabel() {
    * {@inheritdoc}
    */
   protected function submitBlock(Section $section, $region, $uuid, array $configuration) {
-    $section->addBlock($region, $uuid, $configuration);
+    $section->appendComponent(new SectionComponent($uuid, $region, $configuration));
   }
 
 }
diff --git a/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php b/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php
index 7356ef4ccdf18a0abc3670e0aad42ea1c0545694..08ff3174167c374ef2beb59af349ab65f04dbf77 100644
--- a/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php
+++ b/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php
@@ -246,11 +246,10 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
 
     $configuration = $this->block->getConfiguration();
 
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
-    $field = $this->entity->layout_builder__layout->get($this->delta);
-    $section = $field->getSection();
-    $this->submitBlock($section, $this->region, $configuration['uuid'], ['block' => $configuration]);
-    $field->updateFromSection($section);
+    /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+    $field_list = $this->entity->layout_builder__layout;
+    $section = $field_list->getSection($this->delta);
+    $this->submitBlock($section, $this->region, $configuration['uuid'], $configuration);
 
     $this->layoutTempstoreRepository->set($this->entity);
     $form_state->setRedirectUrl($this->entity->toUrl('layout-builder'));
diff --git a/core/modules/layout_builder/src/Form/ConfigureSectionForm.php b/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
index 17913237d51ddfe6b0beddc63a15ec0f8950e445..6993d218be50caf0131ebb607dcc0366c479dba9 100644
--- a/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
+++ b/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Plugin\PluginWithFormsInterface;
 use Drupal\layout_builder\Controller\LayoutRebuildTrait;
 use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
+use Drupal\layout_builder\Section;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -121,14 +122,15 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
     $this->delta = $delta;
     $this->isUpdate = is_null($plugin_id);
 
-    $configuration = [];
     if ($this->isUpdate) {
-      /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
-      $field = $this->entity->layout_builder__layout->get($this->delta);
-      $plugin_id = $field->layout;
-      $configuration = $field->layout_settings;
+      /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+      $field_list = $this->entity->layout_builder__layout;
+      $section = $field_list->getSection($this->delta);
     }
-    $this->layout = $this->layoutManager->createInstance($plugin_id, $configuration);
+    else {
+      $section = new Section($plugin_id);
+    }
+    $this->layout = $section->getLayout();
 
     $form['#tree'] = TRUE;
     $form['layout_settings'] = [];
@@ -166,19 +168,13 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $plugin_id = $this->layout->getPluginId();
     $configuration = $this->layout->getConfiguration();
 
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
+    /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
     $field_list = $this->entity->layout_builder__layout;
     if ($this->isUpdate) {
-      $field = $field_list->get($this->delta);
-      $field->layout = $plugin_id;
-      $field->layout_settings = $configuration;
+      $field_list->getSection($this->delta)->setLayoutSettings($configuration);
     }
     else {
-      $field_list->addItem($this->delta, [
-        'layout' => $plugin_id,
-        'layout_settings' => $configuration,
-        'section' => [],
-      ]);
+      $field_list->insertSection($this->delta, new Section($plugin_id, $configuration));
     }
 
     $this->layoutTempstoreRepository->set($this->entity);
diff --git a/core/modules/layout_builder/src/Form/RemoveBlockForm.php b/core/modules/layout_builder/src/Form/RemoveBlockForm.php
index 139186af66541b656648f3ae3a356fce7e3ca5b1..9c7eb2084dc529776630d10956e96a31af097930 100644
--- a/core/modules/layout_builder/src/Form/RemoveBlockForm.php
+++ b/core/modules/layout_builder/src/Form/RemoveBlockForm.php
@@ -60,11 +60,9 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
    * {@inheritdoc}
    */
   protected function handleEntity(EntityInterface $entity, FormStateInterface $form_state) {
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
-    $field = $entity->layout_builder__layout->get($this->delta);
-    $section = $field->getSection();
-    $section->removeBlock($this->region, $this->uuid);
-    $field->updateFromSection($section);
+    /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+    $field_list = $this->entity->layout_builder__layout;
+    $field_list->getSection($this->delta)->removeComponent($this->uuid);
   }
 
 }
diff --git a/core/modules/layout_builder/src/Form/UpdateBlockForm.php b/core/modules/layout_builder/src/Form/UpdateBlockForm.php
index 2f2aa600e44c7e08d5ed5cd5cf293cce3e372369..3cc36585a11aff0c5724f28506766ee683304f38 100644
--- a/core/modules/layout_builder/src/Form/UpdateBlockForm.php
+++ b/core/modules/layout_builder/src/Form/UpdateBlockForm.php
@@ -40,14 +40,11 @@ public function getFormId() {
    *   The form array.
    */
   public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $entity = NULL, $delta = NULL, $region = NULL, $uuid = NULL) {
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
-    $field = $entity->layout_builder__layout->get($delta);
-    $block = $field->getSection()->getBlock($region, $uuid);
-    if (empty($block['block']['id'])) {
-      throw new \InvalidArgumentException('Invalid UUID specified');
-    }
+    /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+    $field_list = $entity->layout_builder__layout;
+    $plugin = $field_list->getSection($delta)->getComponent($uuid)->getPlugin();
 
-    return parent::buildForm($form, $form_state, $entity, $delta, $region, $block['block']['id'], $block['block']);
+    return parent::buildForm($form, $form_state, $entity, $delta, $region, $plugin->getPluginId(), $plugin->getConfiguration());
   }
 
   /**
@@ -61,7 +58,7 @@ protected function submitLabel() {
    * {@inheritdoc}
    */
   protected function submitBlock(Section $section, $region, $uuid, array $configuration) {
-    $section->updateBlock($region, $uuid, $configuration);
+    $section->getComponent($uuid)->setConfiguration($configuration);
   }
 
 }
diff --git a/core/modules/layout_builder/src/LayoutSectionBuilder.php b/core/modules/layout_builder/src/LayoutSectionBuilder.php
index 1682974f1134533f61792cf627a14d964beac892..525849d9cef3c12f8d3ba2f7d537133351f55ea1 100644
--- a/core/modules/layout_builder/src/LayoutSectionBuilder.php
+++ b/core/modules/layout_builder/src/LayoutSectionBuilder.php
@@ -2,14 +2,11 @@
 
 namespace Drupal\layout_builder;
 
-use Drupal\Component\Plugin\Exception\PluginException;
 use Drupal\Core\Block\BlockManagerInterface;
-use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Layout\LayoutInterface;
 use Drupal\Core\Layout\LayoutPluginManagerInterface;
 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
 use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
-use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 
@@ -17,6 +14,8 @@
  * Builds the UI for layout sections.
  *
  * @internal
+ *
+ * @todo Remove in https://www.drupal.org/project/drupal/issues/2928450.
  */
 class LayoutSectionBuilder {
 
@@ -84,37 +83,21 @@ public function __construct(AccountInterface $account, LayoutPluginManagerInterf
    *
    * @param \Drupal\Core\Layout\LayoutInterface $layout
    *   The ID of the layout.
-   * @param array $section
-   *   An array of configuration, keyed first by region and then by block UUID.
+   * @param \Drupal\layout_builder\SectionComponent[] $components
+   *   An array of components.
    *
    * @return array
    *   The render array for a given section.
    */
-  public function buildSectionFromLayout(LayoutInterface $layout, array $section) {
-    $cacheability = CacheableMetadata::createFromRenderArray([]);
-
+  public function buildSectionFromLayout(LayoutInterface $layout, array $components) {
     $regions = [];
-    $weight = 0;
-    foreach ($section as $region => $blocks) {
-      if (!is_array($blocks)) {
-        throw new \InvalidArgumentException(sprintf('The "%s" region in the "%s" layout has invalid configuration', $region, $layout->getPluginId()));
-      }
-
-      foreach ($blocks as $uuid => $configuration) {
-        if (!is_array($configuration) || !isset($configuration['block'])) {
-          throw new \InvalidArgumentException(sprintf('The block with UUID of "%s" has invalid configuration', $uuid));
-        }
-
-        if ($block_output = $this->buildBlock($uuid, $configuration['block'], $cacheability)) {
-          $block_output['#weight'] = $weight++;
-          $regions[$region][$uuid] = $block_output;
-        }
+    foreach ($components as $component) {
+      if ($output = $component->toRenderArray()) {
+        $regions[$component->getRegion()][$component->getUuid()] = $output;
       }
     }
 
-    $result = $layout->build($regions);
-    $cacheability->applyTo($result);
-    return $result;
+    return $layout->build($regions);
   }
 
   /**
@@ -124,78 +107,15 @@ public function buildSectionFromLayout(LayoutInterface $layout, array $section)
    *   The ID of the layout.
    * @param array $layout_settings
    *   The configuration for the layout.
-   * @param array $section
-   *   An array of configuration, keyed first by region and then by block UUID.
+   * @param \Drupal\layout_builder\SectionComponent[] $components
+   *   An array of components.
    *
    * @return array
    *   The render array for a given section.
    */
-  public function buildSection($layout_id, array $layout_settings, array $section) {
+  public function buildSection($layout_id, array $layout_settings, array $components) {
     $layout = $this->layoutPluginManager->createInstance($layout_id, $layout_settings);
-    return $this->buildSectionFromLayout($layout, $section);
-  }
-
-  /**
-   * Builds the render array for a given block.
-   *
-   * @param string $uuid
-   *   The UUID of this block instance.
-   * @param array $configuration
-   *   An array of configuration relevant to the block instance. Must contain
-   *   the plugin ID with the key 'id'.
-   * @param \Drupal\Core\Cache\CacheableMetadata $cacheability
-   *   The cacheability metadata.
-   *
-   * @return array|null
-   *   The render array representing this block, if accessible. NULL otherwise.
-   */
-  protected function buildBlock($uuid, array $configuration, CacheableMetadata $cacheability) {
-    $block = $this->getBlock($uuid, $configuration);
-
-    $access = $block->access($this->account, TRUE);
-    $cacheability->addCacheableDependency($access);
-
-    $block_output = NULL;
-    if ($access->isAllowed()) {
-      $block_output = [
-        '#theme' => 'block',
-        '#configuration' => $block->getConfiguration(),
-        '#plugin_id' => $block->getPluginId(),
-        '#base_plugin_id' => $block->getBaseId(),
-        '#derivative_plugin_id' => $block->getDerivativeId(),
-        'content' => $block->build(),
-      ];
-      $cacheability->addCacheableDependency($block);
-    }
-    return $block_output;
-  }
-
-  /**
-   * Gets a block instance.
-   *
-   * @param string $uuid
-   *   The UUID of this block instance.
-   * @param array $configuration
-   *   An array of configuration relevant to the block instance. Must contain
-   *   the plugin ID with the key 'id'.
-   *
-   * @return \Drupal\Core\Block\BlockPluginInterface
-   *   The block instance.
-   *
-   * @throws \Drupal\Component\Plugin\Exception\PluginException
-   *   Thrown when the configuration parameter does not contain 'id'.
-   */
-  protected function getBlock($uuid, array $configuration) {
-    if (!isset($configuration['id'])) {
-      throw new PluginException(sprintf('No plugin ID specified for block with "%s" UUID', $uuid));
-    }
-
-    $block = $this->blockManager->createInstance($configuration['id'], $configuration);
-    if ($block instanceof ContextAwarePluginInterface) {
-      $contexts = $this->contextRepository->getRuntimeContexts(array_values($block->getContextMapping()));
-      $this->contextHandler->applyContextMapping($block, $contexts);
-    }
-    return $block;
+    return $this->buildSectionFromLayout($layout, $components);
   }
 
 }
diff --git a/core/modules/layout_builder/src/Plugin/DataType/SectionData.php b/core/modules/layout_builder/src/Plugin/DataType/SectionData.php
new file mode 100644
index 0000000000000000000000000000000000000000..353c53fe7cca8ad323c3ecc57c220fee253854ce
--- /dev/null
+++ b/core/modules/layout_builder/src/Plugin/DataType/SectionData.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\layout_builder\Plugin\DataType;
+
+use Drupal\Core\TypedData\TypedData;
+use Drupal\layout_builder\Section;
+
+/**
+ * Provides a data type wrapping \Drupal\layout_builder\Section.
+ *
+ * @DataType(
+ *   id = "layout_section",
+ *   label = @Translation("Layout Section"),
+ *   description = @Translation("A layout section"),
+ * )
+ */
+class SectionData extends TypedData {
+
+  /**
+   * The section object.
+   *
+   * @var \Drupal\layout_builder\Section
+   */
+  protected $value;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($value, $notify = TRUE) {
+    if ($value && !$value instanceof Section) {
+      throw new \InvalidArgumentException(sprintf('Value assigned to "%s" is not a valid section', $this->getName()));
+    }
+    parent::setValue($value, $notify);
+  }
+
+}
diff --git a/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php b/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php
index 4951d01c4b9bca5ad8330adad9bbb9a59f638a99..c321585fdf5536c85f5712c619680399acec47f5 100644
--- a/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php
+++ b/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php
@@ -78,9 +78,9 @@ public static function create(ContainerInterface $container, array $configuratio
   public function viewElements(FieldItemListInterface $items, $langcode) {
     $elements = [];
 
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface[] $items */
-    foreach ($items as $delta => $item) {
-      $elements[$delta] = $this->builder->buildSection($item->layout, $item->layout_settings, $item->section);
+    /** @var \Drupal\layout_builder\SectionStorageInterface $items */
+    foreach ($items->getSections() as $delta => $section) {
+      $elements[$delta] = $section->toRenderArray();
     }
 
     return $elements;
diff --git a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
index fc1c63413fa1ff7a327312abd6b18ac75dbe2246..2001d1b5ff19fd4901d4ec0e889685546679a658 100644
--- a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
+++ b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
@@ -7,8 +7,6 @@
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\TypedData\MapDataDefinition;
-use Drupal\layout_builder\Field\LayoutSectionItemInterface;
 use Drupal\layout_builder\Section;
 
 /**
@@ -25,22 +23,16 @@
  *   no_ui = TRUE,
  *   cardinality = \Drupal\Core\Field\FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
  * )
+ *
+ * @property \Drupal\layout_builder\Section section
  */
-class LayoutSectionItem extends FieldItemBase implements LayoutSectionItemInterface {
+class LayoutSectionItem extends FieldItemBase {
 
   /**
    * {@inheritdoc}
    */
   public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
-    // Prevent early t() calls by using the TranslatableMarkup.
-    $properties['layout'] = DataDefinition::create('string')
-      ->setLabel(new TranslatableMarkup('Layout'))
-      ->setSetting('case_sensitive', FALSE)
-      ->setRequired(TRUE);
-    $properties['layout_settings'] = MapDataDefinition::create('map')
-      ->setLabel(new TranslatableMarkup('Layout Settings'))
-      ->setRequired(FALSE);
-    $properties['section'] = MapDataDefinition::create('map')
+    $properties['section'] = DataDefinition::create('layout_section')
       ->setLabel(new TranslatableMarkup('Layout Section'))
       ->setRequired(FALSE);
 
@@ -73,17 +65,6 @@ public static function mainPropertyName() {
   public static function schema(FieldStorageDefinitionInterface $field_definition) {
     $schema = [
       'columns' => [
-        'layout' => [
-          'type' => 'varchar',
-          'length' => '255',
-          'binary' => FALSE,
-        ],
-        'layout_settings' => [
-          'type' => 'blob',
-          'size' => 'normal',
-          // @todo Address in https://www.drupal.org/node/2914503.
-          'serialize' => TRUE,
-        ],
         'section' => [
           'type' => 'blob',
           'size' => 'normal',
@@ -100,10 +81,8 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
    * {@inheritdoc}
    */
   public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
-    $values['layout'] = 'layout_onecol';
-    $values['layout_settings'] = [];
     // @todo Expand this in https://www.drupal.org/node/2912331.
-    $values['section'] = [];
+    $values['section'] = new Section('layout_onecol');
     return $values;
   }
 
@@ -111,22 +90,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin
    * {@inheritdoc}
    */
   public function isEmpty() {
-    return empty($this->layout);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSection() {
-    return new Section($this->section);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function updateFromSection(Section $section) {
-    $this->section = $section->getValue();
-    return $this;
+    return empty($this->section);
   }
 
 }
diff --git a/core/modules/layout_builder/src/Section.php b/core/modules/layout_builder/src/Section.php
index f5e19003b58a80304b287edb8e65c8fcc3cba099..55204ddc9eda379ac561a265fc9b17286226b03e 100644
--- a/core/modules/layout_builder/src/Section.php
+++ b/core/modules/layout_builder/src/Section.php
@@ -5,158 +5,303 @@
 /**
  * Provides a domain object for layout sections.
  *
- * A section is a multi-dimensional array, keyed first by region machine name,
- * then by block UUID, containing block configuration values.
+ * A section consists of three parts:
+ * - The layout plugin ID for the layout applied to the section (for example,
+ *   'layout_onecol').
+ * - An array of settings for the layout plugin.
+ * - An array of components that can be rendered in the section.
+ *
+ * @internal
+ *   Layout Builder is currently experimental and should only be leveraged by
+ *   experimental modules and development releases of contributed modules.
+ *   See https://www.drupal.org/core/experimental for more information.
+ *
+ * @see \Drupal\Core\Layout\LayoutDefinition
+ * @see \Drupal\layout_builder\SectionComponent
+ *
+ * @todo Determine whether an interface will be provided for this in
+ *   https://www.drupal.org/project/drupal/issues/2930334.
  */
 class Section {
 
   /**
-   * The section data.
+   * The layout plugin ID.
+   *
+   * @var string
+   */
+  protected $layoutId;
+
+  /**
+   * The layout plugin settings.
    *
    * @var array
    */
-  protected $section;
+  protected $layoutSettings = [];
+
+  /**
+   * An array of components, keyed by UUID.
+   *
+   * @var \Drupal\layout_builder\SectionComponent[]
+   */
+  protected $components = [];
 
   /**
    * Constructs a new Section.
    *
-   * @param array $section
-   *   The section data.
+   * @param string $layout_id
+   *   The layout plugin ID.
+   * @param array $layout_settings
+   *   (optional) The layout plugin settings.
+   * @param \Drupal\layout_builder\SectionComponent[] $components
+   *   (optional) The components.
    */
-  public function __construct(array $section) {
-    $this->section = $section;
+  public function __construct($layout_id, array $layout_settings = [], array $components = []) {
+    $this->layoutId = $layout_id;
+    $this->layoutSettings = $layout_settings;
+    foreach ($components as $component) {
+      $this->setComponent($component);
+    }
   }
 
   /**
-   * Returns the value of the section.
+   * Returns the renderable array for this section.
    *
    * @return array
-   *   The section data.
+   *   A renderable array representing the content of the section.
    */
-  public function getValue() {
-    return $this->section;
+  public function toRenderArray() {
+    $regions = [];
+    foreach ($this->getComponents() as $component) {
+      if ($output = $component->toRenderArray()) {
+        $regions[$component->getRegion()][$component->getUuid()] = $output;
+      }
+    }
+
+    return $this->getLayout()->build($regions);
   }
 
   /**
-   * Gets the configuration of a given block from a region.
+   * Gets the layout plugin for this section.
    *
-   * @param string $region
-   *   The region name.
-   * @param string $uuid
-   *   The UUID of the block to retrieve.
+   * @return \Drupal\Core\Layout\LayoutInterface
+   *   The layout plugin.
+   */
+  public function getLayout() {
+    return $this->layoutPluginManager()->createInstance($this->getLayoutId(), $this->getLayoutSettings());
+  }
+
+  /**
+   * Gets the layout plugin ID for this section.
    *
-   * @return array
-   *   The block configuration.
+   * @return string
+   *   The layout plugin ID.
    *
-   * @throws \InvalidArgumentException
-   *   Thrown when the expected region or UUID do not exist.
+   * @internal
+   *   This method should only be used by code responsible for storing the data.
    */
-  public function getBlock($region, $uuid) {
-    if (!isset($this->section[$region])) {
-      throw new \InvalidArgumentException('Invalid region');
-    }
+  public function getLayoutId() {
+    return $this->layoutId;
+  }
 
-    if (!isset($this->section[$region][$uuid])) {
-      throw new \InvalidArgumentException('Invalid UUID');
-    }
+  /**
+   * Gets the layout plugin settings for this section.
+   *
+   * @return mixed[]
+   *   The layout plugin settings.
+   *
+   * @internal
+   *   This method should only be used by code responsible for storing the data.
+   */
+  public function getLayoutSettings() {
+    return $this->layoutSettings;
+  }
+
+  /**
+   * Sets the layout plugin settings for this section.
+   *
+   * @param mixed[] $layout_settings
+   *   The layout plugin settings.
+   *
+   * @return $this
+   */
+  public function setLayoutSettings(array $layout_settings) {
+    $this->layoutSettings = $layout_settings;
+    return $this;
+  }
 
-    return $this->section[$region][$uuid];
+  /**
+   * Returns the components of the section.
+   *
+   * @return \Drupal\layout_builder\SectionComponent[]
+   *   The components.
+   */
+  public function getComponents() {
+    return $this->components;
   }
 
   /**
-   * Updates the configuration of a given block from a region.
+   * Gets the component for a given UUID.
    *
-   * @param string $region
-   *   The region name.
    * @param string $uuid
-   *   The UUID of the block to retrieve.
-   * @param array $configuration
-   *   The block configuration.
+   *   The UUID of the component to retrieve.
    *
-   * @return $this
+   * @return \Drupal\layout_builder\SectionComponent
+   *   The component.
    *
    * @throws \InvalidArgumentException
-   *   Thrown when the expected region or UUID do not exist.
+   *   Thrown when the expected UUID does not exist.
    */
-  public function updateBlock($region, $uuid, array $configuration) {
-    if (!isset($this->section[$region])) {
-      throw new \InvalidArgumentException('Invalid region');
+  public function getComponent($uuid) {
+    if (!isset($this->components[$uuid])) {
+      throw new \InvalidArgumentException(sprintf('Invalid UUID "%s"', $uuid));
     }
 
-    if (!isset($this->section[$region][$uuid])) {
-      throw new \InvalidArgumentException('Invalid UUID');
-    }
-
-    $this->section[$region][$uuid] = $configuration;
+    return $this->components[$uuid];
+  }
 
+  /**
+   * Helper method to set a component.
+   *
+   * @param \Drupal\layout_builder\SectionComponent $component
+   *   The component.
+   *
+   * @return $this
+   */
+  protected function setComponent(SectionComponent $component) {
+    $this->components[$component->getUuid()] = $component;
     return $this;
   }
 
   /**
-   * Removes a given block from a region.
+   * Removes a given component from a region.
    *
-   * @param string $region
-   *   The region name.
    * @param string $uuid
-   *   The UUID of the block to remove.
+   *   The UUID of the component to remove.
    *
    * @return $this
    */
-  public function removeBlock($region, $uuid) {
-    unset($this->section[$region][$uuid]);
-    $this->section = array_filter($this->section);
+  public function removeComponent($uuid) {
+    unset($this->components[$uuid]);
     return $this;
   }
 
   /**
-   * Adds a block to the front of a region.
+   * Appends a component to the end of a region.
    *
-   * @param string $region
-   *   The region name.
-   * @param string $uuid
-   *   The UUID of the block to add.
-   * @param array $configuration
-   *   The block configuration.
+   * @param \Drupal\layout_builder\SectionComponent $component
+   *   The component being appended.
    *
    * @return $this
    */
-  public function addBlock($region, $uuid, array $configuration) {
-    $this->section += [$region => []];
-    $this->section[$region] = array_merge([$uuid => $configuration], $this->section[$region]);
+  public function appendComponent(SectionComponent $component) {
+    $component->setWeight($this->getNextHighestWeight($component->getRegion()));
+    $this->setComponent($component);
     return $this;
   }
 
   /**
-   * Inserts a block after a specified existing block in a region.
+   * Returns the next highest weight of the component in a region.
    *
    * @param string $region
    *   The region name.
-   * @param string $uuid
-   *   The UUID of the block to insert.
-   * @param array $configuration
-   *   The block configuration.
+   *
+   * @return int
+   *   A number higher than the highest weight of the component in the region.
+   */
+  protected function getNextHighestWeight($region) {
+    $components = $this->getComponentsByRegion($region);
+    $weights = array_map(function (SectionComponent $component) {
+      return $component->getWeight();
+    }, $components);
+    return $weights ? max($weights) + 1 : 0;
+  }
+
+  /**
+   * Gets the components for a specific region.
+   *
+   * @param string $region
+   *   The region name.
+   *
+   * @return \Drupal\layout_builder\SectionComponent[]
+   *   An array of components in the specified region, sorted by weight.
+   */
+  protected function getComponentsByRegion($region) {
+    $components = array_filter($this->getComponents(), function (SectionComponent $component) use ($region) {
+      return $component->getRegion() === $region;
+    });
+    uasort($components, function (SectionComponent $a, SectionComponent $b) {
+      return $a->getWeight() > $b->getWeight() ? 1 : -1;
+    });
+    return $components;
+  }
+
+  /**
+   * Inserts a component after a specified existing component.
+   *
    * @param string $preceding_uuid
-   *   The UUID of the existing block to insert after.
+   *   The UUID of the existing component to insert after.
+   * @param \Drupal\layout_builder\SectionComponent $component
+   *   The component being inserted.
    *
    * @return $this
    *
    * @throws \InvalidArgumentException
-   *   Thrown when the expected region does not exist.
+   *   Thrown when the expected UUID does not exist.
+   */
+  public function insertAfterComponent($preceding_uuid, SectionComponent $component) {
+    // Find the delta of the specified UUID.
+    $uuids = array_keys($this->getComponentsByRegion($component->getRegion()));
+    $delta = array_search($preceding_uuid, $uuids, TRUE);
+    if ($delta === FALSE) {
+      throw new \InvalidArgumentException(sprintf('Invalid preceding UUID "%s"', $preceding_uuid));
+    }
+    return $this->insertComponent($delta + 1, $component);
+  }
+
+  /**
+   * Inserts a component at a specified delta.
+   *
+   * @param int $delta
+   *   The zero-based delta in which to insert the component.
+   * @param \Drupal\layout_builder\SectionComponent $new_component
+   *   The component being inserted.
+   *
+   * @return $this
+   *
+   * @throws \OutOfBoundsException
+   *   Thrown when the specified delta is invalid.
    */
-  public function insertBlock($region, $uuid, array $configuration, $preceding_uuid) {
-    if (!isset($this->section[$region])) {
-      throw new \InvalidArgumentException('Invalid region');
+  public function insertComponent($delta, SectionComponent $new_component) {
+    $components = $this->getComponentsByRegion($new_component->getRegion());
+    $count = count($components);
+    if ($delta > $count) {
+      throw new \OutOfBoundsException(sprintf('Invalid delta "%s" for the "%s" component', $delta, $new_component->getUuid()));
     }
 
-    $slice_id = array_search($preceding_uuid, array_keys($this->section[$region]));
-    if ($slice_id === FALSE) {
-      throw new \InvalidArgumentException('Invalid preceding UUID');
+    // If the delta is the end of the list, append the component instead.
+    if ($delta === $count) {
+      return $this->appendComponent($new_component);
     }
 
-    $before = array_slice($this->section[$region], 0, $slice_id + 1);
-    $after = array_slice($this->section[$region], $slice_id + 1);
-    $this->section[$region] = array_merge($before, [$uuid => $configuration], $after);
+    // Find the weight of the component that exists at the specified delta.
+    $weight = array_values($components)[$delta]->getWeight();
+    $this->setComponent($new_component->setWeight($weight++));
+
+    // Increase the weight of every subsequent component.
+    foreach (array_slice($components, $delta) as $component) {
+      $component->setWeight($weight++);
+    }
     return $this;
   }
 
+  /**
+   * Wraps the layout plugin manager.
+   *
+   * @return \Drupal\Core\Layout\LayoutPluginManagerInterface
+   *   The layout plugin manager.
+   */
+  protected function layoutPluginManager() {
+    return \Drupal::service('plugin.manager.core.layout');
+  }
+
 }
diff --git a/core/modules/layout_builder/src/SectionComponent.php b/core/modules/layout_builder/src/SectionComponent.php
new file mode 100644
index 0000000000000000000000000000000000000000..caad0a2b61db2f7df9edcbf0ca1bf540f7837d2d
--- /dev/null
+++ b/core/modules/layout_builder/src/SectionComponent.php
@@ -0,0 +1,314 @@
+<?php
+
+namespace Drupal\layout_builder;
+
+use Drupal\Component\Plugin\Exception\PluginException;
+use Drupal\Core\Block\BlockPluginInterface;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+
+/**
+ * Provides a value object for a section component.
+ *
+ * A component represents the smallest part of a layout (for example, a block).
+ * Components wrap a renderable plugin, currently using
+ * \Drupal\Core\Block\BlockPluginInterface, and contain the layout region
+ * within the section layout where the component will be rendered.
+ *
+ * @internal
+ *   Layout Builder is currently experimental and should only be leveraged by
+ *   experimental modules and development releases of contributed modules.
+ *   See https://www.drupal.org/core/experimental for more information.
+ *
+ * @see \Drupal\Core\Layout\LayoutDefinition
+ * @see \Drupal\layout_builder\Section
+ * @see \Drupal\layout_builder\SectionStorageInterface
+ *
+ * @todo Determine whether to retain the name 'component' in
+ *   https://www.drupal.org/project/drupal/issues/2929783.
+ * @todo Determine whether an interface will be provided for this in
+ *   https://www.drupal.org/project/drupal/issues/2930334.
+ */
+class SectionComponent {
+
+  /**
+   * The UUID of the component.
+   *
+   * @var string
+   */
+  protected $uuid;
+
+  /**
+   * The region the component is placed in.
+   *
+   * @var string
+   */
+  protected $region;
+
+  /**
+   * An array of plugin configuration.
+   *
+   * @var mixed[]
+   */
+  protected $configuration;
+
+  /**
+   * The weight of the component.
+   *
+   * @var int
+   */
+  protected $weight = 0;
+
+  /**
+   * Any additional properties and values.
+   *
+   * @var mixed[]
+   */
+  protected $additional = [];
+
+  /**
+   * Constructs a new SectionComponent.
+   *
+   * @param string $uuid
+   *   The UUID.
+   * @param string $region
+   *   The region.
+   * @param mixed[] $configuration
+   *   The plugin configuration.
+   * @param mixed[] $additional
+   *   An additional values.
+   */
+  public function __construct($uuid, $region, array $configuration = [], array $additional = []) {
+    $this->uuid = $uuid;
+    $this->region = $region;
+    $this->configuration = $configuration;
+    $this->additional = $additional;
+  }
+
+  /**
+   * Returns the renderable array for this component.
+   *
+   * @return array
+   *   A renderable array representing the content of the component.
+   */
+  public function toRenderArray() {
+    $output = [];
+
+    $plugin = $this->getPlugin();
+    // @todo Figure out the best way to unify fields and blocks and components
+    //   in https://www.drupal.org/node/1875974.
+    if ($plugin instanceof BlockPluginInterface) {
+      $access = $plugin->access($this->currentUser(), TRUE);
+      $cacheability = CacheableMetadata::createFromObject($access);
+
+      if ($access->isAllowed()) {
+        $cacheability->addCacheableDependency($plugin);
+        // @todo Move this to BlockBase in https://www.drupal.org/node/2931040.
+        $output = [
+          '#theme' => 'block',
+          '#configuration' => $plugin->getConfiguration(),
+          '#plugin_id' => $plugin->getPluginId(),
+          '#base_plugin_id' => $plugin->getBaseId(),
+          '#derivative_plugin_id' => $plugin->getDerivativeId(),
+          '#weight' => $this->getWeight(),
+          'content' => $plugin->build(),
+        ];
+      }
+      $cacheability->applyTo($output);
+    }
+    return $output;
+  }
+
+  /**
+   * Gets any arbitrary property for the component.
+   *
+   * @param string $property
+   *   The property to retrieve.
+   *
+   * @return mixed
+   *   The value for that property, or NULL if the property does not exist.
+   */
+  public function get($property) {
+    if (property_exists($this, $property)) {
+      $value = isset($this->{$property}) ? $this->{$property} : NULL;
+    }
+    else {
+      $value = isset($this->additional[$property]) ? $this->additional[$property] : NULL;
+    }
+    return $value;
+  }
+
+  /**
+   * Sets a value to an arbitrary property for the component.
+   *
+   * @param string $property
+   *   The property to use for the value.
+   * @param mixed $value
+   *   The value to set.
+   *
+   * @return $this
+   */
+  public function set($property, $value) {
+    if (property_exists($this, $property)) {
+      $this->{$property} = $value;
+    }
+    else {
+      $this->additional[$property] = $value;
+    }
+    return $this;
+  }
+
+  /**
+   * Gets the region for the component.
+   *
+   * @return string
+   *   The region.
+   */
+  public function getRegion() {
+    return $this->region;
+  }
+
+  /**
+   * Sets the region for the component.
+   *
+   * @param string $region
+   *   The region.
+   *
+   * @return $this
+   */
+  public function setRegion($region) {
+    $this->region = $region;
+    return $this;
+  }
+
+  /**
+   * Gets the weight of the component.
+   *
+   * @return int
+   *   The zero-based weight of the component.
+   *
+   * @throws \UnexpectedValueException
+   *   Thrown if the weight was never set.
+   */
+  public function getWeight() {
+    return $this->weight;
+  }
+
+  /**
+   * Sets the weight of the component.
+   *
+   * @param int $weight
+   *   The zero-based weight of the component.
+   *
+   * @return $this
+   */
+  public function setWeight($weight) {
+    $this->weight = $weight;
+    return $this;
+  }
+
+  /**
+   * Gets the component plugin configuration.
+   *
+   * @return mixed[]
+   *   The component plugin configuration.
+   */
+  protected function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   * Sets the plugin configuration.
+   *
+   * @param mixed[] $configuration
+   *   The plugin configuration.
+   *
+   * @return $this
+   */
+  public function setConfiguration(array $configuration) {
+    $this->configuration = $configuration;
+    return $this;
+  }
+
+  /**
+   * Gets the plugin ID.
+   *
+   * @return string
+   *   The plugin ID.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   *   Thrown if the plugin ID cannot be found.
+   */
+  protected function getPluginId() {
+    if (empty($this->configuration['id'])) {
+      throw new PluginException(sprintf('No plugin ID specified for component with "%s" UUID', $this->uuid));
+    }
+    return $this->configuration['id'];
+  }
+
+  /**
+   * Gets the UUID for this component.
+   *
+   * @return string
+   *   The UUID.
+   */
+  public function getUuid() {
+    return $this->uuid;
+  }
+
+  /**
+   * Gets the plugin for this component.
+   *
+   * @return \Drupal\Component\Plugin\PluginInspectionInterface
+   *   The plugin.
+   */
+  public function getPlugin() {
+    $plugin = $this->pluginManager()->createInstance($this->getPluginId(), $this->getConfiguration());
+    if ($plugin instanceof ContextAwarePluginInterface) {
+      $contexts = $this->contextRepository()->getRuntimeContexts(array_values($plugin->getContextMapping()));
+      $this->contextHandler()->applyContextMapping($plugin, $contexts);
+    }
+    return $plugin;
+  }
+
+  /**
+   * Wraps the component plugin manager.
+   *
+   * @return \Drupal\Core\Block\BlockManagerInterface
+   *   The plugin manager.
+   */
+  protected function pluginManager() {
+    return \Drupal::service('plugin.manager.block');
+  }
+
+  /**
+   * Wraps the context repository.
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextRepositoryInterface
+   *   The context repository.
+   */
+  protected function contextRepository() {
+    return \Drupal::service('context.repository');
+  }
+
+  /**
+   * Wraps the context handler.
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
+   *   The context handler.
+   */
+  protected function contextHandler() {
+    return \Drupal::service('context.handler');
+  }
+
+  /**
+   * Wraps the current user.
+   *
+   * @return \Drupal\Core\Session\AccountInterface
+   *   The current user.
+   */
+  protected function currentUser() {
+    return \Drupal::currentUser();
+  }
+
+}
diff --git a/core/modules/layout_builder/src/SectionStorageInterface.php b/core/modules/layout_builder/src/SectionStorageInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..c4da487eba6bf2d8c60ac0b21d9da17a1d1363f7
--- /dev/null
+++ b/core/modules/layout_builder/src/SectionStorageInterface.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Drupal\layout_builder;
+
+/**
+ * Defines the interface for an object that stores layout sections.
+ *
+ * @internal
+ *   Layout Builder is currently experimental and should only be leveraged by
+ *   experimental modules and development releases of contributed modules.
+ *   See https://www.drupal.org/core/experimental for more information.
+ *
+ * @see \Drupal\layout_builder\Section
+ */
+interface SectionStorageInterface extends \Countable {
+
+  /**
+   * Gets the layout sections.
+   *
+   * @return \Drupal\layout_builder\Section[]
+   *   An array of sections.
+   */
+  public function getSections();
+
+  /**
+   * Gets a domain object for the layout section.
+   *
+   * @param int $delta
+   *   The delta of the section.
+   *
+   * @return \Drupal\layout_builder\Section
+   *   The layout section.
+   */
+  public function getSection($delta);
+
+  /**
+   * Appends a new section to the end of the list.
+   *
+   * @param \Drupal\layout_builder\Section $section
+   *   The section to append.
+   *
+   * @return $this
+   */
+  public function appendSection(Section $section);
+
+  /**
+   * Inserts a new section at a given delta.
+   *
+   * If a section exists at the given index, the section at that position and
+   * others after it are shifted backward.
+   *
+   * @param int $delta
+   *   The delta of the section.
+   * @param \Drupal\layout_builder\Section $section
+   *   The section to insert.
+   *
+   * @return $this
+   */
+  public function insertSection($delta, Section $section);
+
+  /**
+   * Removes the section at the given delta.
+   *
+   * @param int $delta
+   *   The delta of the section.
+   *
+   * @return $this
+   */
+  public function removeSection($delta);
+
+}
diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php
index 61e481c7c7c8d94be39a51309f002e4942cd70a0..90ad29886a4dc801314e1e821ac2d923b787f7b7 100644
--- a/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php
+++ b/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php
@@ -4,6 +4,8 @@
 
 use Drupal\Core\Entity\Entity\EntityViewDisplay;
 use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\layout_builder\Section;
+use Drupal\layout_builder\SectionComponent;
 use Drupal\Tests\BrowserTestBase;
 
 /**
@@ -56,19 +58,14 @@ public function providerTestLayoutSectionFormatter() {
     $data['block_with_context'] = [
       [
         [
-          'layout' => 'layout_onecol',
-          'section' => [
-            'content' => [
-              'baz' => [
-                'block' => [
-                  'id' => 'test_context_aware',
-                  'context_mapping' => [
-                    'user' => '@user.current_user_context:current_user',
-                  ],
-                ],
+          'section' => new Section('layout_onecol', [], [
+            'baz' => new SectionComponent('baz', 'content', [
+              'id' => 'test_context_aware',
+              'context_mapping' => [
+                'user' => '@user.current_user_context:current_user',
               ],
-            ],
-          ],
+            ]),
+          ]),
         ],
       ],
       [
@@ -86,16 +83,11 @@ public function providerTestLayoutSectionFormatter() {
     $data['single_section_single_block'] = [
       [
         [
-          'layout' => 'layout_onecol',
-          'section' => [
-            'content' => [
-              'baz' => [
-                'block' => [
-                  'id' => 'system_powered_by_block',
-                ],
-              ],
-            ],
-          ],
+          'section' => new Section('layout_onecol', [], [
+            'baz' => new SectionComponent('baz', 'content', [
+              'id' => 'system_powered_by_block',
+            ]),
+          ]),
         ],
       ],
       '.layout--onecol',
@@ -107,37 +99,23 @@ public function providerTestLayoutSectionFormatter() {
     $data['multiple_sections'] = [
       [
         [
-          'layout' => 'layout_onecol',
-          'section' => [
-            'content' => [
-              'baz' => [
-                'block' => [
-                  'id' => 'system_powered_by_block',
-                ],
-              ],
-            ],
-          ],
+          'section' => new Section('layout_onecol', [], [
+            'baz' => new SectionComponent('baz', 'content', [
+              'id' => 'system_powered_by_block',
+            ]),
+          ]),
         ],
         [
-          'layout' => 'layout_twocol',
-          'section' => [
-            'first' => [
-              'foo' => [
-                'block' => [
-                  'id' => 'test_block_instantiation',
-                  'display_message' => 'foo text',
-                ],
-              ],
-            ],
-            'second' => [
-              'bar' => [
-                'block' => [
-                  'id' => 'test_block_instantiation',
-                  'display_message' => 'bar text',
-                ],
-              ],
-            ],
-          ],
+          'section' => new Section('layout_twocol', [], [
+            'foo' => new SectionComponent('foo', 'first', [
+              'id' => 'test_block_instantiation',
+              'display_message' => 'foo text',
+            ]),
+            'bar' => new SectionComponent('bar', 'second', [
+              'id' => 'test_block_instantiation',
+              'display_message' => 'bar text',
+            ]),
+          ]),
         ],
       ],
       [
@@ -177,16 +155,11 @@ public function testLayoutSectionFormatter($layout_data, $expected_selector, $ex
   public function testLayoutSectionFormatterAccess() {
     $node = $this->createSectionNode([
       [
-        'layout' => 'layout_onecol',
-        'section' => [
-          'content' => [
-            'baz' => [
-              'block' => [
-                'id' => 'test_access',
-              ],
-            ],
-          ],
-        ],
+        'section' => new Section('layout_onecol', [], [
+          'baz' => new SectionComponent('baz', 'content', [
+            'id' => 'test_access',
+          ]),
+        ]),
       ],
     ]);
 
@@ -216,41 +189,27 @@ public function testMultilingualLayoutSectionFormatter() {
 
     $entity = $this->createSectionNode([
       [
-        'layout' => 'layout_onecol',
-        'section' => [
-          'content' => [
-            'baz' => [
-              'block' => [
-                'id' => 'system_powered_by_block',
-              ],
-            ],
-          ],
-        ],
+        'section' => new Section('layout_onecol', [], [
+          'baz' => new SectionComponent('baz', 'content', [
+            'id' => 'system_powered_by_block',
+          ]),
+        ]),
       ],
     ]);
     $entity->addTranslation('es', [
       'title' => 'Translated node title',
       $this->fieldName => [
         [
-          'layout' => 'layout_twocol',
-          'section' => [
-            'first' => [
-              'foo' => [
-                'block' => [
-                  'id' => 'test_block_instantiation',
-                  'display_message' => 'foo text',
-                ],
-              ],
-            ],
-            'second' => [
-              'bar' => [
-                'block' => [
-                  'id' => 'test_block_instantiation',
-                  'display_message' => 'bar text',
-                ],
-              ],
-            ],
-          ],
+          'section' => new Section('layout_twocol', [], [
+            'foo' => new SectionComponent('foo', 'first', [
+              'id' => 'test_block_instantiation',
+              'display_message' => 'foo text',
+            ]),
+            'bar' => new SectionComponent('bar', 'second', [
+              'id' => 'test_block_instantiation',
+              'display_message' => 'bar text',
+            ]),
+          ]),
         ],
       ],
     ]);
diff --git a/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php b/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php
index c3a23c78710fb271ef18d1682720e0db39d89ad1..c1b340dea411bdcff16e52593ec0d1f4e1d5b62b 100644
--- a/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php
+++ b/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php
@@ -8,6 +8,7 @@
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\KernelTests\KernelTestBase;
+use Drupal\layout_builder\Section;
 
 /**
  * Ensures that Layout Builder and Field Layout are compatible with each other.
@@ -108,13 +109,9 @@ public function testCompatibility() {
     $this->assertSame($original_markup, $new_markup);
 
     // Add a layout override.
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
+    /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
     $field_list = $entity->layout_builder__layout;
-    $field_list->appendItem([
-      'layout' => 'layout_onecol',
-      'layout_settings' => [],
-      'section' => [],
-    ]);
+    $field_list->appendSection(new Section('layout_onecol'));
     $entity->save();
 
     // The rendered entity has now changed. The non-configurable field is shown
diff --git a/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemListTest.php b/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemListTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c8ae2e6c9a876720c89060dfd449b7659c9ad9f
--- /dev/null
+++ b/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemListTest.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\Tests\layout_builder\Kernel;
+
+use Drupal\entity_test\Entity\EntityTestBaseFieldDisplay;
+
+/**
+ * Tests the field type for Layout Sections.
+ *
+ * @coversDefaultClass \Drupal\layout_builder\Field\LayoutSectionItemList
+ *
+ * @group layout_builder
+ */
+class LayoutSectionItemListTest extends SectionStorageTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'field',
+    'text',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEntity(array $section_data) {
+    $this->installEntitySchema('entity_test_base_field_display');
+    layout_builder_add_layout_section_field('entity_test_base_field_display', 'entity_test_base_field_display');
+
+    $entity = EntityTestBaseFieldDisplay::create([
+      'name' => 'The test entity',
+      'layout_builder__layout' => $section_data,
+    ]);
+    $entity->save();
+    return $entity->get('layout_builder__layout');
+  }
+
+}
diff --git a/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemTest.php b/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemTest.php
deleted file mode 100644
index 0e13471c155814143b703687a788165ceb967cef..0000000000000000000000000000000000000000
--- a/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemTest.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-
-namespace Drupal\Tests\layout_builder\Kernel;
-
-use Drupal\Core\Field\FieldItemInterface;
-use Drupal\Core\Field\FieldItemListInterface;
-use Drupal\entity_test\Entity\EntityTest;
-use Drupal\layout_builder\Field\LayoutSectionItemInterface;
-use Drupal\layout_builder\Field\LayoutSectionItemListInterface;
-use Drupal\Tests\field\Kernel\FieldKernelTestBase;
-
-/**
- * Tests the field type for Layout Sections.
- *
- * @group layout_builder
- */
-class LayoutSectionItemTest extends FieldKernelTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = ['layout_builder', 'layout_discovery'];
-
-  /**
-   * Tests using entity fields of the layout section field type.
-   */
-  public function testLayoutSectionItem() {
-    layout_builder_add_layout_section_field('entity_test', 'entity_test');
-
-    $entity = EntityTest::create();
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
-    $field_list = $entity->layout_builder__layout;
-
-    // Test sample item generation.
-    $field_list->generateSampleItems();
-    $this->entityValidateAndSave($entity);
-
-    $field = $field_list->get(0);
-    $this->assertInstanceOf(LayoutSectionItemInterface::class, $field);
-    $this->assertInstanceOf(FieldItemInterface::class, $field);
-    $this->assertSame('section', $field->mainPropertyName());
-    $this->assertSame('layout_onecol', $field->layout);
-    $this->assertSame([], $field->layout_settings);
-    $this->assertSame([], $field->section);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function testLayoutSectionItemList() {
-    layout_builder_add_layout_section_field('entity_test', 'entity_test');
-
-    $entity = EntityTest::create();
-    /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
-    $field_list = $entity->layout_builder__layout;
-    $this->assertInstanceOf(LayoutSectionItemListInterface::class, $field_list);
-    $this->assertInstanceOf(FieldItemListInterface::class, $field_list);
-    $entity->save();
-
-    $field_list->appendItem(['layout' => 'layout_twocol']);
-    $field_list->appendItem(['layout' => 'layout_onecol']);
-    $field_list->appendItem(['layout' => 'layout_threecol_25_50_25']);
-    $this->assertSame([
-      ['layout' => 'layout_twocol'],
-      ['layout' => 'layout_onecol'],
-      ['layout' => 'layout_threecol_25_50_25'],
-    ], $field_list->getValue());
-
-    $field_list->addItem(1, ['layout' => 'layout_threecol_33_34_33']);
-    $this->assertSame([
-      ['layout' => 'layout_twocol'],
-      ['layout' => 'layout_threecol_33_34_33'],
-      ['layout' => 'layout_onecol'],
-      ['layout' => 'layout_threecol_25_50_25'],
-    ], $field_list->getValue());
-
-    $field_list->addItem($field_list->count(), ['layout' => 'layout_twocol_bricks']);
-    $this->assertSame([
-      ['layout' => 'layout_twocol'],
-      ['layout' => 'layout_threecol_33_34_33'],
-      ['layout' => 'layout_onecol'],
-      ['layout' => 'layout_threecol_25_50_25'],
-      ['layout' => 'layout_twocol_bricks'],
-    ], $field_list->getValue());
-  }
-
-}
diff --git a/core/modules/layout_builder/tests/src/Kernel/SectionStorageTestBase.php b/core/modules/layout_builder/tests/src/Kernel/SectionStorageTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..b8e8b3462c35f351334c2f1e1292705dc9bca418
--- /dev/null
+++ b/core/modules/layout_builder/tests/src/Kernel/SectionStorageTestBase.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace Drupal\Tests\layout_builder\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\layout_builder\Section;
+use Drupal\layout_builder\SectionComponent;
+
+/**
+ * Provides a base class for testing implementations of section storage.
+ */
+abstract class SectionStorageTestBase extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'layout_builder',
+    'layout_discovery',
+    'layout_test',
+    'user',
+    'entity_test',
+  ];
+
+  /**
+   * The section storage implementation.
+   *
+   * @var \Drupal\layout_builder\SectionStorageInterface
+   */
+  protected $sectionStorage;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $section_data = [
+      [
+        'section' => new Section('layout_test_plugin', [], [
+          'first-uuid' => new SectionComponent('first-uuid', 'content'),
+        ]),
+      ],
+      [
+        'section' => new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+          'second-uuid' => new SectionComponent('second-uuid', 'content'),
+        ]),
+      ],
+    ];
+    $this->sectionStorage = $this->getEntity($section_data);
+  }
+
+  /**
+   * Sets up the section storage entity.
+   *
+   * @param array $section_data
+   *   An array of section data.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The entity.
+   */
+  abstract protected function getEntity(array $section_data);
+
+  /**
+   * @covers ::getSections
+   */
+  public function testGetSections() {
+    $expected = [
+      new Section('layout_test_plugin', [], [
+        'first-uuid' => new SectionComponent('first-uuid', 'content'),
+      ]),
+      new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+        'second-uuid' => new SectionComponent('second-uuid', 'content'),
+      ]),
+    ];
+    $this->assertSections($expected);
+  }
+
+  /**
+   * @covers ::getSection
+   */
+  public function testGetSection() {
+    $this->assertInstanceOf(Section::class, $this->sectionStorage->getSection(0));
+  }
+
+  /**
+   * @covers ::getSection
+   */
+  public function testGetSectionInvalidDelta() {
+    $this->setExpectedException(\OutOfBoundsException::class, 'Invalid delta "2" for the "The test entity"');
+    $this->sectionStorage->getSection(2);
+  }
+
+  /**
+   * @covers ::insertSection
+   */
+  public function testInsertSection() {
+    $expected = [
+      new Section('layout_test_plugin', [], [
+        'first-uuid' => new SectionComponent('first-uuid', 'content'),
+      ]),
+      new Section('setting_1'),
+      new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+        'second-uuid' => new SectionComponent('second-uuid', 'content'),
+      ]),
+    ];
+
+    $this->sectionStorage->insertSection(1, new Section('setting_1'));
+    $this->assertSections($expected);
+  }
+
+  /**
+   * @covers ::appendSection
+   */
+  public function testAppendSection() {
+    $expected = [
+      new Section('layout_test_plugin', [], [
+        'first-uuid' => new SectionComponent('first-uuid', 'content'),
+      ]),
+      new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+        'second-uuid' => new SectionComponent('second-uuid', 'content'),
+      ]),
+      new Section('foo'),
+    ];
+
+    $this->sectionStorage->appendSection(new Section('foo'));
+    $this->assertSections($expected);
+  }
+
+  /**
+   * @covers ::removeSection
+   */
+  public function testRemoveSection() {
+    $expected = [
+      new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+        'second-uuid' => new SectionComponent('second-uuid', 'content'),
+      ]),
+    ];
+
+    $this->sectionStorage->removeSection(0);
+    $this->assertSections($expected);
+  }
+
+  /**
+   * Asserts that the field list has the expected sections.
+   *
+   * @param \Drupal\layout_builder\Section[] $expected
+   *   The expected sections.
+   */
+  protected function assertSections(array $expected) {
+    $result = $this->sectionStorage->getSections();
+    $this->assertEquals($expected, $result);
+    $this->assertSame(array_keys($expected), array_keys($result));
+  }
+
+}
diff --git a/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php b/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php
index 3e5b2ffad148a47c1ff54354b0befdbb698d0d7b..28f22557e75bb2afa0aab82600a058b0571bfc4c 100644
--- a/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php
+++ b/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php
@@ -7,6 +7,8 @@
 use Drupal\Core\Block\BlockManagerInterface;
 use Drupal\Core\Block\BlockPluginInterface;
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Layout\LayoutDefinition;
 use Drupal\Core\Layout\LayoutInterface;
 use Drupal\Core\Layout\LayoutPluginManagerInterface;
 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
@@ -14,6 +16,7 @@
 use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\layout_builder\LayoutSectionBuilder;
+use Drupal\layout_builder\SectionComponent;
 use Drupal\Tests\UnitTestCase;
 use Prophecy\Argument;
 
@@ -86,7 +89,16 @@ protected function setUp() {
     $this->layoutSectionBuilder = new LayoutSectionBuilder($this->account->reveal(), $this->layoutPluginManager->reveal(), $this->blockManager->reveal(), $this->contextHandler->reveal(), $this->contextRepository->reveal());
 
     $this->layout = $this->prophesize(LayoutInterface::class);
+    $this->layout->getPluginDefinition()->willReturn(new LayoutDefinition([]));
+    $this->layout->build(Argument::type('array'))->willReturnArgument(0);
     $this->layoutPluginManager->createInstance('layout_onecol', [])->willReturn($this->layout->reveal());
+
+    $container = new ContainerBuilder();
+    $container->set('current_user', $this->account->reveal());
+    $container->set('plugin.manager.block', $this->blockManager->reveal());
+    $container->set('context.handler', $this->contextHandler->reveal());
+    $container->set('context.repository', $this->contextRepository->reveal());
+    \Drupal::setContainer($container);
   }
 
   /**
@@ -102,8 +114,12 @@ public function testBuildSection() {
       '#base_plugin_id' => 'block_plugin_id',
       '#derivative_plugin_id' => NULL,
       'content' => $block_content,
+      '#cache' => [
+        'contexts' => [],
+        'tags' => [],
+        'max-age' => -1,
+      ],
     ];
-    $this->layout->build(['content' => ['some_uuid' => $render_array]])->willReturnArgument(0);
 
     $block = $this->prophesize(BlockPluginInterface::class);
     $this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
@@ -120,20 +136,9 @@ public function testBuildSection() {
     $block->getConfiguration()->willReturn([]);
 
     $section = [
-      'content' => [
-        'some_uuid' => [
-          'block' => [
-            'id' => 'block_plugin_id',
-          ],
-        ],
-      ],
+      new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
     ];
     $expected = [
-      '#cache' => [
-        'contexts' => [],
-        'tags' => [],
-        'max-age' => -1,
-      ],
       'content' => [
         'some_uuid' => $render_array,
       ],
@@ -146,7 +151,6 @@ public function testBuildSection() {
    * @covers ::buildSection
    */
   public function testBuildSectionAccessDenied() {
-    $this->layout->build([])->willReturn([]);
 
     $block = $this->prophesize(BlockPluginInterface::class);
     $this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
@@ -156,21 +160,19 @@ public function testBuildSectionAccessDenied() {
     $block->build()->shouldNotBeCalled();
 
     $section = [
+      new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
+    ];
+    $expected = [
       'content' => [
         'some_uuid' => [
-          'block' => [
-            'id' => 'block_plugin_id',
+          '#cache' => [
+            'contexts' => [],
+            'tags' => [],
+            'max-age' => -1,
           ],
         ],
       ],
     ];
-    $expected = [
-      '#cache' => [
-        'contexts' => [],
-        'tags' => [],
-        'max-age' => -1,
-      ],
-    ];
     $result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
     $this->assertEquals($expected, $result);
   }
@@ -179,23 +181,14 @@ public function testBuildSectionAccessDenied() {
    * @covers ::buildSection
    */
   public function testBuildSectionEmpty() {
-    $this->layout->build([])->willReturn([]);
-
     $section = [];
-    $expected = [
-      '#cache' => [
-        'contexts' => [],
-        'tags' => [],
-        'max-age' => -1,
-      ],
-    ];
+    $expected = [];
     $result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
     $this->assertEquals($expected, $result);
   }
 
   /**
    * @covers ::buildSection
-   * @covers ::getBlock
    */
   public function testContextAwareBlock() {
     $render_array = [
@@ -206,8 +199,12 @@ public function testContextAwareBlock() {
       '#base_plugin_id' => 'block_plugin_id',
       '#derivative_plugin_id' => NULL,
       'content' => [],
+      '#cache' => [
+        'contexts' => [],
+        'tags' => [],
+        'max-age' => -1,
+      ],
     ];
-    $this->layout->build(['content' => ['some_uuid' => $render_array]])->willReturnArgument(0);
 
     $block = $this->prophesize(BlockPluginInterface::class)->willImplement(ContextAwarePluginInterface::class);
     $this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
@@ -228,20 +225,9 @@ public function testContextAwareBlock() {
     $this->contextHandler->applyContextMapping($block->reveal(), [])->shouldBeCalled();
 
     $section = [
-      'content' => [
-        'some_uuid' => [
-          'block' => [
-            'id' => 'block_plugin_id',
-          ],
-        ],
-      ],
+      new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
     ];
     $expected = [
-      '#cache' => [
-        'contexts' => [],
-        'tags' => [],
-        'max-age' => -1,
-      ],
       'content' => [
         'some_uuid' => $render_array,
       ],
@@ -252,50 +238,10 @@ public function testContextAwareBlock() {
 
   /**
    * @covers ::buildSection
-   * @covers ::getBlock
    */
   public function testBuildSectionMissingPluginId() {
-    $section = [
-      'content' => [
-        'some_uuid' => [
-          'block' => [],
-        ],
-      ],
-    ];
-    $this->setExpectedException(PluginException::class, 'No plugin ID specified for block with "some_uuid" UUID');
-    $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
-  }
-
-  /**
-   * @covers ::buildSection
-   *
-   * @dataProvider providerTestBuildSectionMalformedData
-   */
-  public function testBuildSectionMalformedData($section, $message) {
-    $this->layout->build(Argument::type('array'))->willReturnArgument(0);
-    $this->layout->getPluginId()->willReturn('the_plugin_id');
-    $this->setExpectedException(\InvalidArgumentException::class, $message);
-    $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
-  }
-
-  /**
-   * Provides test data for ::testBuildSectionMalformedData().
-   */
-  public function providerTestBuildSectionMalformedData() {
-    $data = [];
-    $data['invalid_region'] = [
-      ['content' => 'bar'],
-      'The "content" region in the "the_plugin_id" layout has invalid configuration',
-    ];
-    $data['invalid_configuration'] = [
-      ['content' => ['some_uuid' => 'bar']],
-      'The block with UUID of "some_uuid" has invalid configuration',
-    ];
-    $data['invalid_blocks'] = [
-      ['content' => ['some_uuid' => []]],
-      'The block with UUID of "some_uuid" has invalid configuration',
-    ];
-    return $data;
+    $this->setExpectedException(PluginException::class, 'No plugin ID specified for component with "some_uuid" UUID');
+    $this->layoutSectionBuilder->buildSection('layout_onecol', [], [new SectionComponent('some_uuid', 'content')]);
   }
 
 }
diff --git a/core/modules/layout_builder/tests/src/Unit/SectionTest.php b/core/modules/layout_builder/tests/src/Unit/SectionTest.php
index 33a338706e27ec78aace18dc38969453ae03363e..eff239507d2b111c7717474e483e4706ad58f071 100644
--- a/core/modules/layout_builder/tests/src/Unit/SectionTest.php
+++ b/core/modules/layout_builder/tests/src/Unit/SectionTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\layout_builder\Unit;
 
 use Drupal\layout_builder\Section;
+use Drupal\layout_builder\SectionComponent;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -24,242 +25,158 @@ class SectionTest extends UnitTestCase {
   protected function setUp() {
     parent::setUp();
 
-    $this->section = new Section([
-      'empty-region' => [],
-      'some-region' => [
-        'existing-uuid' => [
-          'block' => [
-            'id' => 'existing-block-id',
-          ],
-        ],
-      ],
-      'ordered-region' => [
-        'first-uuid' => [
-          'block' => [
-            'id' => 'first-block-id',
-          ],
-        ],
-        'second-uuid' => [
-          'block' => [
-            'id' => 'second-block-id',
-          ],
-        ],
-      ],
+    $this->section = new Section('layout_onecol', [], [
+      new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']),
+      (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
+      (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
     ]);
   }
 
   /**
    * @covers ::__construct
-   * @covers ::getValue
+   * @covers ::setComponent
+   * @covers ::getComponents
    */
-  public function testGetValue() {
+  public function testGetComponents() {
     $expected = [
-      'empty-region' => [],
-      'some-region' => [
-        'existing-uuid' => [
-          'block' => [
-            'id' => 'existing-block-id',
-          ],
-        ],
-      ],
-      'ordered-region' => [
-        'first-uuid' => [
-          'block' => [
-            'id' => 'first-block-id',
-          ],
-        ],
-        'second-uuid' => [
-          'block' => [
-            'id' => 'second-block-id',
-          ],
-        ],
-      ],
+      'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+      'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
+      'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
     ];
-    $result = $this->section->getValue();
-    $this->assertSame($expected, $result);
-  }
 
-  /**
-   * @covers ::getBlock
-   */
-  public function testGetBlockInvalidRegion() {
-    $this->setExpectedException(\InvalidArgumentException::class, 'Invalid region');
-    $this->section->getBlock('invalid-region', 'existing-uuid');
+    $this->assertComponents($expected, $this->section);
   }
 
   /**
-   * @covers ::getBlock
+   * @covers ::getComponent
    */
-  public function testGetBlockInvalidUuid() {
-    $this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID');
-    $this->section->getBlock('some-region', 'invalid-uuid');
+  public function testGetComponentInvalidUuid() {
+    $this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID "invalid-uuid"');
+    $this->section->getComponent('invalid-uuid');
   }
 
   /**
-   * @covers ::getBlock
+   * @covers ::getComponent
    */
-  public function testGetBlock() {
-    $expected = ['block' => ['id' => 'existing-block-id']];
+  public function testGetComponent() {
+    $expected = new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']);
 
-    $block = $this->section->getBlock('some-region', 'existing-uuid');
-    $this->assertSame($expected, $block);
+    $this->assertEquals($expected, $this->section->getComponent('existing-uuid'));
   }
 
   /**
-   * @covers ::removeBlock
+   * @covers ::removeComponent
+   * @covers ::getComponentsByRegion
    */
-  public function testRemoveBlock() {
-    $this->section->removeBlock('some-region', 'existing-uuid');
+  public function testRemoveComponent() {
     $expected = [
-      'ordered-region' => [
-        'first-uuid' => [
-          'block' => [
-            'id' => 'first-block-id',
-          ],
-        ],
-        'second-uuid' => [
-          'block' => [
-            'id' => 'second-block-id',
-          ],
-        ],
-      ],
+      'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+      'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
     ];
-    $this->assertSame($expected, $this->section->getValue());
+
+    $this->section->removeComponent('first-uuid');
+    $this->assertComponents($expected, $this->section);
   }
 
   /**
-   * @covers ::addBlock
+   * @covers ::appendComponent
+   * @covers ::getNextHighestWeight
+   * @covers ::getComponentsByRegion
    */
-  public function testAddBlock() {
-    $this->section->addBlock('some-region', 'new-uuid', []);
+  public function testAppendComponent() {
     $expected = [
-      'empty-region' => [],
-      'some-region' => [
-        'new-uuid' => [],
-        'existing-uuid' => [
-          'block' => [
-            'id' => 'existing-block-id',
-          ],
-        ],
-      ],
-      'ordered-region' => [
-        'first-uuid' => [
-          'block' => [
-            'id' => 'first-block-id',
-          ],
-        ],
-        'second-uuid' => [
-          'block' => [
-            'id' => 'second-block-id',
-          ],
-        ],
-      ],
+      'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+      'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
+      'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
+      'new-uuid' => (new SectionComponent('new-uuid', 'some-region', []))->setWeight(1),
     ];
-    $this->assertSame($expected, $this->section->getValue());
+
+    $this->section->appendComponent(new SectionComponent('new-uuid', 'some-region'));
+    $this->assertComponents($expected, $this->section);
   }
 
   /**
-   * @covers ::insertBlock
+   * @covers ::insertAfterComponent
    */
-  public function testInsertBlock() {
-    $this->section->insertBlock('ordered-region', 'new-uuid', [], 'first-uuid');
+  public function testInsertAfterComponent() {
     $expected = [
-      'empty-region' => [],
-      'some-region' => [
-        'existing-uuid' => [
-          'block' => [
-            'id' => 'existing-block-id',
-          ],
-        ],
-      ],
-      'ordered-region' => [
-        'first-uuid' => [
-          'block' => [
-            'id' => 'first-block-id',
-          ],
-        ],
-        'new-uuid' => [],
-        'second-uuid' => [
-          'block' => [
-            'id' => 'second-block-id',
-          ],
-        ],
-      ],
+      'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+      'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(4),
+      'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
+      'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(3),
     ];
-    $this->assertSame($expected, $this->section->getValue());
+
+    $this->section->insertAfterComponent('first-uuid', new SectionComponent('new-uuid', 'ordered-region'));
+    $this->assertComponents($expected, $this->section);
   }
 
   /**
-   * @covers ::insertBlock
+   * @covers ::insertAfterComponent
    */
-  public function testInsertBlockInvalidRegion() {
-    $this->setExpectedException(\InvalidArgumentException::class, 'Invalid region');
-    $this->section->insertBlock('invalid-region', 'new-uuid', [], 'first-uuid');
+  public function testInsertAfterComponentValidUuidRegionMismatch() {
+    $this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID "existing-uuid"');
+    $this->section->insertAfterComponent('existing-uuid', new SectionComponent('new-uuid', 'ordered-region'));
   }
 
   /**
-   * @covers ::insertBlock
+   * @covers ::insertAfterComponent
    */
-  public function testInsertBlockInvalidUuid() {
-    $this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID');
-    $this->section->insertBlock('ordered-region', 'new-uuid', [], 'invalid-uuid');
+  public function testInsertAfterComponentInvalidUuid() {
+    $this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID "invalid-uuid"');
+    $this->section->insertAfterComponent('invalid-uuid', new SectionComponent('new-uuid', 'ordered-region'));
   }
 
   /**
-   * @covers ::updateBlock
+   * @covers ::insertComponent
+   * @covers ::getComponentsByRegion
    */
-  public function testUpdateBlock() {
-    $this->section->updateBlock('some-region', 'existing-uuid', [
-      'block' => [
-        'id' => 'existing-block-id',
-        'settings' => [
-          'foo' => 'bar',
-        ],
-      ],
-    ]);
+  public function testInsertComponent() {
+    $expected = [
+      'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+      'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(4),
+      'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(3),
+      'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(2),
+    ];
+
+    $this->section->insertComponent(0, new SectionComponent('new-uuid', 'ordered-region'));
+    $this->assertComponents($expected, $this->section);
+  }
 
+  /**
+   * @covers ::insertComponent
+   */
+  public function testInsertComponentAppend() {
     $expected = [
-      'empty-region' => [],
-      'some-region' => [
-        'existing-uuid' => [
-          'block' => [
-            'id' => 'existing-block-id',
-            'settings' => [
-              'foo' => 'bar',
-            ],
-          ],
-        ],
-      ],
-      'ordered-region' => [
-        'first-uuid' => [
-          'block' => [
-            'id' => 'first-block-id',
-          ],
-        ],
-        'second-uuid' => [
-          'block' => [
-            'id' => 'second-block-id',
-          ],
-        ],
-      ],
+      'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+      'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
+      'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
+      'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(4),
     ];
-    $this->assertSame($expected, $this->section->getValue());
+
+    $this->section->insertComponent(2, new SectionComponent('new-uuid', 'ordered-region'));
+    $this->assertComponents($expected, $this->section);
   }
 
   /**
-   * @covers ::updateBlock
+   * @covers ::insertComponent
    */
-  public function testUpdateBlockInvalidRegion() {
-    $this->setExpectedException(\InvalidArgumentException::class, 'Invalid region');
-    $this->section->updateBlock('invalid-region', 'new-uuid', []);
+  public function testInsertComponentInvalidDelta() {
+    $this->setExpectedException(\OutOfBoundsException::class, 'Invalid delta "7" for the "new-uuid" component');
+    $this->section->insertComponent(7, new SectionComponent('new-uuid', 'ordered-region'));
   }
 
   /**
-   * @covers ::updateBlock
+   * Asserts that the section has the expected components.
+   *
+   * @param \Drupal\layout_builder\SectionComponent[] $expected
+   *   The expected sections.
+   * @param \Drupal\layout_builder\Section $section
+   *   The section storage to check.
    */
-  public function testUpdateBlockInvalidUuid() {
-    $this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID');
-    $this->section->updateBlock('ordered-region', 'new-uuid', []);
+  protected function assertComponents(array $expected, Section $section) {
+    $result = $section->getComponents();
+    $this->assertEquals($expected, $result);
+    $this->assertSame(array_keys($expected), array_keys($result));
   }
 
 }