diff --git a/config/schema/panels.schema.yml b/config/schema/panels.schema.yml
new file mode 100644
index 0000000..290aa04
--- /dev/null
+++ b/config/schema/panels.schema.yml
@@ -0,0 +1,61 @@
+# Schema for the configuration of the panels module.
+
+panels.display.*:
+  type: config_entity
+  label: Display
+  mapping:
+    id:
+      type: string
+      label: 'Machine-readable name'
+    layout:
+      type: string
+      label: 'Layout'
+    blocks:
+      type: sequence
+      label: 'Blocks'
+      sequence:
+        - type: panels.block_plugin.[id]
+    title:
+      type: string
+      label: 'Title'
+    hide_title:
+      type: boolean
+      label: 'Whether or not the title should be hidden.'
+
+panels.block_plugin.*:
+  type: block.settings.[id]
+  mapping:
+    region:
+      type: string
+      label: 'Region'
+    weight:
+      type: integer
+      label: 'Weight'
+    uuid:
+      type: string
+      label: 'UUID'
+    context_mapping:
+      type: sequence
+      label: 'Context assignments'
+      sequence:
+        - type: string
+
+display_variant.plugin.panels_variant:
+  type: display_variant.plugin
+  label: 'Panels variant'
+  mapping:
+    selection_logic:
+      type: string
+      label: 'Selection logic'
+    selection_conditions:
+      type: sequence
+      label: 'Selection Conditions'
+      sequence:
+        - type: condition.plugin.[id]
+          label: 'Selection Condition'
+    page_title:
+      type: label
+      label: 'Page title'
+    display:
+      type: string
+      label: 'Display'
\ No newline at end of file
diff --git a/panels.routing.yml b/panels.routing.yml
new file mode 100644
index 0000000..6a69cf0
--- /dev/null
+++ b/panels.routing.yml
@@ -0,0 +1,33 @@
+#### Display Block Configuration
+
+panels.display_select_block:
+  path: '/admin/structure/panels/display/{panels_display}/block/select'
+  defaults:
+    _controller: '\Drupal\panels\Controller\PanelsDisplayManagerController::selectBlock'
+    _title: 'Select block'
+  requirements:
+    _entity_access: panels_display.update
+
+panels.display_add_block:
+  path: '/admin/structure/panels/display/{panels_display}/block/add/{block_id}'
+  defaults:
+    _form: '\Drupal\panels\Form\PanelsDisplayAddBlockForm'
+    _title: 'Add block to display'
+  requirements:
+    _entity_access: panels_display.update
+
+panels.display_edit_block:
+  path: '/admin/structure/panels/display/{panels_display}/block/edit/{block_id}'
+  defaults:
+    _form: '\Drupal\panels\Form\PanelsDisplayEditBlockForm'
+    _title: 'Edit block in display'
+  requirements:
+    _entity_access: panels_display.update
+
+panels.display_delete_block:
+  path: '/admin/structure/panels/display/{panels_display}/block/delete/{block_id}'
+  defaults:
+    _form: '\Drupal\panels\Form\PanelsDisplayDeleteBlockForm'
+    _title: 'Delete block in display'
+  requirements:
+    _entity_access: panels_display.update
\ No newline at end of file
diff --git a/src/Controller/PanelsDisplayManagerController.php b/src/Controller/PanelsDisplayManagerController.php
new file mode 100644
index 0000000..08a583d
--- /dev/null
+++ b/src/Controller/PanelsDisplayManagerController.php
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panels\Controller\PanelsDisplayManagerController.
+ */
+
+namespace Drupal\panels\Controller;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Block\BlockManagerInterface;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Plugin\Context\ContextHandlerInterface;
+use Drupal\Core\Url;
+use Drupal\page_manager\Form\AjaxFormTrait;
+use Drupal\panels\Entity\PanelsDisplay;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides route controllers for Page Manager.
+ */
+class PanelsDisplayManagerController extends ControllerBase {
+
+  use AjaxFormTrait;
+
+  /**
+   * The block manager.
+   *
+   * @var \Drupal\Core\Block\BlockManagerInterface
+   */
+  protected $blockManager;
+
+  /**
+   * The condition manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $conditionManager;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The context handler.
+   *
+   * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
+   */
+  protected $contextHandler;
+
+  /**
+   * Constructs a new PanelsDisplayManagerController.
+   *
+   * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
+   *   The block manager.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $condition_manager
+   *   The condition manager.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
+   *   The context handler.
+   */
+  public function __construct(BlockManagerInterface $block_manager, PluginManagerInterface $condition_manager, EntityManagerInterface $entity_manager, ContextHandlerInterface $context_handler) {
+    $this->blockManager = $block_manager;
+    $this->conditionManager = $condition_manager;
+    $this->entityManager = $entity_manager;
+    $this->contextHandler = $context_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.block'),
+      $container->get('plugin.manager.condition'),
+      $container->get('entity.manager'),
+      $container->get('context.handler')
+    );
+  }
+
+  /**
+   * Presents a list of blocks to add to the display.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request.
+   * @param \Drupal\panels\Entity\PanelsDisplay $panels_display
+   *   The panels display entity.
+   *
+   * @return array
+   *   The block selection page.
+   */
+  public function selectBlock(Request $request, PanelsDisplay $panels_display) {
+    // Add a section containing the available blocks to be added to the variant.
+    $build = [
+      '#type' => 'container',
+      '#attached' => [
+        'library' => [
+          'core/drupal.ajax',
+        ],
+      ],
+    ];
+    $available_plugins = $this->blockManager->getDefinitionsForContexts($panels_display->getContexts());
+    foreach ($available_plugins as $plugin_id => $plugin_definition) {
+      // Make a section for each region.
+      $category = $plugin_definition['category'];
+      $category_key = 'category-' . $category;
+      if (!isset($build[$category_key])) {
+        $build[$category_key] = [
+          '#type' => 'fieldgroup',
+          '#title' => $category,
+          'content' => [
+            '#theme' => 'links',
+          ],
+        ];
+      }
+      // Add a link for each available block within each region.
+      $build[$category_key]['content']['#links'][$plugin_id] = [
+        'title' => $plugin_definition['admin_label'],
+        'url' => Url::fromRoute('panels.display_add_block', [
+          'panels_display' => $panels_display->id(),
+          'block_id' => $plugin_id,
+          'region' => $request->query->get('region'),
+        ]),
+        'attributes' => $this->getAjaxAttributes(),
+      ];
+    }
+    return $build;
+  }
+
+}
diff --git a/src/Entity/PanelsDisplay.php b/src/Entity/PanelsDisplay.php
new file mode 100644
index 0000000..6b66a93
--- /dev/null
+++ b/src/Entity/PanelsDisplay.php
@@ -0,0 +1,291 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panels\Entity\PanelsDisplay.
+ */
+
+namespace Drupal\panels\Entity;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
+use Drupal\Core\Render\Element;
+use Drupal\page_manager\Plugin\BlockPluginCollection;
+use Drupal\page_manager\Plugin\BlockVariantTrait;
+use Drupal\page_manager\Plugin\ContextAwareVariantTrait;
+
+/**
+ * Defines the Panels Display configuration entity.
+ *
+ * @ConfigEntityType(
+ *   id = "panels_display",
+ *   label = @Translation("Display"),
+ *   config_prefix = "display",
+ *   entity_keys = {
+ *     "id" = "id",
+ *   },
+ *   handlers = {
+ *     "access" = "Drupal\panels\Entity\PanelsDisplayAccess",
+ *     "form" = {
+ *       "add" = "Drupal\panels\PanelsDisplayForm",
+ *       "edit" = "Drupal\panels\PanelsDisplayForm"
+ *     }
+ *   },
+ *   config_export = {
+ *     "id",
+ *     "layout",
+ *     "layout_settings",
+ *     "panel_settings",
+ *     "blocks",
+ *     "title",
+ *     "hide_title",
+ *   }
+ * )
+ *
+ * Although we use BlockVariantTrait and ContextAwareVariantTrait, we do not
+ * implement these interfaces as VariantInterface::access() is not compatible
+ * with Entity::access().
+ * @todo: Rename BlockVariantTrait to BlockConfigurationCollectionTrait?
+ * @todo: Replace ContextAwareVariantTrait with our own code as it is much
+ *        smaller?
+ * @todo: Do the actual work of assigning contexts in a PanelsDisplayVariant?
+ */
+class PanelsDisplay extends ConfigEntityBase implements EntityWithPluginCollectionInterface {
+  use BlockVariantTrait;
+  use ContextAwareVariantTrait;
+
+  /**
+   * The machine name of this panels display.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The layout plugin to be used.
+   *
+   * @var string
+   */
+  protected $layout;
+
+  /**
+   * The layout settings.
+   *
+   * @var array
+   */
+  protected $layout_settings = [];
+
+  /**
+   * The layout plugin.
+   *
+   * @var \Drupal\layout_plugin\Plugin\Layout\LayoutInterface
+   */
+  protected $layout_plugin;
+
+  /**
+   * The panel settings.
+   *
+   * @var array
+   */
+  protected $panel_settings = [];
+
+  /**
+   * The array of block configurations.
+   *
+   * @var array
+   */
+  protected $blocks = [];
+
+  /**
+   * The panel title.
+   *
+   * @var string
+   */
+  protected $title;
+
+  /**
+   * Whether or not the title is hidden.
+   *
+   * @var bool
+   */
+  protected $hide_title;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getBlockConfig() {
+    return $this->blocks;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBlockConfiguration() {
+    return $this->blocks;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function uuidGenerator() {
+    return \Drupal::service('uuid');
+  }
+
+  /**
+   * Return the layout plugin manager.
+   *
+   * @return \Drupal\layout_plugin\Plugin\Layout\LayoutPluginManagerInterface
+   */
+  protected function layoutPluginManager() {
+    return \Drupal::service('plugin.manager.layout_plugin');
+  }
+
+  /**
+   * Get the layout plugin to use for this display.
+   *
+   * @return \Drupal\layout_plugin\Plugin\Layout\LayoutInterface
+   */
+  public function layoutPlugin() {
+    if (!isset($this->layout_plugin)) {
+      $this->layout_plugin = $this->layoutPluginManager()->createInstance($this->layout, $this->layout_settings);
+    }
+    return $this->layout_plugin;
+  }
+
+  /**
+   * Build this display.
+   *
+   * @return array
+   *    A render array for the display.
+   */
+  public function build() {
+    /** @var \Drupal\layout_plugin\Plugin\Layout\LayoutInterface **/
+    $layout = $this->layoutPlugin();
+    $block_regions = $this->getRegionAssignments();
+
+    $build['#cache']['keys'] = [
+      'panels_display',
+      $this->id(),
+    ];
+
+    $cacheability = CacheableMetadata::createFromRenderArray($build)
+      ->addCacheableDependency($this);
+
+    $content = [];
+    foreach ($layout->getRegionDefinitions() as $region => $region_settings) {
+      $content[$region] = $this->buildRegion($block_regions[$region], $region_settings, $cacheability);
+    }
+    $build += $layout->build($content);
+
+    return $build;
+  }
+
+  /**
+   * Build a particular region.
+   *
+   * @param \Drupal\Core\Block\BlockPluginInterface[] $blocks
+   * @param array $region_settings
+   * @param \Drupal\Core\Cache\CacheableMetadata $cacheability
+   */
+  public function buildRegion(array $blocks, array $region_settings, CacheableMetadata $cacheability) {
+    $content = [];
+    /** @var $blocks \Drupal\Core\Block\BlockPluginInterface[] */
+    $weight = 0;
+    foreach ($blocks as $block_id => $block) {
+      if ($block instanceof ContextAwarePluginInterface) {
+        $this->contextHandler()->applyContextMapping($block, $contexts);
+      }
+      $access = $block->access(\Drupal::currentUser(), TRUE);
+      $cacheability->addCacheableDependency($access);
+      if (!$access->isAllowed()) {
+        continue;
+      }
+
+      $block_build = [
+        '#theme' => 'block',
+        '#attributes' => [],
+        '#weight' => $weight++,
+        '#configuration' => $block->getConfiguration(),
+        '#plugin_id' => $block->getPluginId(),
+        '#base_plugin_id' => $block->getBaseId(),
+        '#derivative_plugin_id' => $block->getDerivativeId(),
+        '#block_plugin' => $block,
+        '#pre_render' => [[$this, 'buildBlock']],
+        '#cache' => [
+          'keys' => ['panels_display', $this->id(), 'block', $block_id],
+          // Each block needs cache tags of the display and the block plugin, as
+          // only the display is a config entity that will trigger cache tag
+          // invalidations in case of block configuration changes.
+          'tags' => Cache::mergeTags($this->getCacheTags(), $block->getCacheTags()),
+          'contexts' => $block->getCacheContexts(),
+          'max-age' => $block->getCacheMaxAge(),
+        ],
+      ];
+
+      // Merge the cacheability metadata of blocks into the display. This helps
+      // to avoid cache redirects if the blocks have more cache contexts than
+      // the display, which the display must respect as well.
+      $cacheability->addCacheableDependency($block);
+
+      $content[$block_id] = $block_build;
+    }
+
+    return $content;
+  }
+
+  /**
+   * #pre_render callback for building a block.
+   *
+   * Renders the content using the provided block plugin, if there is no
+   * content, aborts rendering, and makes sure the block won't be rendered.
+   */
+  public function buildBlock($build) {
+    $content = $build['#block_plugin']->build();
+    // Remove the block plugin from the render array.
+    unset($build['#block_plugin']);
+    if ($content !== NULL && !Element::isEmpty($content)) {
+      $build['content'] = $content;
+    }
+    else {
+      // Abort rendering: render as the empty string and ensure this block is
+      // render cached, so we can avoid the work of having to repeatedly
+      // determine whether the block is empty. E.g. modifying or adding entities
+      // could cause the block to no longer be empty.
+      $build = [
+        '#markup' => '',
+        '#cache' => $build['#cache'],
+      ];
+    }
+    // If $content is not empty, then it contains cacheability metadata, and
+    // we must merge it with the existing cacheability metadata. This allows
+    // blocks to be empty, yet still bubble cacheability metadata, to indicate
+    // why they are empty.
+    if (!empty($content)) {
+      CacheableMetadata::createFromRenderArray($build)
+        ->merge(CacheableMetadata::createFromRenderArray($content))
+        ->applyTo($build);
+    }
+
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRegionNames() {
+    return $this->layoutPlugin()->getRegionNames();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginCollections() {
+    return [
+      'blocks' => $this->getBlockCollection(),
+    ];
+  }
+
+}
diff --git a/src/Entity/PanelsDisplayAccess.php b/src/Entity/PanelsDisplayAccess.php
new file mode 100644
index 0000000..b002672
--- /dev/null
+++ b/src/Entity/PanelsDisplayAccess.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panels\Entity\PanelsDisplayAccess.
+ */
+
+namespace Drupal\panels\Entity;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Entity\EntityAccessControlHandler;
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Defines the access control handler for the page entity type.
+ */
+class PanelsDisplayAccess extends EntityAccessControlHandler implements EntityHandlerInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static($entity_type);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
+    dpm($entity);
+    // @todo: Implement something that defers access checks to whatever is
+    //        using this display, i.e. the Page entity or mini-panel.
+    return AccessResult::allowed();
+  }
+
+}
diff --git a/src/Form/PanelsDisplayAddBlockForm.php b/src/Form/PanelsDisplayAddBlockForm.php
new file mode 100644
index 0000000..e955021
--- /dev/null
+++ b/src/Form/PanelsDisplayAddBlockForm.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panels\Form\PanelsDisplayAddBlockForm.
+ */
+
+namespace Drupal\panels\Form;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\panels\Entity\PanelsDisplay;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides a form for adding a block plugin to a display variant.
+ */
+class PanelsDisplayAddBlockForm extends PanelsDisplayConfigureBlockFormBase {
+
+  /**
+   * The block manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $blockManager;
+
+  /**
+   * Constructs a new PanelsDisplayFormBase.
+   */
+  public function __construct(PluginManagerInterface $block_manager) {
+    $this->blockManager = $block_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.block')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panels_display_add_block_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function prepareBlock($plugin_id) {
+    $block = $this->blockManager->createInstance($plugin_id);
+    $block_id = $this->display->addBlock($block->getConfiguration());
+    return $this->display->getBlock($block_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, PanelsDisplay $panels_display = NULL, $block_id = NULL) {
+    $form = parent::buildForm($form, $form_state, $panels_display, $block_id);
+    $form['region']['#default_value'] = $request->query->get('region');
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function submitText() {
+    return $this->t('Add block');
+  }
+
+}
diff --git a/src/Form/PanelsDisplayConfigureBlockFormBase.php b/src/Form/PanelsDisplayConfigureBlockFormBase.php
new file mode 100644
index 0000000..149d6e7
--- /dev/null
+++ b/src/Form/PanelsDisplayConfigureBlockFormBase.php
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panels\Form\PanelsDisplayConfigureBlockFormBase.
+ */
+
+namespace Drupal\panels\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+use Drupal\panels\Entity\PanelsDisplay;
+
+/**
+ * Provides a base form for configuring a block as part of a display variant.
+ */
+abstract class PanelsDisplayConfigureBlockFormBase extends FormBase {
+
+  use ContextAwarePluginAssignmentTrait;
+
+  /**
+   * The panels display entity.
+   *
+   * @var \Drupal\panels\Entity\PanelsDisplay
+   */
+  protected $panels_display;
+
+  /**
+   * The plugin being configured.
+   *
+   * @var \Drupal\Core\Block\BlockPluginInterface
+   */
+  protected $block;
+
+  /**
+   * Prepares the block plugin based on the block ID.
+   *
+   * @param string $block_id
+   *   Either a block ID, or the plugin ID used to create a new block.
+   *
+   * @return \Drupal\Core\Block\BlockPluginInterface
+   *   The block plugin.
+   */
+  abstract protected function prepareBlock($block_id);
+
+  /**
+   * Returns the text to use for the submit button.
+   *
+   * @return string
+   *   The submit button text.
+   */
+  abstract protected function submitText();
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, PanelsDisplay $panels_display = NULL, $block_id = NULL) {
+    $this->display = $panels_display;
+    $this->block = $this->prepareBlock($block_id);
+    $form_state->set('display', $this->display->id());
+    $form_state->set('block_id', $this->block->getConfiguration()['uuid']);
+
+    $form['#tree'] = TRUE;
+    $form['settings'] = $this->block->buildConfigurationForm([], $form_state);
+    $form['settings']['id'] = [
+      '#type' => 'value',
+      '#value' => $this->block->getPluginId(),
+    ];
+    $form['region'] = [
+      '#title' => $this->t('Region'),
+      '#type' => 'select',
+      '#options' => $this->display->getRegionNames(),
+      '#default_value' => $this->display->getRegionAssignment($this->block->getConfiguration()['uuid']),
+      '#required' => TRUE,
+    ];
+
+    if ($this->block instanceof ContextAwarePluginInterface) {
+      $form['context_mapping'] = $this->addContextAssignmentElement($this->block, $this->display->getContexts());
+    }
+
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->submitText(),
+      '#button_type' => 'primary',
+    ];
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $this->block = $this->display->getBlock($form_state->get('block_id'));
+
+    $settings = (new FormState())->setValues($form_state->getValue('settings'));
+    // Call the plugin validate handler.
+    $this->block->validateConfigurationForm($form, $settings);
+    // Update the original form values.
+    $form_state->setValue('settings', $settings->getValues());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $settings = (new FormState())->setValues($form_state->getValue('settings'));
+
+    // Call the plugin submit handler.
+    $this->block->submitConfigurationForm($form, $settings);
+    // Update the original form values.
+    $form_state->setValue('settings', $settings->getValues());
+
+    if ($this->block instanceof ContextAwarePluginInterface) {
+      $this->block->setContextMapping($form_state->getValue('context_mapping', []));
+    }
+
+    $this->display->updateBlock($this->block->getConfiguration()['uuid'], ['region' => $form_state->getValue('region')]);
+    $this->display->save();
+
+    // @todo: Do we need to redirect somewhere? If so, where? Does destination
+    //        still exist?
+  }
+
+}
diff --git a/src/Form/PanelsDisplayDeleteBlockForm.php b/src/Form/PanelsDisplayDeleteBlockForm.php
new file mode 100644
index 0000000..8f4dc39
--- /dev/null
+++ b/src/Form/PanelsDisplayDeleteBlockForm.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panels\Form\PanelsDisplayDeleteBlockForm.
+ */
+
+namespace Drupal\panels\Form;
+
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\panels\Entity\PanelsDisplay;
+
+/**
+ * Provides a form for deleting an access condition.
+ */
+class PanelsDisplayDeleteBlockForm extends ConfirmFormBase {
+
+  /**
+   * The page entity.
+   *
+   * @var \Drupal\panels\Entity\PanelsDisplay
+   */
+  protected $panels_display;
+
+  /**
+   * The plugin being configured.
+   *
+   * @var \Drupal\Core\Block\BlockPluginInterface
+   */
+  protected $block;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panels_display_delete_block_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to delete the block %label?', ['%label' => $this->block->label()]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    // @todo: What URL do we use here?
+    return new Url('<front>');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, PanelsDisplay $panels_display = NULL, $block_id = NULL) {
+    $this->display = $panels_display;
+    $this->block = $this->display->getBlock($block_id);
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->display->removeBlock($this->block->getConfiguration()['uuid']);
+    $this->display->save();
+    drupal_set_message($this->t('The block %label has been removed.', ['%label' => $this->block->label()]));
+
+    // @todo: What URL is best to use.
+    $form_state->setRedirectUrl($this->getCancelUrl());
+  }
+
+}
diff --git a/src/Form/PanelsDisplayEditBlockForm.php b/src/Form/PanelsDisplayEditBlockForm.php
new file mode 100644
index 0000000..e0c514f
--- /dev/null
+++ b/src/Form/PanelsDisplayEditBlockForm.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panels\Form\PanelsDisplayEditBlockForm.
+ */
+
+namespace Drupal\panels\Form;
+
+/**
+ * Provides a form for editing a block plugin of a display variant.
+ */
+class PanelsDisplayEditBlockForm extends PanelsDisplayConfigureBlockFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panels_display_variant_edit_block_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function prepareBlock($block_id) {
+    return $this->displayVariant->getBlock($block_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function submitText() {
+    return $this->t('Update block');
+  }
+
+}
diff --git a/src/PanelsDisplayForm.php b/src/PanelsDisplayForm.php
new file mode 100644
index 0000000..f3cd6f6
--- /dev/null
+++ b/src/PanelsDisplayForm.php
@@ -0,0 +1,242 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panels\PanelsDisplayForm.
+ */
+
+namespace Drupal\panels;
+
+use Drupal\layout_plugin\Layout;
+use Drupal\Component\Serialization\Json;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Form controller for panels display forms.
+ */
+class PanelsDisplayForm extends EntityForm {
+
+  /**
+   * Constructs the PanelsDisplayForm object.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager
+   */
+  public function __construct(EntityManagerInterface $entity_manager) {
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, FormStateInterface $form_state) {
+    $display = $this->entity;
+
+    $form['id'] = [
+      '#type' => 'machine_name',
+      '#title' => t('Machine Name'),
+      '#default_value' => $display->id(),
+      '#required' => TRUE,
+      '#machine_name' => [
+        'exists' => ['Drupal\panels\Entity\PanelsDisplay', 'load'],
+      ],
+    ];
+
+    // Do not allow blocks to be added until the display variant has been saved.
+    if (!$display->id()) {
+      $form['layout'] = [
+        '#title' => $this->t('Layout'),
+        '#type' => 'select',
+        '#options' => Layout::getLayoutOptions(['group_by_category' => TRUE]),
+        '#default_value' => NULL
+      ];
+
+      return $form;
+    }
+
+    // Set up the attributes used by a modal to prevent duplication later.
+    $attributes = [
+      'class' => ['use-ajax'],
+      'data-dialog-type' => 'modal',
+      'data-dialog-options' => Json::encode([
+        'width' => 'auto',
+      ]),
+    ];
+    $add_button_attributes = NestedArray::mergeDeep($attributes, [
+      'class' => [
+        'button',
+        'button--small',
+        'button-action',
+      ],
+    ]);
+
+    // Blocks Section.
+    if ($block_assignments = $display->getRegionAssignments()) {
+      // Build a table of all blocks used by this display.
+      $form['block_section'] = [
+        '#type' => 'details',
+        '#title' => $this->t('Blocks'),
+        '#open' => TRUE,
+      ];
+      $form['block_section']['add'] = [
+        '#type' => 'link',
+        '#title' => $this->t('Add new block'),
+        '#url' => Url::fromRoute('panels.display_select_block', [
+          'panels_display' => $display->id(),
+        ]),
+        '#attributes' => $add_button_attributes,
+        '#attached' => [
+          'library' => [
+            'core/drupal.ajax',
+          ],
+        ],
+      ];
+      $form['block_section']['blocks'] = [
+        '#type' => 'table',
+        '#header' => [
+          $this->t('Label'),
+          $this->t('Plugin ID'),
+          $this->t('Region'),
+          $this->t('Weight'),
+          $this->t('Operations'),
+        ],
+        '#empty' => $this->t('There are no regions for blocks.'),
+        // @todo This should utilize https://drupal.org/node/2065485.
+        '#parents' => ['display_variant', 'blocks'],
+      ];
+      // Loop through the blocks per region.
+      foreach ($block_assignments as $region => $blocks) {
+        // Add a section for each region and allow blocks to be dragged between
+        // them.
+        $form['block_section']['blocks']['#tabledrag'][] = [
+          'action' => 'match',
+          'relationship' => 'sibling',
+          'group' => 'block-region-select',
+          'subgroup' => 'block-region-' . $region,
+          'hidden' => FALSE,
+        ];
+        $form['block_section']['blocks']['#tabledrag'][] = [
+          'action' => 'order',
+          'relationship' => 'sibling',
+          'group' => 'block-weight',
+          'subgroup' => 'block-weight-' . $region,
+        ];
+        $form['block_section']['blocks'][$region] = [
+          '#attributes' => [
+            'class' => ['region-title', 'region-title-' . $region],
+            'no_striping' => TRUE,
+          ],
+        ];
+        $form['block_section']['blocks'][$region]['title'] = [
+          '#markup' => $display->getRegionName($region),
+          '#wrapper_attributes' => [
+            'colspan' => 5,
+          ],
+        ];
+        $form['block_section']['blocks'][$region . '-message'] = [
+          '#attributes' => [
+            'class' => [
+              'region-message',
+              'region-' . $region . '-message',
+              empty($blocks) ? 'region-empty' : 'region-populated',
+            ],
+          ],
+        ];
+        $form['block_section']['blocks'][$region . '-message']['message'] = [
+          '#markup' => '<em>' . t('No blocks in this region') . '</em>',
+          '#wrapper_attributes' => [
+            'colspan' => 5,
+          ],
+        ];
+
+        /** @var $blocks \Drupal\block\BlockPluginInterface[] */
+        foreach ($blocks as $block_id => $block) {
+          $row = [
+            '#attributes' => [
+              'class' => ['draggable'],
+            ],
+          ];
+          $row['label']['#markup'] = $block->label();
+          $row['id']['#markup'] = $block->getPluginId();
+          // Allow the region to be changed for each block.
+          $row['region'] = [
+            '#title' => $this->t('Region'),
+            '#title_display' => 'invisible',
+            '#type' => 'select',
+            '#options' => $display->getRegionNames(),
+            '#default_value' => $display->getRegionAssignment($block_id),
+            '#attributes' => [
+              'class' => ['block-region-select', 'block-region-' . $region],
+            ],
+          ];
+          // Allow the weight to be changed for each block.
+          $configuration = $block->getConfiguration();
+          $row['weight'] = [
+            '#type' => 'weight',
+            '#default_value' => isset($configuration['weight']) ? $configuration['weight'] : 0,
+            '#title' => t('Weight for @block block', ['@block' => $block->label()]),
+            '#title_display' => 'invisible',
+            '#attributes' => [
+              'class' => ['block-weight', 'block-weight-' . $region],
+            ],
+          ];
+          // Add the operation links.
+          $operations = [];
+          $operations['edit'] = [
+            'title' => $this->t('Edit'),
+            'url' => Url::fromRoute('panels.display_edit_block', [
+              'panels_display' => $display->id(),
+              'block_id' => $block_id,
+            ]),
+            'attributes' => $attributes,
+          ];
+          $operations['delete'] = [
+            'title' => $this->t('Delete'),
+            'url' => Url::fromRoute('panels.display_delete_block', [
+              'panels_display' => $display->id(),
+              'block_id' => $block_id,
+            ]),
+            'attributes' => $attributes,
+          ];
+
+          $row['operations'] = [
+            '#type' => 'operations',
+            '#links' => $operations,
+          ];
+          $form['block_section']['blocks'][$block_id] = $row;
+        }
+      }
+
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function actions(array $form, FormStateInterface $form_state) {
+    // To allow us to show this form as part of a variant plugin configuration,
+    // we don't show any actions if the 'display_variant_form' key is set to
+    // true in the form state.
+    if ($form_state->get('display_variant_form')) {
+      return [];
+    }
+
+    return parent::actions($form, $form_state);
+  }
+}
\ No newline at end of file
diff --git a/src/Plugin/DisplayVariant/PanelsDisplayVariant.php b/src/Plugin/DisplayVariant/PanelsDisplayVariant.php
index e8a9e6f..d505527 100644
--- a/src/Plugin/DisplayVariant/PanelsDisplayVariant.php
+++ b/src/Plugin/DisplayVariant/PanelsDisplayVariant.php
@@ -7,22 +7,16 @@
 
 namespace Drupal\panels\Plugin\DisplayVariant;
 
-use Drupal\Component\Serialization\Json;
-use Drupal\Component\Utility\Html;
-use Drupal\Component\Utility\NestedArray;
-use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Uuid\UuidInterface;
+use Drupal\Core\Config\Entity\ConfigEntityStorage;
 use Drupal\Core\Display\VariantBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Component\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
 use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\Url;
 use Drupal\page_manager\PageExecutableInterface;
 use Drupal\Core\Utility\Token;
-use Drupal\page_manager\Plugin\BlockVariantInterface;
-use Drupal\page_manager\Plugin\BlockVariantTrait;
 use Drupal\page_manager\Plugin\ConditionVariantInterface;
 use Drupal\page_manager\Plugin\ConditionVariantTrait;
 use Drupal\page_manager\Plugin\ContextAwareVariantInterface;
@@ -40,9 +34,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *   admin_label = @Translation("Panels")
  * )
  */
-class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInterface, ConditionVariantInterface, ContainerFactoryPluginInterface, PageAwareVariantInterface, BlockVariantInterface {
+class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInterface, ConditionVariantInterface, ContainerFactoryPluginInterface, PageAwareVariantInterface {
 
-  use BlockVariantTrait;
   use ContextAwareVariantTrait;
   use ConditionVariantTrait;
 
@@ -54,6 +47,13 @@ class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInt
   protected $layout;
 
   /**
+   * The display entity.
+   *
+   * @var \Drupal\panels\Entity\PanelsDisplay
+   */
+  protected $display;
+
+  /**
    * The context handler.
    *
    * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
@@ -89,6 +89,13 @@ class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInt
   protected $token;
 
   /**
+   * The panels display entity storage handler.
+   *
+   * @var \Drupal\Core\Config\Entity\ConfigEntityStorage
+   */
+  protected $displayStorage;
+
+  /**
    * Constructs a new PanelsDisplayVariant.
    *
    * @param array $configuration
@@ -105,14 +112,18 @@ class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInt
    *   The UUID generator.
    * @param \Drupal\Core\Utility\Token $token
    *   The token service.
+   * @param \Drupal\Core\Config\Entity\ConfigEntityStorage
+   *   The panels display entity storage handler.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, ContextHandlerInterface $context_handler, AccountInterface $account, UuidInterface $uuid_generator, Token $token) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, ContextHandlerInterface $context_handler, AccountInterface $account, UuidInterface $uuid_generator, Token $token, ConfigEntityStorage $display_storage) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
 
     $this->contextHandler = $context_handler;
     $this->account = $account;
     $this->uuidGenerator = $uuid_generator;
     $this->token = $token;
+    $this->displayStorage = $display_storage;
+
   }
 
   /**
@@ -126,93 +137,67 @@ class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInt
       $container->get('context.handler'),
       $container->get('current_user'),
       $container->get('uuid'),
-      $container->get('token')
+      $container->get('token'),
+      $container->get('entity.manager')->getStorage('panels_display')
     );
   }
 
   /**
-   * Returns instance of the layout plugin used by this page variant.
+   * Returns the display entity.
    *
-   * @return \Drupal\layout_plugin\Plugin\Layout\LayoutInterface
-   *   Layout plugin instance.
+   * @return \Drupal\panels\Entity\PanelsDisplay
+   *   The Display Entity.
    */
-  public function getLayout() {
-    if (!isset($this->layout)) {
-      $this->layout = Layout::layoutPluginManager()->createInstance($this->configuration['layout'], []);
+  public function getDisplay() {
+    if (!isset($this->display)) {
+      $this->display = $this->displayStorage->load($this->configuration['display']);
     }
-    return $this->layout;
+    return $this->display;
   }
 
   /**
-   * {@inheritdoc}
+   * Sets the display entity.
+   *
+   * @param \Drupal\panels\Entity\PanelsDisplay
+   *   The display entity to use.
    */
-  public function getRegionNames() {
-    return $this->getLayout()->getPluginDefinition()['region_names'];
+  public function setDisplay(PanelsDisplay $display) {
+    $this->display = $display;
   }
 
   /**
-   * Build render arrays for each of the regions.
+   * Returns instance of the layout plugin used by this page variant.
    *
-   * @return
-   *   An associative array keyed by region id, containing the render array
-   *   representing the content of each region.
+   * @return \Drupal\layout_plugin\Plugin\Layout\LayoutInterface
+   *   Layout plugin instance.
    */
-  protected function buildRegions() {
-    $build = [];
-    $contexts = $this->getContexts();
-    foreach ($this->getRegionAssignments() as $region => $blocks) {
-      if (!$blocks) {
-        continue;
-      }
-
-      $region_name = Html::getClass("block-region-$region");
-      $build[$region]['#prefix'] = '<div class="' . $region_name . '">';
-      $build[$region]['#suffix'] = '</div>';
+  public function getLayout() {
+    return $this->getDisplay()->layoutPlugin();
+  }
 
-      /** @var $blocks \Drupal\block\BlockPluginInterface[] */
-      $weight = 0;
-      foreach ($blocks as $block_id => $block) {
-        if ($block instanceof ContextAwarePluginInterface) {
-          $this->contextHandler()->applyContextMapping($block, $contexts);
-        }
-        if ($block->access($this->account)) {
-          $block_render_array = [
-            '#theme' => 'block',
-            '#attributes' => [],
-            '#weight' => $weight++,
-            '#configuration' => $block->getConfiguration(),
-            '#plugin_id' => $block->getPluginId(),
-            '#base_plugin_id' => $block->getBaseId(),
-            '#derivative_plugin_id' => $block->getDerivativeId(),
-          ];
-          if (!empty($block_render_array['#configuration']['label'])) {
-            $block_render_array['#configuration']['label'] = SafeMarkup::checkPlain($block_render_array['#configuration']['label']);
-          }
-          $block_render_array['content'] = $block->build();
-
-          $build[$region][$block_id] = $block_render_array;
-        }
-      }
-    }
-    $build['#title'] = $this->renderPageTitle($this->configuration['page_title']);
-    return $build;
+  /**
+   * {@inheritdoc}
+   */
+  public function getRegionNames() {
+    return $this->getDisplay()->getRegionNames();
   }
 
   /**
    * {@inheritdoc}
    */
   public function build() {
-    $regions = $this->buildRegions();
-    if ($layout = $this->getLayout()) {
-      return $layout->build($regions);
-    }
-    return $regions;
+    return $this->getDisplay()->build();
   }
 
   /**
    * {@inheritdoc}
    */
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    // If there is no displayEntity yet then set one up.
+    if (empty($this->configuration['display']) && empty($this->display)) {
+      $this->display = $this->displayStorage->create();
+    }
+
     $form = parent::buildConfigurationForm($form, $form_state);
 
     // Allow to configure the page title, even when adding a new display.
@@ -221,210 +206,59 @@ class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInt
       '#type' => 'textfield',
       '#title' => $this->t('Page title'),
       '#description' => $this->t('Configure the page title that will be used for this display.'),
-      '#default_value' => !$this->id() ? $this->executable->getPage()->label() : $this->configuration['page_title'],
+      '#default_value' => !$this->id() ? $this->executable->getPage()->label() : $this->getDisplay()->get('title'),
     );
 
-    // Do not allow blocks to be added until the display variant has been saved.
-    if (!$this->id()) {
-      $form['layout'] = [
-        '#title' => $this->t('Layout'),
-        '#type' => 'select',
-        '#options' => Layout::getLayoutOptions(['group_by_category' => TRUE]),
-        '#default_value' => NULL
-      ];
-
-      return $form;
-    }
+    /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
+    $form_object = \Drupal::entityManager()->getFormObject('panels_display', $this->getDisplay()->id() ? 'edit' : 'add');
+    $form_object->setEntity($this->getDisplay());
 
-    // Determine the page ID, used for links below.
-    $page_id = $this->executable->getPage()->id();
+    // Set the display_variant_form key to true so that the $form_object knows
+    // not to add extra actions.
+    // @see \Drupal\panels\PanelsDisplayForm::actions()
+    $form_state->set('display_variant_form', TRUE);
+    $form += $form_object->buildForm($form, $form_state);
 
-    // Set up the attributes used by a modal to prevent duplication later.
-    $attributes = [
-      'class' => ['use-ajax'],
-      'data-dialog-type' => 'modal',
-      'data-dialog-options' => Json::encode([
-        'width' => 'auto',
-      ]),
-    ];
-    $add_button_attributes = NestedArray::mergeDeep($attributes, [
-      'class' => [
-        'button',
-        'button--small',
-        'button-action',
-      ],
-    ]);
-
-    if ($block_assignments = $this->getRegionAssignments()) {
-      // Build a table of all blocks used by this display variant.
-      $form['block_section'] = [
-        '#type' => 'details',
-        '#title' => $this->t('Blocks'),
-        '#open' => TRUE,
-      ];
-      $form['block_section']['add'] = [
-        '#type' => 'link',
-        '#title' => $this->t('Add new block'),
-        '#url' => Url::fromRoute('page_manager.display_variant_select_block', [
-          'page' => $page_id,
-          'display_variant_id' => $this->id(),
-        ]),
-        '#attributes' => $add_button_attributes,
-        '#attached' => [
-          'library' => [
-            'core/drupal.ajax',
-          ],
-        ],
-      ];
-      $form['block_section']['blocks'] = [
-        '#type' => 'table',
-        '#header' => [
-          $this->t('Label'),
-          $this->t('Plugin ID'),
-          $this->t('Region'),
-          $this->t('Weight'),
-          $this->t('Operations'),
-        ],
-        '#empty' => $this->t('There are no regions for blocks.'),
-        // @todo This should utilize https://drupal.org/node/2065485.
-        '#parents' => ['display_variant', 'blocks'],
-      ];
-      // Loop through the blocks per region.
-      foreach ($block_assignments as $region => $blocks) {
-        // Add a section for each region and allow blocks to be dragged between
-        // them.
-        $form['block_section']['blocks']['#tabledrag'][] = [
-          'action' => 'match',
-          'relationship' => 'sibling',
-          'group' => 'block-region-select',
-          'subgroup' => 'block-region-' . $region,
-          'hidden' => FALSE,
-        ];
-        $form['block_section']['blocks']['#tabledrag'][] = [
-          'action' => 'order',
-          'relationship' => 'sibling',
-          'group' => 'block-weight',
-          'subgroup' => 'block-weight-' . $region,
-        ];
-        $form['block_section']['blocks'][$region] = [
-          '#attributes' => [
-            'class' => ['region-title', 'region-title-' . $region],
-            'no_striping' => TRUE,
-          ],
-        ];
-        $form['block_section']['blocks'][$region]['title'] = [
-          '#markup' => $this->getRegionName($region),
-          '#wrapper_attributes' => [
-            'colspan' => 5,
-          ],
-        ];
-        $form['block_section']['blocks'][$region . '-message'] = [
-          '#attributes' => [
-            'class' => [
-              'region-message',
-              'region-' . $region . '-message',
-              empty($blocks) ? 'region-empty' : 'region-populated',
-            ],
-          ],
-        ];
-        $form['block_section']['blocks'][$region . '-message']['message'] = [
-          '#markup' => '<em>' . t('No blocks in this region') . '</em>',
-          '#wrapper_attributes' => [
-            'colspan' => 5,
-          ],
-        ];
-
-        /** @var $blocks \Drupal\block\BlockPluginInterface[] */
-        foreach ($blocks as $block_id => $block) {
-          $row = [
-            '#attributes' => [
-              'class' => ['draggable'],
-            ],
-          ];
-          $row['label']['#markup'] = $block->label();
-          $row['id']['#markup'] = $block->getPluginId();
-          // Allow the region to be changed for each block.
-          $row['region'] = [
-            '#title' => $this->t('Region'),
-            '#title_display' => 'invisible',
-            '#type' => 'select',
-            '#options' => $this->getRegionNames(),
-            '#default_value' => $this->getRegionAssignment($block_id),
-            '#attributes' => [
-              'class' => ['block-region-select', 'block-region-' . $region],
-            ],
-          ];
-          // Allow the weight to be changed for each block.
-          $configuration = $block->getConfiguration();
-          $row['weight'] = [
-            '#type' => 'weight',
-            '#default_value' => isset($configuration['weight']) ? $configuration['weight'] : 0,
-            '#title' => t('Weight for @block block', ['@block' => $block->label()]),
-            '#title_display' => 'invisible',
-            '#attributes' => [
-              'class' => ['block-weight', 'block-weight-' . $region],
-            ],
-          ];
-          // Add the operation links.
-          $operations = [];
-          $operations['edit'] = [
-            'title' => $this->t('Edit'),
-            'url' => Url::fromRoute('page_manager.display_variant_edit_block', [
-              'page' => $page_id,
-              'display_variant_id' => $this->id(),
-              'block_id' => $block_id,
-            ]),
-            'attributes' => $attributes,
-          ];
-          $operations['delete'] = [
-            'title' => $this->t('Delete'),
-            'url' => Url::fromRoute('page_manager.display_variant_delete_block', [
-              'page' => $page_id,
-              'display_variant_id' => $this->id(),
-              'block_id' => $block_id,
-            ]),
-            'attributes' => $attributes,
-          ];
-
-          $row['operations'] = [
-            '#type' => 'operations',
-            '#links' => $operations,
-          ];
-          $form['block_section']['blocks'][$block_id] = $row;
-        }
-      }
-    }
     return $form;
   }
 
   /**
    * {@inheritdoc}
    */
+  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
+    /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
+    $form_object = \Drupal::entityManager()->getFormObject('panels_display', $this->getDisplay()->id() ? 'edit' : 'add');
+    $form_object->setEntity($this->getDisplay());
+    $form_object->validateForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
     parent::submitConfigurationForm($form, $form_state);
 
-    if ($form_state->hasValue('layout')) {
-      $this->configuration['layout'] = $form_state->getValue('layout');
-    }
-
+    // Store the page title variant setting.
     if ($form_state->hasValue('page_title')) {
       $this->configuration['page_title'] = $form_state->getValue('page_title');
     }
 
-    // If the blocks were rearranged, update their values.
-    if ($form_state->hasValue('blocks')) {
-      foreach ($form_state->getValue('blocks') as $block_id => $block_values) {
-        $this->updateBlock($block_id, $block_values);
-      }
-    }
+    // Submit the Panels Display form and set the display.
+    /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
+    $form_object = \Drupal::entityManager()->getFormObject('panels_display', $this->getDisplay()->id() ? 'edit' : 'add');
+    $form_object->setEntity($this->getDisplay());
+    $form_object->submitForm($form, $form_state);
+    $form_object->save($form, $form_state);
+
+    $this->setDisplay($form_object->getEntity());
   }
 
   /**
    * {@inheritdoc}
    */
   public function access(AccountInterface $account = NULL) {
-    // If no blocks are configured for this variant, deny access.
-    if (empty($this->configuration['blocks'])) {
+    // If no display is configured for this variant, deny access.
+    if (empty($this->configuration['display'])) {
       return FALSE;
     }
 
@@ -441,10 +275,10 @@ class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInt
    */
   public function defaultConfiguration() {
     return parent::defaultConfiguration() + [
-      'blocks' => [],
       'selection_conditions' => [],
       'selection_logic' => 'and',
       'page_title' => '',
+      'display' => '',
     ];
   }
 
@@ -452,9 +286,7 @@ class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInt
    * {@inheritdoc}
    */
   public function calculateDependencies() {
-    foreach ($this->getBlockCollection() as $instance) {
-      $this->calculatePluginDependencies($instance);
-    }
+    // @todo: Get dependencies from the display.
     foreach ($this->getSelectionConditions() as $instance) {
       $this->calculatePluginDependencies($instance);
     }
@@ -467,7 +299,7 @@ class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInt
   public function getConfiguration() {
     return [
       'selection_conditions' => $this->getSelectionConditions()->getConfiguration(),
-      'blocks' => $this->getBlockCollection()->getConfiguration(),
+      'display' => $this->getDisplay()->id(),
     ] + parent::getConfiguration();
   }
 
@@ -548,15 +380,17 @@ class PanelsDisplayVariant extends VariantBase implements ContextAwareVariantInt
   /**
    * {@inheritdoc}
    */
-  protected function getBlockConfig() {
-    return $this->configuration['blocks'];
+  protected function uuidGenerator() {
+    return $this->uuidGenerator;
   }
 
   /**
    * {@inheritdoc}
    */
-  protected function uuidGenerator() {
-    return $this->uuidGenerator;
+  public function setContexts(array $contexts) {
+    $this->getDisplay()->setContexts($contexts);
+    $this->contexts = $contexts;
+    return $this;
   }
 
 }
