diff --git a/config/schema/page_manager.schema.yml b/config/schema/page_manager.schema.yml
index 8c6f225..9c9f22f 100644
--- a/config/schema/page_manager.schema.yml
+++ b/config/schema/page_manager.schema.yml
@@ -8,6 +8,9 @@ page_manager.page.*:
     label:
       type: label
       label: 'Label'
+    description:
+      type: text
+      label: 'Description'
     use_admin_theme:
       type: boolean
       label: 'Whether the page is displayed using the admin theme or not'
@@ -61,6 +64,7 @@ page_manager.page_variant.*:
       label: 'Display variant'
     variant_settings:
       type: display_variant.plugin.[%parent.variant]
+      label: 'Variant settings'
     page:
       type: string
       label: 'Parent page'
@@ -108,6 +112,14 @@ page_manager.block_plugin.*:
       sequence:
         - type: string
 
+display_variant.plugin.block_page:
+  type: display_variant.plugin
+  label: 'Page with blocks'
+
+display_variant.plugin.simple_page:
+  type: display_variant.plugin
+  label: 'Simple page'
+
 display_variant.plugin.block_display:
   type: display_variant.plugin
   label: 'Block variant plugin'
diff --git a/page_manager_ui/css/page-manager-ui.admin.css b/page_manager_ui/css/page-manager-ui.admin.css
new file mode 100644
index 0000000..e82c377
--- /dev/null
+++ b/page_manager_ui/css/page-manager-ui.admin.css
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * Styles for Page Manager admin.
+ */
+
+/* Narrow screens */
+
+.page-manager-wizard-tree,
+.page-manager-wizard-form {
+  box-sizing: border-box;
+}
+
+/**
+ * Wizard actions across the top.
+ */
+.page-manager-wizard-actions {
+  text-align: right; /* LTR */
+}
+.page-manager-wizard-actions ul.inline,
+.page-manager-wizard-actions ul.inline li {
+  display: inline-block;
+  margin: 0;
+}
+.page-manager-wizard-actions ul.inline {
+  border-top: 1px solid black;
+  border-left: 1px solid black;
+}
+.page-manager-wizard-actions ul.inline li {
+  border-right: 1px solid black;
+  padding: .5em;
+}
+
+/**
+ * The tree of wizard steps.
+ */
+.page-manager-wizard-tree ul {
+  margin: 0;
+  padding: 0;
+  list-style: none;
+}
+.page-manager-wizard-tree ul > li > ul {
+  margin-left: 1em;
+}
+.page-manager-wizard-tree > ul {
+  border: 1px solid black;
+  padding-bottom: .5em;
+  margin-bottom: 20px;
+}
+.page-manager-wizard-tree li {
+  border-bottom: 1px solid black;
+  padding: .5em;
+  padding-right: 0;
+}
+.page-manager-wizard-tree li:last-child {
+  border-bottom: 0;
+  padding-bottom: 0;
+}
+
+/**
+ * The wizard form.
+ */
+.page-manager-wizard-form {
+  border: 1px solid black;
+  padding: 1em;
+  margin-bottom: 20px;
+}
+
+/* Wide screens */
+@media
+  screen and (min-width: 780px),
+  (orientation: landscape) and (min-device-height: 780px) {
+
+  /**
+   * Overall layout.
+   */
+  .page-manager-wizard-tree {
+    float: left; /* LTR */
+    width: 20%;
+  }
+  .page-manager-wizard-form {
+    float: left; /* LTR */
+    width: 80%;
+  }
+  .page-manager-wizard-form-actions {
+    margin-left: 20%; /* LTR */
+  }
+
+  /**
+   * Make the borders look nice.
+   */
+  .page-manager-wizard-tree > ul {
+    border-right: 0; /* LTR */
+  }
+  .page-manager-wizard-form {
+    min-height: 700px;
+  }
+
+  /**
+   * Right-to-left support.
+   */
+  [dir="rtl"] .page-manager-wizard-tree,
+  [dir="rtl"] .page-manager-wizard-form {
+    float: right;
+  }
+  [dir="rtl"] .page-manager-wizard-form-actions {
+    margin-left: 0;
+    margin-right: 20%;
+  }
+  [dir="rtl"] .page-manager-wizard-tree > ul {
+    border-right: 1px solid black;
+    border-left: 0;
+  }
+}
diff --git a/page_manager_ui/page_manager_ui.libraries.yml b/page_manager_ui/page_manager_ui.libraries.yml
new file mode 100644
index 0000000..0a37c69
--- /dev/null
+++ b/page_manager_ui/page_manager_ui.libraries.yml
@@ -0,0 +1,5 @@
+admin:
+  version: VERSION
+  css:
+    layout:
+      css/page-manager-ui.admin.css: {}
diff --git a/page_manager_ui/page_manager_ui.module b/page_manager_ui/page_manager_ui.module
index 2c15495..c89cf12 100644
--- a/page_manager_ui/page_manager_ui.module
+++ b/page_manager_ui/page_manager_ui.module
@@ -20,20 +20,80 @@ function page_manager_ui_entity_type_build(array &$entity_types) {
   /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
   $entity_types['page']
     ->setListBuilderClass(PageListBuilder::class)
-    ->setFormClass('add', PageAddForm::class)
-    ->setFormClass('edit', PageEditForm::class)
     ->setFormClass('delete', PageDeleteForm::class)
     ->setLinkTemplate('collection', '/admin/structure/page_manager')
     ->setLinkTemplate('add-form', '/admin/structure/page_manager/add')
-    ->setLinkTemplate('edit-form', '/admin/structure/page_manager/manage/{page}')
+    ->setLinkTemplate('edit-form', '/admin/structure/page_manager/manage/{machine_name}/{step}')
     ->setLinkTemplate('delete-form', '/admin/structure/page_manager/manage/{page}/delete')
     ->setLinkTemplate('enable', '/admin/structure/page_manager/manage/{page}/enable')
-    ->setLinkTemplate('disable', '/admin/structure/page_manager/manage/{page}/disable');
-
-  $entity_types['page_variant']
-    ->setFormClass('add', PageVariantAddForm::class)
-    ->setFormClass('edit', PageVariantEditForm::class)
-    ->setFormClass('delete', PageVariantDeleteForm::class)
-    ->setLinkTemplate('edit-form', '/admin/structure/page_manager/manage/{page}/variant/{page_variant}')
-    ->setLinkTemplate('delete-form', '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/delete');
+    ->setLinkTemplate('disable', '/admin/structure/page_manager/manage/{page}/disable')
+    ->setHandlerClass('wizard', [
+      'add' => 'Drupal\page_manager_ui\Wizard\PageAddWizard',
+      'edit' => 'Drupal\page_manager_ui\Wizard\PageEditWizard',
+    ]);
+
+   $entity_types['page_variant']
+     ->setHandlerClass('wizard', [
+       'add_variant' => 'Drupal\page_manager_ui\Wizard\PageVariantAddWizard',
+     ]);
+}
+
+/**
+ * Implements hook_theme().
+ */
+function page_manager_ui_theme() {
+  return [
+    'page_manager_wizard_form' => [
+      'render element' => 'form',
+    ],
+    'page_manager_wizard_tree' => [
+      'variables' => [
+        'wizard' => NULL,
+        'cached_values' => [],
+        'tree' => [],
+        'divider' => ' » ',
+        'step' => NULL,
+      ],
+    ],
+  ];
+}
+
+/**
+ * Preprocess function for page-manager-wizard-tree.html.twig.
+ */
+function template_preprocess_page_manager_wizard_tree(&$variables) {
+  /** @var $wizard \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface */
+  $wizard = $variables['wizard'];
+  $cached_values = $variables['cached_values'];
+  $tree = $variables['tree'];
+  $variables['step'] = $wizard->getStep($cached_values);
+
+  foreach ($wizard->getOperations($cached_values) as $step => $operation) {
+    $parameters = $wizard->getNextParameters($cached_values);
+    // Override step to be the step we want.
+    $parameters['step'] = $step;
+
+    // Fill in parents if there are breadcrumbs.
+    $parent =& $tree;
+    if (isset($operation['breadcrumbs'])) {
+      foreach ($operation['breadcrumbs'] as $breadcrumb) {
+        $breadcrumb_string = (string) $breadcrumb;
+        if (!isset($parent[$breadcrumb_string])) {
+          $parent[$breadcrumb_string] = [
+            'title' => $breadcrumb,
+            'children' => [],
+          ];
+        }
+        $parent =& $parent[$breadcrumb_string]['children'];
+      }
+    }
+
+    $parent[$step] = [
+      'title' => !empty($operation['title']) ? $operation['title'] : '',
+      'url' => new \Drupal\Core\Url($wizard->getRouteName(), $parameters),
+      'step' => $step,
+    ];
+  }
+
+  $variables['tree'] = $tree;
 }
diff --git a/page_manager_ui/page_manager_ui.routing.yml b/page_manager_ui/page_manager_ui.routing.yml
index 00a9c3a..e5f35bb 100644
--- a/page_manager_ui/page_manager_ui.routing.yml
+++ b/page_manager_ui/page_manager_ui.routing.yml
@@ -11,18 +11,34 @@ entity.page.collection:
 entity.page.add_form:
   path: '/admin/structure/page_manager/add'
   defaults:
-    _entity_form: 'page.add'
+    _entity_wizard: 'page.add'
     _title: 'Add new page'
+    tempstore_id: page_manager.page
   requirements:
     _entity_create_access: page
 
+entity.page.add_step_form:
+  path: '/admin/structure/page_manager/add/{machine_name}/{step}'
+  defaults:
+    _entity_wizard: 'page.add'
+    _title: 'Add new page'
+    tempstore_id: page_manager.page
+  requirements:
+     _entity_create_access: page
+
 entity.page.edit_form:
-  path: '/admin/structure/page_manager/manage/{page}'
+  path: '/admin/structure/page_manager/manage/{machine_name}/{step}'
   defaults:
-    _entity_form: 'page.edit'
+    _entity_wizard: 'page.edit'
     _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editPageTitle'
+    tempstore_id: page_manager.page
+    page: '{machine_name}'
+  options:
+    parameters:
+      page:
+        type: tempstore:page
   requirements:
-    _entity_access: page.update
+    _permission: 'administer pages'
 
 entity.page.delete_form:
   path: '/admin/structure/page_manager/manage/{page}/delete'
@@ -30,7 +46,7 @@ entity.page.delete_form:
     _entity_form: 'page.delete'
     _title: 'Delete page'
   requirements:
-    _entity_access: page.delete
+    _permission: 'administer pages'
 
 entity.page.enable:
   path: '/admin/structure/page_manager/manage/{page}/enable'
@@ -38,7 +54,7 @@ entity.page.enable:
     _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::performPageOperation'
     op: 'enable'
   requirements:
-    _entity_access: 'page.update'
+    _permission: 'administer pages'
 
 entity.page.disable:
   path: '/admin/structure/page_manager/manage/{page}/disable'
@@ -46,174 +62,204 @@ entity.page.disable:
     _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::performPageOperation'
     op: 'disable'
   requirements:
-    _entity_access: 'page.update'
-
-#### Access Conditions
+    _permission: 'administer pages'
 
-page_manager.access_condition_select:
-  path: '/admin/structure/page_manager/manage/{page}/access/select'
+entity.page.reorder_variants_form:
+  path: '/admin/structure/page_manager/manage/{machine_name}/reorder_variants'
   defaults:
-    _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::selectAccessCondition'
-    _title: 'Select access condition'
+    _title: 'Reorder variants'
+    _form: '\Drupal\page_manager_ui\Form\PageReorderVariantsForm'
   requirements:
-    _entity_access: page.update
+    _permission: 'administer pages'
+
+#### Access Conditions
 
-page_manager.access_condition_add:
-  path: '/admin/structure/page_manager/manage/{page}/access/add/{condition_id}'
+entity.page.condition.add:
+  path: '/admin/structure/page_manager/manage/{machine_name}/access/add/{condition}'
   defaults:
-    _form: '\Drupal\page_manager_ui\Form\AccessConditionAddForm'
-    _title: 'Add new access condition'
+    _form: '\Drupal\page_manager_ui\Form\AccessConfigure'
+    _title: 'Add access condition'
+    tempstore_id: page_manager.page
   requirements:
-    _entity_access: page.update
+    _permission: 'administer pages'
 
-page_manager.access_condition_edit:
-  path: '/admin/structure/page_manager/manage/{page}/access/edit/{condition_id}'
+entity.page.condition.edit:
+  path: '/admin/structure/page_manager/manage/{machine_name}/access/edit/{condition}'
   defaults:
-    _form: '\Drupal\page_manager_ui\Form\AccessConditionEditForm'
-    _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editAccessConditionTitle'
+    _form: '\Drupal\page_manager_ui\Form\AccessConfigure'
+    _title: 'Edit access condition'
+    tempstore_id: page_manager.page
   requirements:
-    _entity_access: page.update
+    _permission: 'administer pages'
 
-page_manager.access_condition_delete:
-  path: '/admin/structure/page_manager/manage/{page}/access/delete/{condition_id}'
+entity.page.condition.delete:
+  path: '/admin/structure/page_manager/manage/{machine_name}/access/delete/{id}'
   defaults:
-    _form: '\Drupal\page_manager_ui\Form\AccessConditionDeleteForm'
+    _form: '\Drupal\page_manager_ui\Form\AccessDelete'
     _title: 'Delete access condition'
+    tempstore_id: page_manager.page
   requirements:
-    _entity_access: page.update
+    _permission: 'administer pages'
 
 #### Parameters
 
-page_manager.parameter_edit:
-  path: '/admin/structure/page_manager/manage/{page}/parameter/edit/{name}'
+page_manager.parameter.edit:
+  path: '/admin/structure/page_manager/manage/{machine_name}/parameter/edit/{name}'
   defaults:
     _form: '\Drupal\page_manager_ui\Form\ParameterEditForm'
     _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editParameterTitle'
+    tempstore_id: page_manager.page
+    page: '{machine_name}'
+  options:
+    parameters:
+      page:
+        type: tempstore:page
   requirements:
     _entity_access: page.update
 
-#### Static Contexts
-
-page_manager.static_context_add:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/context/add'
-  defaults:
-    _form: '\Drupal\page_manager_ui\Form\StaticContextAddForm'
-    _title: 'Add new static context'
-  requirements:
-    _entity_access: page_variant.update
-
-page_manager.static_context_edit:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/context/edit/{name}'
-  defaults:
-    _form: '\Drupal\page_manager_ui\Form\StaticContextEditForm'
-    _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editStaticContextTitle'
-  requirements:
-    _entity_access: page_variant.update
-
-page_manager.static_context_delete:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/context/delete/{name}'
-  defaults:
-    _form: '\Drupal\page_manager_ui\Form\StaticContextDeleteForm'
-    _title: 'Delete static context'
-  requirements:
-    _entity_access: page_variant.update
-
 #### Variants
 
 page_manager.variant_select:
-  path: '/admin/structure/page_manager/manage/{page}/add'
+  path: '/admin/structure/page_manager/manage/{machine_name}/add'
   defaults:
     _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::selectVariant'
     _title: 'Select variant'
   requirements:
-    _entity_access: page.update
+    _permission: 'administer pages'
 
 entity.page_variant.add_form:
-  path: '/admin/structure/page_manager/manage/{page}/add/{variant_plugin_id}'
+  path: '/admin/structure/page_manager/manage/{page}/add_variant'
   defaults:
-    _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::addPageVariantEntityForm'
+    _entity_wizard: 'page_variant.add_variant'
     _title: 'Add page variant'
+    tempstore_id: page_manager.page_variant
   requirements:
-    _entity_create_access: page_variant
+    _permission: 'administer pages'
 
-entity.page_variant.edit_form:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}'
+entity.page_variant.add_step_form:
+  path: '/admin/structure/page_manager/manage/{page}/add_variant/{machine_name}/{step}'
   defaults:
-    _entity_form: 'page_variant.edit'
-    _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editPageVariantTitle'
+    _entity_wizard: 'page_variant.add_variant'
+    _title: 'Add page variant'
+    tempstore_id: page_manager.page_variant
   requirements:
-    _entity_access: page_variant.update
+    _permission: 'administer pages'
 
 entity.page_variant.delete_form:
-  path: '/admin/structure/page_variant/variant/{page_variant}/delete'
+  path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/delete'
   defaults:
-    _entity_form: 'page_variant.delete'
+    _form: '\Drupal\page_manager_ui\Form\PageVariantDeleteForm'
     _title: 'Delete page variant'
+    tempstore_id: page_manager.page
   requirements:
-    _entity_access: page_variant.delete
+    _permission: 'administer pages'
 
-page_manager.variant_select_block:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/block/select'
+page_manager.block_display_select_block:
+  path: '/admin/structure/page_manager/block_display/{block_display}/select'
   defaults:
     _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::selectBlock'
     _title: 'Select block'
+    tempstore_id: 'page_manager.block_display'
   requirements:
-    _entity_access: page_variant.update
+    _ctools_access: 'block_display'
 
-page_manager.variant_add_block:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/block/add/{block_id}'
+page_manager.block_display_add_block:
+  path: '/admin/structure/page_manager/block_display/{block_display}/add/{block_id}'
   defaults:
     _form: '\Drupal\page_manager_ui\Form\VariantPluginAddBlockForm'
-    _title: 'Add block to variant'
+    _title: 'Add block'
+    tempstore_id: 'page_manager.block_display'
   requirements:
-    _entity_access: page_variant.update
+    _ctools_access: 'block_display'
 
-page_manager.variant_edit_block:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/block/edit/{block_id}'
+page_manager.block_display_edit_block:
+  path: '/admin/structure/page_manager/block_display/{block_display}/edit/{block_id}'
   defaults:
     _form: '\Drupal\page_manager_ui\Form\VariantPluginEditBlockForm'
-    _title: 'Edit block in variant'
+    _title: 'Edit block'
+    tempstore_id: 'page_manager.block_display'
   requirements:
-    _entity_access: page_variant.update
+    _ctools_access: 'block_display'
 
-page_manager.variant_delete_block:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/block/delete/{block_id}'
+page_manager.block_display_delete_block:
+  path: '/admin/structure/page_manager/block_display/{block_display}/delete/{block_id}'
   defaults:
     _form: '\Drupal\page_manager_ui\Form\VariantPluginDeleteBlockForm'
-    _title: 'Delete block in variant'
+    _title: 'Delete block'
+    tempstore_id: 'page_manager.block_display'
   requirements:
-    _entity_access: page_variant.update
+    _ctools_access: 'block_display'
 
-#### Selection Conditions
+#### Static Contexts
 
-page_manager.selection_condition_select:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/selection/select'
+entity.page_variant.context.add:
+  path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/contexts/add/{data_type}'
   defaults:
-    _controller: '\Drupal\page_manager_ui\Controller\PageManagerController::selectSelectionCondition'
-    _title: 'Select selection condition'
+    _form: '\Drupal\page_manager_ui\Form\StaticContextConfigure'
+    _title: 'Add custom context'
+    op: add
+    tempstore_id: page_manager.page
+    page: '{machine_name}'
+  options:
+    parameters:
+      page:
+        type: tempstore:page
   requirements:
-    _entity_access: page_variant.update
+    _permission: 'administer pages'
 
-page_manager.selection_condition_add:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/selection/add/{condition_id}'
+entity.page_variant.context.edit:
+  path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/contexts/edit/{data_type}'
   defaults:
-    _form: '\Drupal\page_manager_ui\Form\SelectionConditionAddForm'
+    _form: '\Drupal\page_manager_ui\Form\StaticContextConfigure'
+    _title: 'Edit context'
+    tempstore_id: page_manager.page
+    page: '{machine_name}'
+  options:
+    parameters:
+      page:
+        type: tempstore:page
+  requirements:
+    _permission: 'administer pages'
+
+entity.page_variant.context.delete:
+  path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/context/delete/{data_type}'
+  defaults:
+    _form: '\Drupal\page_manager_ui\Form\StaticContextDeleteForm'
+    _title: 'Delete static context'
+    tempstore_id: page_manager.page
+    page: '{machine_name}'
+  options:
+    parameters:
+      page:
+        type: tempstore:page
+  requirements:
+    _permission: 'administer pages'
+
+#### Selection Conditions
+
+entity.page_variant.condition.add:
+  path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/selection/add/{condition}'
+  defaults:
+    _form: '\Drupal\page_manager_ui\Form\SelectionConfigure'
+    tempstore_id: page_manager.page
     _title: 'Add new selection condition'
   requirements:
-    _entity_access: page_variant.update
+    _permission: 'administer pages'
 
-page_manager.selection_condition_edit:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/selection/edit/{condition_id}'
+entity.page_variant.condition.edit:
+  path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/selection/edit/{condition}'
   defaults:
-    _form: '\Drupal\page_manager_ui\Form\SelectionConditionEditForm'
-    _title_callback: '\Drupal\page_manager_ui\Controller\PageManagerController::editSelectionConditionTitle'
+    _form: '\Drupal\page_manager_ui\Form\SelectionConfigure'
+    tempstore_id: page_manager.page
+    _title: 'Edit selection condition'
   requirements:
-    _entity_access: page_variant.update
+    _permission: 'administer pages'
 
-page_manager.selection_condition_delete:
-  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/selection/delete/{condition_id}'
+entity.page_variant.condition.delete:
+  path: '/admin/structure/page_manager/manage/{machine_name}/variant/{variant_machine_name}/selection/delete/{id}'
   defaults:
-    _form: '\Drupal\page_manager_ui\Form\SelectionConditionDeleteForm'
+    _form: '\Drupal\page_manager_ui\Form\SelectionDelete'
+    tempstore_id: page_manager.page
     _title: 'Delete selection condition'
   requirements:
-    _entity_access: page_variant.update
+    _permission: 'administer pages'
diff --git a/page_manager_ui/src/Controller/PageManagerController.php b/page_manager_ui/src/Controller/PageManagerController.php
index 708d0e6..cb1682c 100644
--- a/page_manager_ui/src/Controller/PageManagerController.php
+++ b/page_manager_ui/src/Controller/PageManagerController.php
@@ -10,11 +10,13 @@ namespace Drupal\page_manager_ui\Controller;
 use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\Block\BlockManagerInterface;
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Plugin\Context\Context;
 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
 use Drupal\Core\Url;
 use Drupal\ctools\Form\AjaxFormTrait;
 use Drupal\page_manager\PageInterface;
 use Drupal\page_manager\PageVariantInterface;
+use Drupal\user\SharedTempStoreFactory;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -54,6 +56,13 @@ class PageManagerController extends ControllerBase {
   protected $contextHandler;
 
   /**
+   * Tempstore factory.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
    * Constructs a new VariantPluginEditForm.
    *
    * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
@@ -64,12 +73,15 @@ class PageManagerController extends ControllerBase {
    *   The variant manager.
    * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
    *   The context handler.
++   * @param \Drupal\user\SharedTempStoreFactory $tempstore
++   *   The tempstore factory.
    */
-  public function __construct(BlockManagerInterface $block_manager, PluginManagerInterface $condition_manager, PluginManagerInterface $variant_manager, ContextHandlerInterface $context_handler) {
+  public function __construct(BlockManagerInterface $block_manager, PluginManagerInterface $condition_manager, PluginManagerInterface $variant_manager, ContextHandlerInterface $context_handler, SharedTempStoreFactory $tempstore) {
     $this->blockManager = $block_manager;
     $this->conditionManager = $condition_manager;
     $this->variantManager = $variant_manager;
     $this->contextHandler = $context_handler;
+    $this->tempstore = $tempstore;
   }
 
   /**
@@ -80,7 +92,8 @@ class PageManagerController extends ControllerBase {
       $container->get('plugin.manager.block'),
       $container->get('plugin.manager.condition'),
       $container->get('plugin.manager.display_variant'),
-      $container->get('context.handler')
+      $container->get('context.handler'),
+      $container->get('user.shared_tempstore')
     );
   }
 
@@ -93,7 +106,10 @@ class PageManagerController extends ControllerBase {
    * @return string
    *   The title for the page edit form.
    */
-  public function editPageTitle(PageInterface $page) {
+  public function editPageTitle($machine_name, $tempstore_id) {
+    $cached_values = $this->tempstore->get($tempstore_id)->get($machine_name);
+    /** @var \Drupal\page_manager\PageInterface $page */
+    $page = $cached_values['page'];
     return $this->t('Edit %label page', ['%label' => $page->label()]);
   }
 
@@ -147,14 +163,14 @@ class PageManagerController extends ControllerBase {
    *
    * @param \Drupal\page_manager\PageVariantInterface $page_variant
    *   The page variant entity.
-   * @param string $name
+   * @param string $data_type
    *   The static context name.
    *
    * @return string
    *   The title for the static context edit form.
    */
-  public function editStaticContextTitle(PageVariantInterface $page_variant, $name) {
-    $static_context = $page_variant->getStaticContext($name);
+  public function editStaticContextTitle(PageInterface $page, $data_type) {
+    $static_context = $page->getStaticContext($data_type);
     return $this->t('Edit @label static context', ['@label' => $static_context['label']]);
   }
 
@@ -300,7 +316,22 @@ class PageManagerController extends ControllerBase {
    * @return array
    *   The block selection page.
    */
-  public function selectBlock(Request $request, PageVariantInterface $page_variant) {
+  public function selectBlock(Request $request, $block_display) {
+    $cached_values = $this->tempstore->get('page_manager.block_display')->get($block_display);
+    /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */
+    $variant_plugin = $cached_values['plugin'];
+
+    // Rehydrate the contexts on this end.
+    $contexts = [];
+    /**
+     * @var string $context_name
+     * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context_definition
+     */
+    foreach ($cached_values['contexts'] as $context_name => $context_definition) {
+      $contexts[$context_name] = new Context($context_definition);
+    }
+    $variant_plugin->setContexts($contexts);
+
     // Add a section containing the available blocks to be added to the variant.
     $build = [
       '#type' => 'container',
@@ -310,7 +341,7 @@ class PageManagerController extends ControllerBase {
         ],
       ],
     ];
-    $available_plugins = $this->blockManager->getDefinitionsForContexts($page_variant->getContexts());
+    $available_plugins = $this->blockManager->getDefinitionsForContexts($variant_plugin->getContexts());
     // Order by category, and then by admin label.
     $available_plugins = $this->blockManager->getSortedDefinitions($available_plugins);
     foreach ($available_plugins as $plugin_id => $plugin_definition) {
@@ -329,11 +360,11 @@ class PageManagerController extends ControllerBase {
       // 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('page_manager.variant_add_block', [
-          'page' => $page_variant->get('page'),
-          'page_variant' => $page_variant->id(),
+        'url' => Url::fromRoute('page_manager.block_display_add_block', [
+          'block_display' => $block_display,
           'block_id' => $plugin_id,
           'region' => $request->query->get('region'),
+          'destination' => $request->query->get('destination'),
         ]),
         'attributes' => $this->getAjaxAttributes(),
       ];
diff --git a/page_manager_ui/src/Form/AccessConditionAddForm.php b/page_manager_ui/src/Form/AccessConditionAddForm.php
deleted file mode 100644
index c71a452..0000000
--- a/page_manager_ui/src/Form/AccessConditionAddForm.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\AccessConditionAddForm.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Condition\ConditionManager;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides a form for adding a new access condition.
- */
-class AccessConditionAddForm extends AccessConditionFormBase {
-
-  /**
-   * The condition manager.
-   *
-   * @var \Drupal\Core\Condition\ConditionManager
-   */
-  protected $conditionManager;
-
-  /**
-   * Constructs a new AccessConditionAddForm.
-   *
-   * @param \Drupal\Core\Condition\ConditionManager $condition_manager
-   *   The condition manager.
-   */
-  public function __construct(ConditionManager $condition_manager) {
-    $this->conditionManager = $condition_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('plugin.manager.condition')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_manager_access_condition_add_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function prepareCondition($condition_id) {
-    // Create a new access condition instance.
-    return $this->conditionManager->createInstance($condition_id);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitButtonText() {
-    return $this->t('Add access condition');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitMessageText() {
-    return $this->t('The %label access condition has been added.', ['%label' => $this->condition->getPluginDefinition()['label']]);
-  }
-
-}
diff --git a/page_manager_ui/src/Form/AccessConditionDeleteForm.php b/page_manager_ui/src/Form/AccessConditionDeleteForm.php
deleted file mode 100644
index ab254a6..0000000
--- a/page_manager_ui/src/Form/AccessConditionDeleteForm.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\AccessConditionDeleteForm.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageInterface;
-use Drupal\Core\Form\ConfirmFormBase;
-
-/**
- * Provides a form for deleting an access condition.
- */
-class AccessConditionDeleteForm extends ConfirmFormBase {
-
-  /**
-   * The page entity this selection condition belongs to.
-   *
-   * @var \Drupal\page_manager\PageInterface
-   */
-  protected $page;
-
-  /**
-   * The access condition used by this form.
-   *
-   * @var \Drupal\Core\Condition\ConditionInterface
-   */
-  protected $accessCondition;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_manager_access_condition_delete_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQuestion() {
-    return $this->t('Are you sure you want to delete the access condition %name?', ['%name' => $this->accessCondition->getPluginDefinition()['label']]);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCancelUrl() {
-    return $this->page->toUrl('edit-form');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfirmText() {
-    return $this->t('Delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $condition_id = NULL) {
-    $this->page = $page;
-    $this->accessCondition = $page->getAccessCondition($condition_id);
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $this->page->removeAccessCondition($this->accessCondition->getConfiguration()['uuid']);
-    $this->page->save();
-    drupal_set_message($this->t('The access condition %name has been removed.', ['%name' => $this->accessCondition->getPluginDefinition()['label']]));
-    $form_state->setRedirectUrl($this->getCancelUrl());
-  }
-
-}
diff --git a/page_manager_ui/src/Form/AccessConditionEditForm.php b/page_manager_ui/src/Form/AccessConditionEditForm.php
deleted file mode 100644
index 92cc124..0000000
--- a/page_manager_ui/src/Form/AccessConditionEditForm.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\AccessConditionEditForm.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-/**
- * Provides a form for editing an access condition.
- */
-class AccessConditionEditForm extends AccessConditionFormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_manager_access_condition_edit_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function prepareCondition($condition_id) {
-    // Load the access condition directly from the page entity.
-    return $this->page->getAccessCondition($condition_id);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitButtonText() {
-    return $this->t('Update access condition');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitMessageText() {
-    return $this->t('The %label access condition has been updated.', ['%label' => $this->condition->getPluginDefinition()['label']]);
-  }
-
-}
diff --git a/page_manager_ui/src/Form/AccessConditionFormBase.php b/page_manager_ui/src/Form/AccessConditionFormBase.php
deleted file mode 100644
index 26b5ead..0000000
--- a/page_manager_ui/src/Form/AccessConditionFormBase.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\AccessConditionFormBase.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageInterface;
-
-/**
- * Provides a base form for editing and adding an access condition.
- */
-abstract class AccessConditionFormBase extends ConditionFormBase {
-
-  /**
-   * The page entity this condition belongs to.
-   *
-   * @var \Drupal\page_manager\PageInterface
-   */
-  protected $page;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $condition_id = NULL) {
-    $this->page = $page;
-    return parent::buildForm($form, $form_state, $condition_id, $page->getContexts());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    parent::submitForm($form, $form_state);
-
-    $configuration = $this->condition->getConfiguration();
-    // If this access condition is new, add it to the page.
-    if (!isset($configuration['uuid'])) {
-      $this->page->addAccessCondition($configuration);
-    }
-
-    // Save the page entity.
-    $this->page->save();
-
-    $form_state->setRedirectUrl($this->page->toUrl('edit-form'));
-  }
-
-}
diff --git a/page_manager_ui/src/Form/AccessConfigure.php b/page_manager_ui/src/Form/AccessConfigure.php
new file mode 100644
index 0000000..0b9f3d2
--- /dev/null
+++ b/page_manager_ui/src/Form/AccessConfigure.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\AccessConfigure;
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Form\ConditionConfigure;
+use Drupal\page_manager\PageInterface;
+
+class AccessConfigure extends ConditionConfigure {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getParentRouteInfo($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+
+    $route_name = $page->isNew() ? 'entity.page.add_step_form' : 'entity.page.edit_form';
+    return [$route_name, [
+      'machine_name' => $this->machine_name,
+      'step' => 'access',
+    ]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditions($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    return $page->get('access_conditions');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setConditions($cached_values, $conditions) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    $page->set('access_conditions', $conditions);
+    $cached_values['page'] = $page;
+    return $cached_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    return $page->getContexts();
+  }
+
+}
diff --git a/page_manager_ui/src/Form/AccessDelete.php b/page_manager_ui/src/Form/AccessDelete.php
new file mode 100644
index 0000000..7961bba
--- /dev/null
+++ b/page_manager_ui/src/Form/AccessDelete.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\AccessDelete.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Form\ConditionDelete;
+use Drupal\page_manager\PageInterface;
+
+class AccessDelete extends ConditionDelete {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getParentRouteInfo($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+
+    $route_name = $page->isNew() ? 'entity.page.add_step_form' : 'entity.page.edit_form';
+    return [$route_name, [
+      'machine_name' => $this->machine_name,
+      'step' => 'access',
+    ]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditions($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    return $page->get('access_conditions');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setConditions($cached_values, $conditions) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    $page->set('access_conditions', $conditions);
+    return $cached_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    return $page->getContexts();
+  }
+
+}
diff --git a/page_manager_ui/src/Form/ConditionFormBase.php b/page_manager_ui/src/Form/ConditionFormBase.php
deleted file mode 100644
index c9f4105..0000000
--- a/page_manager_ui/src/Form/ConditionFormBase.php
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\ConditionFormBase.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Form\FormState;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Plugin\ContextAwarePluginInterface;
-use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
-
-/**
- * Provides a base form for editing and adding a condition.
- */
-abstract class ConditionFormBase extends FormBase {
-
-  use ContextAwarePluginAssignmentTrait;
-
-  /**
-   * The condition used by this form.
-   *
-   * @var \Drupal\Core\Condition\ConditionInterface
-   */
-  protected $condition;
-
-  /**
-   * Prepares the condition used by this form.
-   *
-   * @param string $condition_id
-   *   Either a condition ID, or the plugin ID used to create a new
-   *   condition.
-   *
-   * @return \Drupal\Core\Condition\ConditionInterface
-   *   The condition object.
-   */
-  abstract protected function prepareCondition($condition_id);
-
-  /**
-   * Returns the text to use for the submit button.
-   *
-   * @return string
-   *   The submit button text.
-   */
-  abstract protected function submitButtonText();
-
-  /**
-   * Returns the text to use for the submit message.
-   *
-   * @return string
-   *   The submit message text.
-   */
-  abstract protected function submitMessageText();
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, $condition_id = NULL, $contexts = []) {
-    $this->condition = $this->prepareCondition($condition_id);
-    $temporary = $form_state->getTemporary();
-    $temporary['gathered_contexts'] = $contexts;
-    $form_state->setTemporary($temporary);
-
-    // Allow the condition to add to the form.
-    $form['condition'] = $this->condition->buildConfigurationForm([], $form_state);
-    $form['condition']['#tree'] = TRUE;
-
-    $form['actions'] = ['#type' => 'actions'];
-    $form['actions']['submit'] = [
-      '#type' => 'submit',
-      '#value' => $this->submitButtonText(),
-      '#button_type' => 'primary',
-    ];
-
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    // Allow the condition to validate the form.
-    $condition_values = (new FormState())->setValues($form_state->getValue('condition'));
-    $this->condition->validateConfigurationForm($form, $condition_values);
-    // Update the original form values.
-    $form_state->setValue('condition', $condition_values->getValues());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    // Allow the condition to submit the form.
-    $condition_values = (new FormState())->setValues($form_state->getValue('condition'));
-    $this->condition->submitConfigurationForm($form, $condition_values);
-    // Update the original form values.
-    $form_state->setValue('condition', $condition_values->getValues());
-
-    if ($this->condition instanceof ContextAwarePluginInterface) {
-      $this->condition->setContextMapping($condition_values->getValue('context_mapping', []));
-    }
-
-    // Set the submission message.
-    drupal_set_message($this->submitMessageText());
-  }
-
-}
diff --git a/page_manager_ui/src/Form/PageAccessForm.php b/page_manager_ui/src/Form/PageAccessForm.php
new file mode 100644
index 0000000..9b392df
--- /dev/null
+++ b/page_manager_ui/src/Form/PageAccessForm.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\PageAccessForm.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Form\ManageConditions;
+
+class PageAccessForm extends ManageConditions {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_access_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditionClass() {
+    return 'Drupal\page_manager_ui\Form\AccessConfigure';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.page';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getOperationsRouteInfo($cached_values, $machine_name, $row) {
+    return ['entity.page.condition', ['machine_name' => $machine_name, 'condition' => $row]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditions($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    return $page->get('access_conditions');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    return $page->getContexts();
+  }
+
+  /**
+   * The route to which condition 'add' actions should submit.
+   *
+   * @return string
+   */
+  protected function getAddRoute($cached_values) {
+    return 'entity.page.condition.add';
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $triggering_element = $form_state->getTriggeringElement();
+    if ($triggering_element['#value']->getUntranslatedString() == 'Update') {
+      return;
+    }
+    parent::submitForm($form, $form_state);
+  }
+
+}
diff --git a/page_manager_ui/src/Form/PageAddForm.php b/page_manager_ui/src/Form/PageAddForm.php
deleted file mode 100644
index 3ae2346..0000000
--- a/page_manager_ui/src/Form/PageAddForm.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\PageAddForm.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-
-/**
- * Provides a form for adding a new page entity.
- */
-class PageAddForm extends PageFormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function save(array $form, FormStateInterface $form_state) {
-    parent::save($form, $form_state);
-    drupal_set_message($this->t('The %label page has been added.', ['%label' => $this->entity->label()]));
-    $form_state->setRedirect('entity.page.edit_form', [
-      'page' => $this->entity->id(),
-    ]);
-  }
-
-}
diff --git a/page_manager_ui/src/Form/PageEditForm.php b/page_manager_ui/src/Form/PageEditForm.php
deleted file mode 100644
index f67afb6..0000000
--- a/page_manager_ui/src/Form/PageEditForm.php
+++ /dev/null
@@ -1,262 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\PageEditForm.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Url;
-use Drupal\ctools\Form\AjaxFormTrait;
-
-/**
- * Provides a form for editing a page entity.
- */
-class PageEditForm extends PageFormBase {
-
-  use AjaxFormTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function form(array $form, FormStateInterface $form_state) {
-    $form = parent::form($form, $form_state);
-
-    $form['use_admin_theme'] = [
-      '#type' => 'checkbox',
-      '#title' => $this->t('Use admin theme'),
-      '#default_value' => $this->entity->usesAdminTheme(),
-    ];
-    $attributes = $this->getAjaxAttributes();
-    $add_button_attributes = $this->getAjaxButtonAttributes();
-
-    $form['parameters_section'] = $this->buildParametersForm();
-
-    $form['variant_section'] = [
-      '#type' => 'details',
-      '#title' => $this->t('Variants'),
-      '#open' => TRUE,
-    ];
-    $form['variant_section']['add_new_page'] = [
-      '#type' => 'link',
-      '#title' => $this->t('Add new variant'),
-      '#url' => Url::fromRoute('page_manager.variant_select', [
-        'page' => $this->entity->id(),
-      ]),
-      '#attributes' => $add_button_attributes,
-      '#attached' => [
-        'library' => [
-          'core/drupal.ajax',
-        ],
-      ],
-    ];
-    $form['variant_section']['variants'] = [
-      '#type' => 'table',
-      '#header' => [
-        $this->t('Label'),
-        $this->t('Plugin'),
-        $this->t('Weight'),
-        $this->t('Operations'),
-      ],
-      '#empty' => $this->t('There are no variants.'),
-      '#tabledrag' => [[
-        'action' => 'order',
-        'relationship' => 'sibling',
-        'group' => 'variant-weight',
-      ]],
-    ];
-    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
-    foreach ($this->entity->getVariants() as $page_variant) {
-      $row = [
-        '#attributes' => [
-          'class' => ['draggable'],
-        ],
-      ];
-      $row['label']['#markup'] = $page_variant->label();
-      $row['id']['#markup'] = $page_variant->getVariantPlugin()->adminLabel();
-      $row['weight'] = [
-        '#type' => 'weight',
-        '#default_value' => $page_variant->getWeight(),
-        '#title' => $this->t('Weight for @page_variant variant', ['@page_variant' => $page_variant->label()]),
-        '#title_display' => 'invisible',
-        '#attributes' => [
-          'class' => ['variant-weight'],
-        ],
-      ];
-      $operations = [];
-      $operations['edit'] = [
-        'title' => $this->t('Edit'),
-        'url' => $page_variant->toUrl('edit-form'),
-      ];
-      $operations['delete'] = [
-        'title' => $this->t('Delete'),
-        'url' => $page_variant->toUrl('delete-form'),
-      ];
-      $row['operations'] = [
-        '#type' => 'operations',
-        '#links' => $operations,
-      ];
-      $form['variant_section']['variants'][$page_variant->id()] = $row;
-    }
-
-    if ($access_conditions = $this->entity->getAccessConditions()) {
-      $form['access_section_section'] = [
-        '#type' => 'details',
-        '#title' => $this->t('Access Conditions'),
-        '#open' => TRUE,
-      ];
-      $form['access_section_section']['add'] = [
-        '#type' => 'link',
-        '#title' => $this->t('Add new access condition'),
-        '#url' => Url::fromRoute('page_manager.access_condition_select', [
-          'page' => $this->entity->id(),
-        ]),
-        '#attributes' => $add_button_attributes,
-        '#attached' => [
-          'library' => [
-            'core/drupal.ajax',
-          ],
-        ],
-      ];
-      $form['access_section_section']['access_section'] = [
-        '#type' => 'table',
-        '#header' => [
-          $this->t('Label'),
-          $this->t('Description'),
-          $this->t('Operations'),
-        ],
-        '#empty' => $this->t('There are no access conditions.'),
-      ];
-
-      $form['access_section_section']['access_logic'] = [
-        '#type' => 'radios',
-        '#options' => [
-          'and' => $this->t('All conditions must pass'),
-          'or' => $this->t('Only one condition must pass'),
-        ],
-        '#default_value' => $this->entity->getAccessLogic(),
-      ];
-
-      $form['access_section_section']['access'] = [
-        '#tree' => TRUE,
-      ];
-      foreach ($access_conditions as $access_id => $access_condition) {
-        $row = [];
-        $row['label']['#markup'] = $access_condition->getPluginDefinition()['label'];
-        $row['description']['#markup'] = $access_condition->summary();
-        $operations = [];
-        $operations['edit'] = [
-          'title' => $this->t('Edit'),
-          'url' => Url::fromRoute('page_manager.access_condition_edit', [
-            'page' => $this->entity->id(),
-            'condition_id' => $access_id,
-          ]),
-          'attributes' => $attributes,
-        ];
-        $operations['delete'] = [
-          'title' => $this->t('Delete'),
-          'url' => Url::fromRoute('page_manager.access_condition_delete', [
-            'page' => $this->entity->id(),
-            'condition_id' => $access_id,
-          ]),
-          'attributes' => $attributes,
-        ];
-        $row['operations'] = [
-          '#type' => 'operations',
-          '#links' => $operations,
-        ];
-        $form['access_section_section']['access_section'][$access_id] = $row;
-      }
-    }
-    return $form;
-  }
-
-  /**
-   * Builds the parameters form for a page entity.
-   *
-   * @return array
-   */
-  protected function buildParametersForm() {
-    $form = [
-      '#type' => 'details',
-      '#title' => $this->t('Parameters'),
-      '#open' => TRUE,
-    ];
-    $form['parameters'] = [
-      '#type' => 'table',
-      '#header' => [
-        $this->t('Machine name'),
-        $this->t('Label'),
-        $this->t('Type'),
-        $this->t('Operations'),
-      ],
-      '#empty' => $this->t('There are no parameters.'),
-    ];
-    foreach ($this->entity->getParameterNames() as $parameter_name) {
-      $parameter = $this->entity->getParameter($parameter_name);
-      $row = [];
-      $row['machine_name'] = $parameter['machine_name'];
-      if ($label = $parameter['label']) {
-        $row['label'] = $label;
-      }
-      else {
-        $row['type']['colspan'] = 2;
-      }
-      $row['type']['data'] = $parameter['type'] ?: $this->t('<em>No context assigned</em>');
-
-      $operations = [];
-      $operations['edit'] = [
-        'title' => $this->t('Edit'),
-        'url' => Url::fromRoute('page_manager.parameter_edit', [
-          'page' => $this->entity->id(),
-          'name' => $parameter['machine_name'],
-        ]),
-        'attributes' => $this->getAjaxAttributes(),
-      ];
-      $row['operations']['data'] = [
-        '#type' => 'operations',
-        '#links' => $operations,
-      ];
-
-      $form['parameters']['#rows'][$parameter['machine_name']] = $row;
-    }
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function save(array $form, FormStateInterface $form_state) {
-    if (!$form_state->isValueEmpty('variants')) {
-      foreach ($form_state->getValue('variants') as $variant_id => $data) {
-        if ($variant_entity = $this->entity->getVariant($variant_id)) {
-          $variant_entity->setWeight($data['weight']);
-          $variant_entity->save();
-        }
-      }
-    }
-    parent::save($form, $form_state);
-    drupal_set_message($this->t('The %label page has been updated.', ['%label' => $this->entity->label()]));
-    $form_state->setRedirect('entity.page.collection');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
-    $keys_to_ignore = ['variants', 'parameters'];
-    $values_to_restore = [];
-    foreach ($keys_to_ignore as $key) {
-      $values_to_restore[$key] = $form_state->getValue($key);
-      $form_state->unsetValue($key);
-    }
-    parent::copyFormValuesToEntity($entity, $form, $form_state);
-    foreach ($values_to_restore as $key => $value) {
-      $form_state->setValue($key, $value);
-    }
-  }
-
-}
diff --git a/page_manager_ui/src/Form/PageFormBase.php b/page_manager_ui/src/Form/PageFormBase.php
deleted file mode 100644
index e5c17a8..0000000
--- a/page_manager_ui/src/Form/PageFormBase.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\PageFormBase.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Entity\EntityForm;
-use Drupal\Core\Entity\Query\QueryFactory;
-use Drupal\Core\Form\FormStateInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides a base form for editing and adding a page entity.
- */
-abstract class PageFormBase extends EntityForm {
-
-  /**
-   * {@inheritdoc}
-   *
-   * @var \Drupal\page_manager\PageInterface
-   */
-  protected $entity;
-
-  /**
-   * The entity query factory.
-   *
-   * @var \Drupal\Core\Entity\Query\QueryFactory
-   */
-  protected $entityQuery;
-
-  /**
-   * Construct a new PageFormBase.
-   *
-   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
-   *   The entity query factory.
-   */
-  public function __construct(QueryFactory $entity_query) {
-    $this->entityQuery = $entity_query;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity.query')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function form(array $form, FormStateInterface $form_state) {
-    $form['label'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('Label'),
-      '#description' => $this->t('The label for this page.'),
-      '#default_value' => $this->entity->label(),
-      '#required' => TRUE,
-      '#maxlength' => '255',
-    ];
-    $form['id'] = [
-      '#type' => 'machine_name',
-      '#default_value' => $this->entity->id(),
-      '#disabled' => !$this->entity->isNew(),
-      '#maxlength' => 64,
-      '#required' => TRUE,
-      '#machine_name' => [
-        'exists' => [$this, 'exists'],
-      ],
-    ];
-    $form['path'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('Path'),
-      '#maxlength' => 255,
-      '#default_value' => $this->entity->getPath(),
-      '#required' => TRUE,
-      '#element_validate' => [[$this, 'validatePath']],
-    ];
-
-    return parent::form($form, $form_state);
-  }
-
-  /**
-   * Determines if the page entity already exists.
-   *
-   * @param string $id
-   *   The page entity ID.
-   *
-   * @return bool
-   *   TRUE if the format exists, FALSE otherwise.
-   */
-  public function exists($id) {
-    return (bool) $this->entityQuery->get('page')
-      ->condition('id', $id)
-      ->execute();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validatePath(&$element, FormStateInterface $form_state) {
-    // Ensure the path has a leading slash.
-    $value = '/' . trim($element['#value'], '/');
-    $form_state->setValueForElement($element, $value);
-
-    // Ensure each path is unique.
-    $path = $this->entityQuery->get('page')
-      ->condition('path', $value)
-      ->condition('id', $form_state->getValue('id'), '<>')
-      ->execute();
-    if ($path) {
-      $form_state->setErrorByName('path', $this->t('The page path must be unique.'));
-    }
-  }
-
-}
diff --git a/page_manager_ui/src/Form/PageGeneralForm.php b/page_manager_ui/src/Form/PageGeneralForm.php
new file mode 100644
index 0000000..741a6c7
--- /dev/null
+++ b/page_manager_ui/src/Form/PageGeneralForm.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\PageGeneralForm.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Display\VariantManager;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\page_manager\PageVariantInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class PageGeneralForm extends FormBase {
+
+  /**
+   * The variant manager.
+   *
+   * @var \Drupal\Core\Display\VariantManager
+   */
+  protected $variantManager;
+
+  /**
+   * The entity query factory.
+   *
+   * @var \Drupal\Core\Entity\Query\QueryFactory
+   */
+  protected $entityQuery;
+
+  /**
+   * Constructs a new PageGeneralForm.
+   *
+   * @param \Drupal\Core\Display\VariantManager $variant_manager
+   *   The variant manager.
+   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
+   *   The entity query factory.
+   */
+  public function __construct(VariantManager $variant_manager, QueryFactory $entity_query) {
+    $this->variantManager = $variant_manager;
+    $this->entityQuery = $entity_query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.display_variant'),
+      $container->get('entity.query')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_general_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $form['description'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Administrative description'),
+      '#default_value' => $page->getDescription(),
+    ];
+    $form['path'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Path'),
+      '#maxlength' => 255,
+      '#default_value' => $page->getPath(),
+      '#required' => TRUE,
+      '#element_validate' => [[$this, 'validatePath']],
+    ];
+    $form['use_admin_theme'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Use admin theme'),
+      '#default_value' => $page->usesAdminTheme(),
+    ];
+
+    if ($page->isNew()) {
+      $variant_plugin_options = [];
+      foreach ($this->variantManager->getDefinitions() as $plugin_id => $definition) {
+        // The following two variants are provided by Drupal Core. They are not
+        // configurable and therefore not compatible with Page Manager but have
+        // similar and confusing labels. Skip them so that they are not shown in
+        // the UI.
+        if (in_array($plugin_id, ['simple_page', 'block_page'])) {
+          continue;
+        }
+
+        $variant_plugin_options[$plugin_id] = $definition['admin_label'];
+      }
+      $form['variant_plugin_id'] = [
+        '#title' => $this->t('Variant type'),
+        '#type' => 'select',
+        '#options' => $variant_plugin_options,
+        '#default_value' => !empty($cached_values['variant_plugin_id']) ? $cached_values['variant_plugin_id'] : '',
+      ];
+      $form['wizard_options'] = [
+        '#type' => 'checkboxes',
+        '#title' => $this->t('Optional features'),
+        '#description' => $this->t('Check any optional features you need to be presented with forms for configuring them. If you do not check them here you will still be able to utilize these features once the new page is created. If you are not sure, leave these unchecked.'),
+        '#options' => [
+          'parameters' => $this->t('Page parameters'),
+          'access' => $this->t('Page acess'),
+          'contexts' => $this->t('Variant contexts'),
+          'selection' => $this->t('Variant selection criteria'),
+        ],
+        '#default_value' => !empty($cached_values['wizard_options']) ? $cached_values['wizard_options'] : [],
+      ];
+    }
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $page->set('description', $form_state->getValue('description'));
+    $page->set('path', $form_state->getValue('path'));
+    $page->set('use_admin_theme', $form_state->getValue('use_admin_theme'));
+
+    if ($page->isNew()) {
+      $page->set('id', $form_state->getValue('id'));
+      $page->set('label', $form_state->getValue('label'));
+      if (empty($cached_values['variant_plugin_id'])) {
+        $variant_plugin_id = $cached_values['variant_plugin_id'] = $form_state->getValue('variant_plugin_id');
+        /* @var \Drupal\page_manager\PageVariantInterface $page_variant */
+        $page_variant = \Drupal::entityManager()
+          ->getStorage('page_variant')
+          ->create([
+            'variant' => $form_state->getValue('variant_plugin_id'),
+            'page' => $page->id(),
+            'id' => "{$page->id()}-{$variant_plugin_id}-0",
+            'label' => $form['variant_plugin_id']['#options'][$variant_plugin_id],
+          ]);
+        $page_variant->setPageEntity($page);
+        $page->addVariant($page_variant);
+        $cached_values['page_variant'] = $page_variant;
+      }
+      if ($cached_values['variant_plugin_id'] != $form_state->getValue('variant_plugin_id') && !empty($cached_values['page_variant'])) {
+        $page_variant = $cached_values['page_variant'];
+        /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */
+        $page_variant->set('variant', $form_state->getValue('variant_plugin_id'));
+        $page_variant->set('variant_settings', []);
+        $cached_values['variant_plugin_id'] = $form_state->getValue('variant_plugin_id');
+      }
+
+      $cached_values['wizard_options'] = $form_state->getValue('wizard_options');
+      $form_state->setTemporaryValue('wizard', $cached_values);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validatePath(&$element, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+
+    // Ensure the path has a leading slash.
+    $value = '/' . trim($element['#value'], '/');
+    $form_state->setValueForElement($element, $value);
+
+    // Ensure each path is unique.
+    $path_query = $this->entityQuery->get('page')
+      ->condition('path', $value);
+    if (!$page->isNew()) {
+      $path_query->condition('id', $page->id(), '<>');
+    }
+    $path = $path_query->execute();
+    if ($path) {
+      $form_state->setErrorByName('path', $this->t('The page path must be unique.'));
+    }
+  }
+
+}
diff --git a/page_manager_ui/src/Form/PageParametersForm.php b/page_manager_ui/src/Form/PageParametersForm.php
new file mode 100644
index 0000000..0a0061a
--- /dev/null
+++ b/page_manager_ui/src/Form/PageParametersForm.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\PageParametersForm.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\OpenModalDialogCommand;
+use Drupal\Core\Display\ContextAwareVariantInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Plugin\Context\ContextDefinitionInterface;
+use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
+use Drupal\Core\TypedData\ListDataDefinitionInterface;
+use Drupal\Core\Url;
+
+class PageParametersForm extends FormBase {
+
+  /**
+   * @var string
+   */
+  protected $machine_name;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_parameters_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $this->machine_name = $cached_values['id'];
+    $form['items'] = [
+      '#type' => 'markup',
+      '#prefix' => '<div id="available-parameters">',
+      '#suffix' => '</div>',
+      '#theme' => 'table',
+      '#header' => [$this->t('Label'), $this->t('Type'), $this->t('Operations')],
+      '#rows' => $this->renderRows($cached_values),
+      '#empty' => $this->t('There are no parameters defined for this page.')
+    ];
+    return $form;
+  }
+
+  protected function renderRows($cached_values) {
+    $parameters = [];
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    /**
+     * @var string $parameter
+     * @var \Drupal\Core\Plugin\Context\ContextInterface $context
+     */
+    foreach ($page->getContexts() as $parameter => $context) {
+      // @todo this list should be replaced with some sort of context type check.
+      if (!in_array($parameter, ['current_user'])) {
+        list($route_partial, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $cached_values['id'], $parameter);
+        $build = [
+          '#type' => 'operations',
+          '#links' => $this->getOperations($route_partial, $route_parameters),
+        ];
+      }
+      else {
+        $build = [];
+      }
+
+      $contexts[$parameter] = [
+        $context->getContextDefinition()->getLabel(),
+        $context->getContextDefinition()->getDataType(),
+        'operations' => [
+          'data' => $build,
+        ],
+      ];
+    }
+    return $contexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+  }
+
+  protected function getOperations($route_name_base, array $route_parameters = array()) {
+    $operations['edit'] = array(
+      'title' => t('Edit'),
+      'url' => new Url($route_name_base . '.edit', $route_parameters),
+      'weight' => 10,
+      'attributes' => array(
+        'class' => ['use-ajax'],
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => Json::encode([
+          'width' => 700,
+        ]),
+      ),
+    );
+    return $operations;
+  }
+
+  /**
+   * Returns the tempstore id to use.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.page';
+  }
+
+  protected function getOperationsRouteInfo($cached_values, $machine_name, $row) {
+    return ['page_manager.parameter', ['machine_name' => $machine_name, 'name' => $row]];
+  }
+
+}
diff --git a/page_manager_ui/src/Form/PageReorderVariantsForm.php b/page_manager_ui/src/Form/PageReorderVariantsForm.php
new file mode 100644
index 0000000..2d209d8
--- /dev/null
+++ b/page_manager_ui/src/Form/PageReorderVariantsForm.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\page_manager_ui\Form\PageReorderVariantsForm.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\page_manager\Entity\PageVariant;
+use Drupal\page_manager\PageInterface;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a form for adding a variant.
+ */
+class PageReorderVariantsForm extends FormBase {
+
+  /**
+   * Tempstore factory.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * Constructs a new DisplayVariantAddForm.
+   *
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore
+   *   The tempstore factory.
+   */
+  public function __construct(SharedTempStoreFactory $tempstore) {
+    $this->tempstore = $tempstore;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.shared_tempstore')
+    );
+  }
+
+  /**
+   * Get the tempstore id.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.page';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_reorder_variants_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $machine_name = '') {
+    $cached_values = $this->tempstore->get($this->getTempstoreId())->get($machine_name);
+    $form_state->setTemporaryValue('wizard', $cached_values);
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+
+    $form['variants'] = [
+      '#type' => 'table',
+      '#header' => [
+        $this->t('Label'),
+        $this->t('Plugin'),
+        $this->t('Weight'),
+      ],
+      '#empty' => $this->t('There are no variants.'),
+      '#tabledrag' => [[
+        'action' => 'order',
+        'relationship' => 'sibling',
+        'group' => 'variant-weight',
+      ]],
+    ];
+
+    $variants = $page->getVariants();
+    if (!empty($cached_values['deleted_variants'])) {
+      foreach ($cached_values['deleted_variants'] as $page_variant) {
+        unset($variants[$page_variant->id()]);
+      }
+    }
+    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
+    foreach ($variants as $page_variant) {
+      $row = [
+        '#attributes' => [
+          'class' => ['draggable'],
+        ],
+      ];
+      $row['label']['#markup'] = $page_variant->label();
+      $row['id']['#markup'] = $page_variant->getVariantPlugin()->adminLabel();
+      $row['weight'] = [
+        '#type' => 'weight',
+        '#default_value' => $page_variant->getWeight(),
+        '#title' => $this->t('Weight for @page_variant variant', ['@page_variant' => $page_variant->label()]),
+        '#title_display' => 'invisible',
+        '#attributes' => [
+          'class' => ['variant-weight'],
+        ],
+      ];
+      $form['variants'][$page_variant->id()] = $row;
+    }
+
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Update'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\Entity\Page $page */
+    $page = $cached_values['page'];
+
+    foreach ($form_state->getValue('variants') as $id => $values) {
+      if ($page_variant = $page->getVariant($id)) {
+        $page_variant->setWeight($values['weight']);
+      }
+    }
+
+    $form_state->setRedirect('entity.page.edit_form', [
+      'machine_name' => $page->id(),
+      'step' => 'general',
+    ]);
+
+    $this->tempstore->get($this->getTempstoreId())->set($page->id(), $cached_values);
+  }
+
+}
diff --git a/page_manager_ui/src/Form/PageVariantAddForm.php b/page_manager_ui/src/Form/PageVariantAddForm.php
index 70f6594..62827e3 100644
--- a/page_manager_ui/src/Form/PageVariantAddForm.php
+++ b/page_manager_ui/src/Form/PageVariantAddForm.php
@@ -7,26 +7,140 @@
 
 namespace Drupal\page_manager_ui\Form;
 
+use Drupal\Core\Display\VariantManager;
+use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\page_manager\Entity\PageVariant;
+use Drupal\page_manager\PageInterface;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Provides a form for adding a variant.
  */
-class PageVariantAddForm extends PageVariantFormBase {
+class PageVariantAddForm extends FormBase {
+
+  /**
+   * The variant manager.
+   *
+   * @var \Drupal\Core\Display\VariantManager
+   */
+  protected $variantManager;
+
+  /**
+   * Tempstore factory.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * Constructs a new DisplayVariantAddForm.
+   *
+   * @param \Drupal\Core\Display\VariantManager $variant_manager
+   *   The variant manager.
+   */
+  public function __construct(VariantManager $variant_manager, SharedTempStoreFactory $tempstore) {
+    $this->variantManager = $variant_manager;
+    $this->tempstore = $tempstore;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.display_variant'),
+      $container->get('user.shared_tempstore')
+    );
+  }
 
   /**
    * {@inheritdoc}
    */
-  protected function submitText() {
-    return $this->t('Add variant');
+  public function getFormId() {
+    return 'page_manager_add_variant_form';
   }
 
   /**
    * {@inheritdoc}
    */
-  public function save(array $form, FormStateInterface $form_state) {
-    parent::save($form, $form_state);
-    $form_state->setRedirectUrl($this->getEntity()->toUrl('edit-form'));
+  public function buildForm(array $form, FormStateInterface $form_state, $machine_name = '') {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+
+    $variant_plugin_options = [];
+    foreach ($this->variantManager->getDefinitions() as $plugin_id => $definition) {
+      // The following two variants are provided by Drupal Core. They are not
+      // configurable and therefore not compatible with Page Manager but have
+      // similar and confusing labels. Skip them so that they are not shown in
+      // the UI.
+      if (in_array($plugin_id, ['simple_page', 'block_page'])) {
+        continue;
+      }
+
+      $variant_plugin_options[$plugin_id] = $definition['admin_label'];
+    }
+    $form['variant_plugin_id'] = [
+      '#title' => $this->t('Type'),
+      '#type' => 'select',
+      '#options' => $variant_plugin_options,
+      '#default_value' => !empty($cached_values['variant_plugin_id']) ? $cached_values['variant_plugin_id'] : '',
+    ];
+    $form['wizard_options'] = [
+      '#type' => 'checkboxes',
+      '#title' => $this->t('Optional features'),
+      '#description' => $this->t('Check any optional features you need to be presented with forms for configuring them. If you do not check them here you will still be able to utilize these features once the new variant is created.'),
+      '#options' => [
+        'selection' => $this->t('Selection criteria'),
+        'contexts' => $this->t('Contexts'),
+      ],
+      '#default_value' => !empty($cached_values['wizard_options']) ? $cached_values['wizard_options'] : [],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * Check if a variant id is taken.
+   *
+   * @param \Drupal\page_manager\PageInterface $page
+   *   The page entity.
+   * @param string $variant_id
+   *   The page variant id to check.
+   *
+   * @return bool
+   *   TRUE if the ID is available; FALSE otherwise.
+   */
+  protected function variantExists(PageInterface $page, $variant_id) {
+    return isset($page->getVariants()[$variant_id]) || PageVariant::load($variant_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $variant_plugin_id = $cached_values['variant_plugin_id'] = $form_state->getValue('variant_plugin_id');
+
+    /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */
+    $page_variant = $cached_values['page_variant'];
+    $page_variant->setVariantPluginId($variant_plugin_id);
+    $page_variant->set('label', $form_state->getValue('label'));
+    $page_variant->set('page', $page->id());
+
+    // Loop over variant ids until one is available.
+    $variant_id_base = "{$page->id()}-{$variant_plugin_id}";
+    $key = 0;
+    while ($this->variantExists($page, "{$variant_id_base}-{$key}")) {
+      $key++;
+    }
+
+    $cached_values['id'] = "{$variant_id_base}-{$key}";
+    $page_variant->set('id', $cached_values['id']);
+    $cached_values['wizard_options'] = $form_state->getValue('wizard_options');
+    $form_state->setTemporaryValue('wizard', $cached_values);
   }
 
 }
diff --git a/page_manager_ui/src/Form/PageVariantConfigureForm.php b/page_manager_ui/src/Form/PageVariantConfigureForm.php
new file mode 100644
index 0000000..a15cb15
--- /dev/null
+++ b/page_manager_ui/src/Form/PageVariantConfigureForm.php
@@ -0,0 +1,94 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\PageVariantConfigureForm.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+class PageVariantConfigureForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    // @todo this should vary by step/variant plugin id.
+    return 'page_manage_variant_configure_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\PageInterface $page */
+    $page = $cached_values['page'];
+    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
+    $page_variant = $cached_values['page_variant'];
+
+    $form['page_variant_label'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Label'),
+      '#required' => TRUE,
+      '#size' => 32,
+      '#maxlength' => 255,
+      '#default_value' => $page_variant->label(),
+    ];
+
+    $variant_plugin = $page_variant->getVariantPlugin();
+    $form['variant_settings'] = $variant_plugin->buildConfigurationForm([], (new FormState())->setValues($form_state->getValue('variant_settings', [])));
+    $form['variant_settings']['#tree'] = TRUE;
+
+    if (!$page->isNew()) {
+      $form['delete'] = [
+        '#type' => 'link',
+        '#title' => $this->t('Delete this variant'),
+        '#attributes' => [
+          'class' => ['button', 'use-ajax'],
+          'data-dialog-type' => 'modal',
+        ],
+        '#url' => new Url('entity.page_variant.delete_form', [
+          'machine_name' => $page->id(),
+          'variant_machine_name' => $page_variant->id(),
+        ]),
+      ];
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */
+    $page_variant = $cached_values['page_variant'];
+
+    $variant_plugin = $page_variant->getVariantPlugin();
+    $variant_plugin->validateConfigurationForm($form['variant_settings'], (new FormState())->setValues($form_state->getValue('variant_settings', [])));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */
+    $page_variant = $cached_values['page_variant'];
+    $variant_plugin = $page_variant->getVariantPlugin();
+    $variant_plugin->submitConfigurationForm($form['variant_settings'], (new FormState())->setValues($form_state->getValue('variant_settings', [])));
+    $configuration = $variant_plugin->getConfiguration();
+    $page_variant->set('variant_settings', $configuration);
+    $page_variant->set('label', $form_state->getValue('page_variant_label'));
+  }
+
+}
diff --git a/page_manager_ui/src/Form/PageVariantContextsForm.php b/page_manager_ui/src/Form/PageVariantContextsForm.php
new file mode 100644
index 0000000..dbbfc8d
--- /dev/null
+++ b/page_manager_ui/src/Form/PageVariantContextsForm.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\PageVariantContextsForm.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\OpenModalDialogCommand;
+use Drupal\Core\Display\ContextAwareVariantInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Plugin\Context\ContextDefinitionInterface;
+use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
+use Drupal\Core\TypedData\ListDataDefinitionInterface;
+use Drupal\Core\Url;
+
+class PageVariantContextsForm extends FormBase {
+
+  /**
+   * @var string
+   */
+  protected $machine_name;
+
+  /**
+   * @var string
+   */
+  protected $variant_machine_name;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_variant_context_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $this->machine_name = $cached_values['id'];
+    $this->variant_machine_name = $cached_values['page_variant']->id();
+    $form['items'] = [
+      '#type' => 'markup',
+      '#prefix' => '<div id="available-contexts">',
+      '#suffix' => '</div>',
+      '#theme' => 'table',
+      '#header' => [$this->t('Context'), $this->t('Type'), $this->t('Operations')],
+      '#rows' => $this->renderRows($cached_values),
+      '#empty' => $this->t('No Contexts configured for this variant.')
+    ];
+    $types = [];
+    foreach (\Drupal::typedDataManager()->getDefinitions() as $type => $definition) {
+      $types[$type] = $definition['label'];
+    }
+    if (isset($types['entity'])) {
+      unset($types['entity']);
+    }
+    asort($types);
+    $form['types'] = [
+      '#type' => 'select',
+      '#options' => $types,
+    ];
+    $form['add'] = [
+      '#type' => 'submit',
+      '#name' => 'add',
+      '#value' => $this->t('Add new context'),
+      '#ajax' => [
+        'callback' => [$this, 'add'],
+        'event' => 'click',
+      ],
+      '#submit' => [
+        'callback' => [$this, 'submitForm'],
+      ]
+    ];
+    return $form;
+  }
+
+  public function add(array &$form, FormStateInterface $form_state) {
+    $type = $form_state->getValue('types');
+    $content = \Drupal::formBuilder()->getForm('\Drupal\page_manager_ui\Form\StaticContextConfigure', 'add', $type, $this->getTempstoreId(), $this->machine_name, $this->variant_machine_name);
+    $content['#attached']['library'][] = 'core/drupal.dialog.ajax';
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    list(, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $this->variant_machine_name, $type);
+    $content['submit']['#attached']['drupalSettings']['ajax'][$content['submit']['#id']]['url'] = $this->url($this->getAddRoute($cached_values), $route_parameters, ['query' => [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]]);
+    $response = new AjaxResponse();
+    $response->addCommand(new OpenModalDialogCommand($this->t('Add new context'), $content, array('width' => '700')));
+    return $response;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+
+    /** @var \Drupal\Core\StringTranslation\TranslatableMarkup $add */
+    $add = $form_state->getValue('add');
+    if ($add->getUntranslatedString() == 'Add new context') {
+      list(, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $this->variant_machine_name, $form_state->getValue('types'));
+      $form_state->setRedirect($this->getAddRoute($cached_values), $route_parameters);
+    }
+  }
+
+  protected function renderRows($cached_values) {
+    $contexts = [];
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page_variant = $cached_values['page_variant'];
+    $static_contexts = $page_variant->getStaticContexts();
+
+    /**
+     * @var string $parameter
+     * @var \Drupal\Core\Plugin\Context\ContextInterface $context
+     */
+    foreach ($page_variant->getContexts() as $parameter => $context) {
+      // @todo this list should be replaced with some sort of context type check.
+      if (!in_array($parameter, ['current_user']) && (array_key_exists($parameter, $static_contexts))) {
+        list($route_partial, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $cached_values['id'], $this->variant_machine_name, $parameter);
+        $build = [
+          '#type' => 'operations',
+          '#links' => $this->getOperations($route_partial, $route_parameters),
+        ];
+      }
+      else {
+        $build = [];
+      }
+
+      $contexts[$parameter] = [
+        $context->getContextDefinition()->getLabel(),
+        $context->getContextDefinition()->getDataType(),
+        'operations' => [
+          'data' => $build,
+        ],
+      ];
+    }
+    return $contexts;
+  }
+
+  protected function getOperations($route_name_base, array $route_parameters = array()) {
+    $operations['edit'] = array(
+      'title' => t('Edit'),
+      'url' => new Url($route_name_base . '.edit', $route_parameters),
+      'weight' => 10,
+      'attributes' => array(
+        'class' => ['use-ajax'],
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => Json::encode([
+          'width' => 700,
+        ]),
+      ),
+    );
+    $operations['delete'] = array(
+      'title' => t('Delete'),
+      'url' => new Url($route_name_base . '.delete', $route_parameters),
+      'weight' => 100,
+      'attributes' => array(
+        'class' => array('use-ajax'),
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => Json::encode([
+          'width' => 700,
+        ]),
+      ),
+    );
+    return $operations;
+  }
+
+  /**
+   * Returns the tempstore id to use.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.page';
+  }
+
+  protected function getOperationsRouteInfo($cached_values, $machine_name, $variant_machine_name, $row) {
+    return ['entity.page_variant.context', ['machine_name' => $machine_name, 'variant_machine_name' => $variant_machine_name, 'data_type' => $row]];
+  }
+
+  /**
+   * The route to which condition 'add' actions should submit.
+   *
+   * @return string
+   */
+  protected function getAddRoute($cached_values) {
+    return 'entity.page_variant.context.add';
+  }
+
+}
diff --git a/page_manager_ui/src/Form/PageVariantDeleteForm.php b/page_manager_ui/src/Form/PageVariantDeleteForm.php
index 55b7ae8..07224c3 100644
--- a/page_manager_ui/src/Form/PageVariantDeleteForm.php
+++ b/page_manager_ui/src/Form/PageVariantDeleteForm.php
@@ -7,35 +7,74 @@
 
 namespace Drupal\page_manager_ui\Form;
 
-use Drupal\Core\Entity\EntityConfirmFormBase;
+use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Builds the form to delete a PageVariant.
  */
-class PageVariantDeleteForm extends EntityConfirmFormBase {
+class PageVariantDeleteForm extends ConfirmFormBase {
 
   /**
-   * The entity being used by this form.
+   * Tempstore factory.
    *
-   * @var \Drupal\page_manager\PageVariantInterface
+   * @var \Drupal\user\SharedTempStoreFactory
    */
-  protected $entity;
+  protected $tempstore;
+
+  /**
+   * Constructs a PageVariantDeleteForm.
+   *
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore
+   *   The tempstore factory.
+   */
+  public function __construct(SharedTempStoreFactory $tempstore) {
+    $this->tempstore = $tempstore;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.shared_tempstore')
+    );
+  }
+
+  /**
+   * Get the tempstore id.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.page';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_variant_delete_form';
+  }
 
   /**
    * {@inheritdoc}
    */
   public function getQuestion() {
-    return $this->t('Are you sure you want to delete %name?', ['%name' => $this->entity->label()]);
+    return $this->t('Are you sure you want to delete this variant?');
   }
 
   /**
    * {@inheritdoc}
    */
   public function getCancelUrl() {
+    $machine_name = $this->getRouteMatch()->getParameter('machine_name');
     return new Url('entity.page.edit_form', [
-      'page' => $this->entity->get('page'),
+      'machine_name' => $machine_name,
+      'step' => 'general',
     ]);
   }
 
@@ -50,13 +89,23 @@ class PageVariantDeleteForm extends EntityConfirmFormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $this->entity->delete();
+    $machine_name = $this->getRouteMatch()->getParameter('machine_name');
+    $variant_machine_name = $this->getRouteMatch()->getParameter('variant_machine_name');
+    $cached_values = $this->tempstore->get($this->getTempstoreId())->get($machine_name);
+    /** @var \Drupal\page_manager\PageInterface $page */
+    $page = $cached_values['page'];
+    $page_variant = $page->getVariant($variant_machine_name);
+
+    // Add to a list to remove for real later.
+    $cached_values['deleted_variants'][] = $page_variant;
 
     drupal_set_message($this->t('The variant %label has been removed.', [
-      '%label' => $this->entity->label(),
+      '%label' => $page_variant->label(),
     ]));
 
     $form_state->setRedirectUrl($this->getCancelUrl());
+
+    $this->tempstore->get($this->getTempstoreId())->set($page->id(), $cached_values);
   }
 
 }
diff --git a/page_manager_ui/src/Form/PageVariantEditForm.php b/page_manager_ui/src/Form/PageVariantEditForm.php
deleted file mode 100644
index 138a4cf..0000000
--- a/page_manager_ui/src/Form/PageVariantEditForm.php
+++ /dev/null
@@ -1,404 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\page_manager_ui\Form\PageVariantEditForm.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Url;
-use Drupal\ctools\Form\AjaxFormTrait;
-use Drupal\ctools\Plugin\BlockVariantInterface;
-
-/**
- * Provides a form for editing a variant.
- */
-class PageVariantEditForm extends PageVariantFormBase {
-
-  use AjaxFormTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitText() {
-    return $this->t('Update variant');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function form(array $form, FormStateInterface $form_state) {
-    $form = parent::form($form, $form_state);
-
-    if ($this->getVariantPlugin() instanceof BlockVariantInterface) {
-      $form['variant_settings']['block_section'] = $this->buildBlockForm();
-    }
-
-    $form['selection_section'] = $this->buildSelectionForm();
-
-    $form['context'] = $this->buildContextForm();
-
-    return $form;
-  }
-
-  /**
-   * Builds the block form for a variant.
-   *
-   * @return array
-   */
-  protected function buildBlockForm() {
-    $variant_plugin = $this->getVariantPlugin();
-    if (!$variant_plugin instanceof BlockVariantInterface) {
-      return [];
-    }
-
-    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
-    $page_variant = $this->getEntity();
-
-    // Set up the attributes used by a modal to prevent duplication later.
-    $attributes = $this->getAjaxAttributes();
-    $add_button_attributes = $this->getAjaxButtonAttributes();
-
-    $form = [];
-    if ($block_assignments = $variant_plugin->getRegionAssignments()) {
-      // Build a table of all blocks used by this variant.
-      $form = [
-        '#type' => 'details',
-        '#title' => $this->t('Blocks'),
-        '#open' => TRUE,
-      ];
-      $form['add'] = [
-        '#type' => 'link',
-        '#title' => $this->t('Add new block'),
-        '#url' => Url::fromRoute('page_manager.variant_select_block', [
-          'page' => $page_variant->get('page'),
-          'page_variant' => $page_variant->id(),
-        ]),
-        '#attributes' => $add_button_attributes,
-        '#attached' => [
-          'library' => [
-            'core/drupal.ajax',
-          ],
-        ],
-      ];
-      $form['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' => ['variant_plugin', '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['blocks']['#tabledrag'][] = [
-          'action' => 'match',
-          'relationship' => 'sibling',
-          'group' => 'block-region-select',
-          'subgroup' => 'block-region-' . $region,
-          'hidden' => FALSE,
-        ];
-        $form['blocks']['#tabledrag'][] = [
-          'action' => 'order',
-          'relationship' => 'sibling',
-          'group' => 'block-weight',
-          'subgroup' => 'block-weight-' . $region,
-        ];
-        $form['blocks'][$region] = [
-          '#attributes' => [
-            'class' => ['region-title', 'region-title-' . $region],
-            'no_striping' => TRUE,
-          ],
-        ];
-        $form['blocks'][$region]['title'] = [
-          '#markup' => $variant_plugin->getRegionName($region),
-          '#wrapper_attributes' => [
-            'colspan' => 5,
-          ],
-        ];
-        $form['blocks'][$region . '-message'] = [
-          '#attributes' => [
-            'class' => [
-              'region-message',
-              'region-' . $region . '-message',
-              empty($blocks) ? 'region-empty' : 'region-populated',
-            ],
-          ],
-        ];
-        $form['blocks'][$region . '-message']['message'] = [
-          '#markup' => '<em>' . $this->t('No blocks in this region') . '</em>',
-          '#wrapper_attributes' => [
-            'colspan' => 5,
-          ],
-        ];
-
-        /** @var \Drupal\Core\Block\BlockPluginInterface[] $blocks */
-        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' => $variant_plugin->getRegionNames(),
-            '#default_value' => $variant_plugin->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' => $this->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.variant_edit_block', [
-              'page' => $page_variant->get('page'),
-              'page_variant' => $page_variant->id(),
-              'block_id' => $block_id,
-            ]),
-            'attributes' => $attributes,
-          ];
-          $operations['delete'] = [
-            'title' => $this->t('Delete'),
-            'url' => Url::fromRoute('page_manager.variant_delete_block', [
-              'page' => $page_variant->get('page'),
-              'page_variant' => $page_variant->id(),
-              'block_id' => $block_id,
-            ]),
-            'attributes' => $attributes,
-          ];
-
-          $row['operations'] = [
-            '#type' => 'operations',
-            '#links' => $operations,
-          ];
-          $form['blocks'][$block_id] = $row;
-        }
-      }
-    }
-    return $form;
-  }
-
-  /**
-   * Builds the selection form for a variant.
-   *
-   * @return array
-   */
-  protected function buildSelectionForm() {
-    // Set up the attributes used by a modal to prevent duplication later.
-    $attributes = $this->getAjaxAttributes();
-    $add_button_attributes = $this->getAjaxButtonAttributes();
-
-    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
-    $page_variant = $this->getEntity();
-
-    // Selection conditions.
-    $form = [
-      '#type' => 'details',
-      '#title' => $this->t('Selection Conditions'),
-      '#open' => TRUE,
-    ];
-    $form['add'] = [
-      '#type' => 'link',
-      '#title' => $this->t('Add new selection condition'),
-      '#url' => Url::fromRoute('page_manager.selection_condition_select', [
-        'page' => $page_variant->get('page'),
-        'page_variant' => $page_variant->id(),
-      ]),
-      '#attributes' => $add_button_attributes,
-      '#attached' => [
-        'library' => [
-          'core/drupal.ajax',
-        ],
-      ],
-    ];
-    $form['table'] = [
-      '#type' => 'table',
-      '#header' => [
-        $this->t('Label'),
-        $this->t('Description'),
-        $this->t('Operations'),
-      ],
-      '#empty' => $this->t('There are no selection conditions.'),
-    ];
-
-    $form['selection_logic'] = [
-      '#type' => 'radios',
-      '#options' => [
-        'and' => $this->t('All conditions must pass'),
-        'or' => $this->t('Only one condition must pass'),
-      ],
-      '#default_value' => $page_variant->getSelectionLogic(),
-    ];
-
-    $form['selection'] = [
-      '#tree' => TRUE,
-    ];
-    foreach ($page_variant->getSelectionConditions() as $selection_id => $selection_condition) {
-      $row = [];
-      $row['label']['#markup'] = $selection_condition->getPluginDefinition()['label'];
-      $row['description']['#markup'] = $selection_condition->summary();
-      $operations = [];
-      $operations['edit'] = [
-        'title' => $this->t('Edit'),
-        'url' => Url::fromRoute('page_manager.selection_condition_edit', [
-          'page' => $page_variant->get('page'),
-          'page_variant' => $page_variant->id(),
-          'condition_id' => $selection_id,
-        ]),
-        'attributes' => $attributes,
-      ];
-      $operations['delete'] = [
-        'title' => $this->t('Delete'),
-        'url' => Url::fromRoute('page_manager.selection_condition_delete', [
-          'page' => $page_variant->get('page'),
-          'page_variant' => $page_variant->id(),
-          'condition_id' => $selection_id,
-        ]),
-        'attributes' => $attributes,
-      ];
-      $row['operations'] = [
-        '#type' => 'operations',
-        '#links' => $operations,
-      ];
-      $form['table'][$selection_id] = $row;
-    }
-
-    return $form;
-  }
-
-  /**
-   * Builds the context form for a variant.
-   *
-   * @return array
-   */
-  protected function buildContextForm() {
-    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
-    $page_variant = $this->getEntity();
-
-    // Set up the attributes used by a modal to prevent duplication later.
-    $attributes = $this->getAjaxAttributes();
-    $add_button_attributes = $this->getAjaxButtonAttributes();
-
-    $form = [
-      '#type' => 'details',
-      '#title' => $this->t('Available context'),
-      '#open' => TRUE,
-    ];
-    $form['add'] = [
-      '#type' => 'link',
-      '#title' => $this->t('Add new static context'),
-      '#url' => Url::fromRoute('page_manager.static_context_add', [
-        'page' => $page_variant->get('page'),
-        'page_variant' => $page_variant->id(),
-      ]),
-      '#attributes' => $add_button_attributes,
-      '#attached' => [
-        'library' => [
-          'core/drupal.ajax',
-        ],
-      ],
-    ];
-    $form['available_context'] = [
-      '#type' => 'table',
-      '#header' => [
-        $this->t('Label'),
-        $this->t('Name'),
-        $this->t('Type'),
-        $this->t('Operations'),
-      ],
-      '#empty' => $this->t('There is no available context.'),
-    ];
-    $contexts = $page_variant->getContexts();
-    foreach ($contexts as $name => $context) {
-      $context_definition = $context->getContextDefinition();
-
-      $row = [];
-      $row['label'] = [
-        '#markup' => $context_definition->getLabel(),
-      ];
-      $row['machine_name'] = [
-        '#markup' => $name,
-      ];
-      $row['type'] = [
-        '#markup' => $context_definition->getDataType(),
-      ];
-
-      // Add operation links if the context is a static context.
-      $operations = [];
-      if ($page_variant->getStaticContext($name)) {
-        $operations['edit'] = [
-          'title' => $this->t('Edit'),
-          'url' => Url::fromRoute('page_manager.static_context_edit', [
-            'page' => $page_variant->get('page'),
-            'page_variant' => $page_variant->id(),
-            'name' => $name,
-          ]),
-          'attributes' => $attributes,
-        ];
-        $operations['delete'] = [
-          'title' => $this->t('Delete'),
-          'url' => Url::fromRoute('page_manager.static_context_delete', [
-            'page' => $page_variant->get('page'),
-            'page_variant' => $page_variant->id(),
-            'name' => $name,
-          ]),
-          'attributes' => $attributes,
-        ];
-      }
-      $row['operations'] = [
-        '#type' => 'operations',
-        '#links' => $operations,
-      ];
-
-      $form['available_context'][$name] = $row;
-    }
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function save(array $form, FormStateInterface $form_state) {
-    // @todo This feels very wrong.
-    $variant_plugin = $this->getVariantPlugin();
-    if ($variant_plugin instanceof BlockVariantInterface) {
-      // If the blocks were rearranged, update their values.
-      if (!$form_state->isValueEmpty(['variant_plugin', 'blocks'])) {
-        foreach ($form_state->getValue(['variant_plugin', 'blocks']) as $block_id => $block_values) {
-          $variant_plugin->updateBlock($block_id, $block_values);
-        }
-      }
-    }
-
-    parent::save($form, $form_state);
-
-    $form_state->setRedirect('entity.page.edit_form', ['page' => $this->entity->get('page')]);
-  }
-
-}
diff --git a/page_manager_ui/src/Form/PageVariantFormBase.php b/page_manager_ui/src/Form/PageVariantFormBase.php
deleted file mode 100644
index 719f86f..0000000
--- a/page_manager_ui/src/Form/PageVariantFormBase.php
+++ /dev/null
@@ -1,177 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\page_manager_ui\Form\PageVariantFormBase.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Entity\EntityForm;
-use Drupal\Core\Entity\Query\QueryFactory;
-use Drupal\Core\Form\FormState;
-use Drupal\Core\Form\FormStateInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides a base form for editing and adding a page variant.
- */
-abstract class PageVariantFormBase extends EntityForm {
-
-  /**
-   * The entity being used by this form.
-   *
-   * @var \Drupal\page_manager\PageVariantInterface
-   */
-  protected $entity;
-
-  /**
-   * The variant plugin for this page variant entity.
-   *
-   * @var \Drupal\Core\Display\VariantInterface
-   */
-  protected $variantPlugin;
-
-  /**
-   * The entity query factory.
-   *
-   * @var \Drupal\Core\Entity\Query\QueryFactory
-   */
-  protected $entityQuery;
-
-  /**
-   * Construct a new PageFormBase.
-   *
-   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
-   *   The entity query factory.
-   */
-  public function __construct(QueryFactory $entity_query) {
-    $this->entityQuery = $entity_query;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity.query')
-    );
-  }
-
-  /**
-   * Returns the text to use for the submit button.
-   *
-   * @return string
-   *   The submit button text.
-   */
-  abstract protected function submitText();
-
-  /**
-   * {@inheritdoc}
-   */
-  public function form(array $form, FormStateInterface $form_state) {
-    $form = parent::form($form, $form_state);
-
-    $form['label'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('Label'),
-      '#description' => $this->t('The label for this variant.'),
-      '#default_value' => $this->entity->label() ?: (string) $this->getVariantPlugin()->adminLabel(),
-      '#maxlength' => '255',
-    ];
-
-    $form['id'] = [
-      '#type' => 'machine_name',
-      '#disabled' => !$this->entity->isNew(),
-      '#default_value' => !$this->entity->isNew() ? $this->entity->id() : '',
-      '#machine_name' => [
-        'exists' => [$this, 'exists'],
-      ],
-    ];
-
-    // Allow the variant to add to the form.
-    $form['variant_settings'] = $this->getVariantPlugin()->buildConfigurationForm([], $form_state);
-    $form['variant_settings']['#tree'] = TRUE;
-
-    $form['actions'] = ['#type' => 'actions'];
-    $form['actions']['submit'] = [
-      '#type' => 'submit',
-      '#value' => 'Add/Edit',
-      '#button_type' => 'primary',
-    ];
-
-    return $form;
-  }
-
-  /**
-   * Determines if the page variant entity already exists.
-   *
-   * @param string $id
-   *   The page variant entity ID.
-   *
-   * @return bool
-   *   TRUE if the entity exists, FALSE otherwise.
-   */
-  public function exists($id) {
-    return (bool) $this->entityQuery->get('page_variant')
-      ->condition('id', $id)
-      ->execute();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function save(array $form, FormStateInterface $form_state) {
-    $status = parent::save($form, $form_state);
-
-    if ($status) {
-      drupal_set_message($this->t('Saved the %label variant.', [
-        '%label' => $this->entity->label(),
-      ]));
-    }
-    else {
-      drupal_set_message($this->t('The %label variant was not saved.', [
-        '%label' => $this->entity->label(),
-      ]));
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    parent::validateForm($form, $form_state);
-
-    // Allow the variant to validate the form.
-    $variant_plugin_values = (new FormState())->setValues($form_state->getValue('variant_settings'));
-    $this->getVariantPlugin()->validateConfigurationForm($form, $variant_plugin_values);
-    // Update the original form values.
-    $form_state->setValue('variant_settings', $variant_plugin_values->getValues());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    // Allow the variant to submit the form.
-    $variant_plugin_values = (new FormState())->setValues($form_state->getValue('variant_settings'));
-    $this->getVariantPlugin()->submitConfigurationForm($form, $variant_plugin_values);
-    // Update the original form values.
-    $form_state->setValue('variant_settings', $variant_plugin_values->getValues());
-
-    parent::submitForm($form, $form_state);
-  }
-
-  /**
-   * Gets the variant plugin for this page variant entity.
-   *
-   * @return \Drupal\Core\Display\VariantInterface
-   */
-  protected function getVariantPlugin() {
-    if (!$this->variantPlugin) {
-      $this->variantPlugin = $this->entity->getVariantPlugin();
-    }
-    return $this->variantPlugin;
-  }
-
-}
diff --git a/page_manager_ui/src/Form/PageVariantSelectionForm.php b/page_manager_ui/src/Form/PageVariantSelectionForm.php
new file mode 100644
index 0000000..5fc080d
--- /dev/null
+++ b/page_manager_ui/src/Form/PageVariantSelectionForm.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\PageAccessForm.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+
+use Drupal\ctools\Form\ManageConditions;
+
+class PageVariantSelectionForm extends ManageConditions {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_access_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditionClass() {
+    return 'Drupal\page_manager_ui\Form\SelectionConfigure';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.page';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getOperationsRouteInfo($cached_values, $machine_name, $row) {
+    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
+    $page_variant = $cached_values['page_variant'];
+    return ['entity.page_variant.condition', [
+      'machine_name' => $machine_name,
+      'variant_machine_name' => $page_variant->id(),
+      'condition' => $row
+    ]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditions($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\PageVariant */
+    $page_variant = $cached_values['page_variant'];
+    return $page_variant->get('selection_criteria');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    return $page->getContexts();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAddRoute($cached_values) {
+    return 'entity.page_variant.condition.add';
+  }
+
+}
diff --git a/page_manager_ui/src/Form/ParameterEditForm.php b/page_manager_ui/src/Form/ParameterEditForm.php
index 6b53955..2cd01ae 100644
--- a/page_manager_ui/src/Form/ParameterEditForm.php
+++ b/page_manager_ui/src/Form/ParameterEditForm.php
@@ -13,6 +13,7 @@ use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\TypedData\PrimitiveInterface;
 use Drupal\Core\TypedData\TypedDataManagerInterface;
 use Drupal\page_manager\PageInterface;
+use Drupal\user\SharedTempStoreFactory;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -49,16 +50,29 @@ class ParameterEditForm extends FormBase {
   protected $typedDataManager;
 
   /**
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * @var string
+   */
+  protected $tempstore_id;
+
+  /**
    * Constructs a new ParameterEditForm.
    *
    * @param \Drupal\Core\Entity\EntityTypeRepositoryInterface $entity_type_repository
    *   The entity type repository.
    * @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
    *   The typed data manager.
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore
+   *   The temporary store.
    */
-  public function __construct(EntityTypeRepositoryInterface $entity_type_repository, TypedDataManagerInterface $typed_data_manager) {
+  public function __construct(EntityTypeRepositoryInterface $entity_type_repository, TypedDataManagerInterface $typed_data_manager, SharedTempStoreFactory $tempstore) {
     $this->entityTypeRepository = $entity_type_repository;
     $this->typedDataManager = $typed_data_manager;
+    $this->tempstore = $tempstore;
   }
 
   /**
@@ -67,10 +81,19 @@ class ParameterEditForm extends FormBase {
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('entity_type.repository'),
-      $container->get('typed_data_manager')
+      $container->get('typed_data_manager'),
+      $container->get('user.shared_tempstore')
     );
   }
 
+  protected function getTempstore() {
+    return $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
+  }
+
+  protected function setTempstore($cached_values) {
+    $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values);
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -81,9 +104,12 @@ class ParameterEditForm extends FormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $name = '') {
-    $this->page = $page;
-    $parameter = $this->page->getParameter($name);
+  public function buildForm(array $form, FormStateInterface $form_state, $name = '', $tempstore_id = NULL, $machine_name = NULL) {
+    $this->tempstore_id = $tempstore_id;
+    $this->machine_name = $machine_name;
+    $cached_values = $this->getTempstore();
+    $page = $cached_values['page'];
+    $parameter = $page->getParameter($name);
 
     $form['machine_name'] = [
       '#type' => 'value',
@@ -153,23 +179,42 @@ class ParameterEditForm extends FormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cache_values = $this->getTempstore();
+    /** @var \Drupal\page_manager\PageInterface $page */
+    $page = $cache_values['page'];
     $name = $form_state->getValue('machine_name');
     $type = $form_state->getValue('type');
-
     if ($type === static::NO_CONTEXT_KEY) {
-      $this->page->removeParameter($name);
+      $page->removeParameter($name);
       $label = NULL;
     }
     else {
       $label = $form_state->getValue('label');
-      $this->page->setParameter($name, $type, $label);
+      $page->setParameter($name, $type, $label);
     }
-    $this->page->save();
 
-    // Set the submission message.
+    $this->setTempstore($cache_values);
     drupal_set_message($this->t('The %label parameter has been updated.', ['%label' => $label ?: $name]));
+    list($route_name, $route_parameters) = $this->getParentRouteInfo($cache_values);
+    $form_state->setRedirect($route_name, $route_parameters);
+  }
 
-    $form_state->setRedirectUrl($this->page->toUrl('edit-form'));
+  protected function getParentRouteInfo($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+
+    if ($page->isNew()) {
+      return ['entity.page.add_step_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'selection',
+      ]];
+    }
+    else {
+      return ['entity.page.edit_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'parameters',
+      ]];
+    }
   }
 
 }
diff --git a/page_manager_ui/src/Form/SelectionConditionAddForm.php b/page_manager_ui/src/Form/SelectionConditionAddForm.php
deleted file mode 100644
index c392745..0000000
--- a/page_manager_ui/src/Form/SelectionConditionAddForm.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\SelectionConditionAddForm.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Condition\ConditionManager;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides a form for adding a new selection condition.
- */
-class SelectionConditionAddForm extends SelectionConditionFormBase {
-
-  /**
-   * The condition manager.
-   *
-   * @var \Drupal\Core\Condition\ConditionManager
-   */
-  protected $conditionManager;
-
-  /**
-   * Constructs a new SelectionConditionAddForm.
-   *
-   * @param \Drupal\Core\Condition\ConditionManager $condition_manager
-   *   The condition manager.
-   */
-  public function __construct(ConditionManager $condition_manager) {
-    $this->conditionManager = $condition_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('plugin.manager.condition')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_manager_selection_condition_add_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function prepareCondition($condition_id) {
-    // Create a new selection condition instance.
-    return $this->conditionManager->createInstance($condition_id);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitButtonText() {
-    return $this->t('Add selection condition');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitMessageText() {
-    return $this->t('The %label selection condition has been added.', ['%label' => $this->condition->getPluginDefinition()['label']]);
-  }
-
-}
diff --git a/page_manager_ui/src/Form/SelectionConditionDeleteForm.php b/page_manager_ui/src/Form/SelectionConditionDeleteForm.php
deleted file mode 100644
index 4795fa2..0000000
--- a/page_manager_ui/src/Form/SelectionConditionDeleteForm.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\SelectionConditionDeleteForm.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Form\ConfirmFormBase;
-use Drupal\page_manager\PageVariantInterface;
-
-/**
- * Provides a form for deleting a selection condition.
- */
-class SelectionConditionDeleteForm extends ConfirmFormBase {
-
-  /**
-   * The page entity this selection condition belongs to.
-   *
-   * @var \Drupal\page_manager\PageVariantInterface
-   */
-  protected $pageVariant;
-
-  /**
-   * The selection condition used by this form.
-   *
-   * @var \Drupal\Core\Condition\ConditionInterface
-   */
-  protected $selectionCondition;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_manager_selection_condition_delete_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQuestion() {
-    return $this->t('Are you sure you want to delete the selection condition %name?', ['%name' => $this->selectionCondition->getPluginDefinition()['label']]);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCancelUrl() {
-    return $this->pageVariant->toUrl('edit-form');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfirmText() {
-    return $this->t('Delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $condition_id = NULL) {
-    $this->pageVariant = $page_variant;
-    $this->selectionCondition = $page_variant->getSelectionCondition($condition_id);
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $this->pageVariant->removeSelectionCondition($this->selectionCondition->getConfiguration()['uuid']);
-    $this->pageVariant->save();
-    drupal_set_message($this->t('The selection condition %name has been removed.', ['%name' => $this->selectionCondition->getPluginDefinition()['label']]));
-    $form_state->setRedirectUrl($this->getCancelUrl());
-  }
-
-}
diff --git a/page_manager_ui/src/Form/SelectionConditionEditForm.php b/page_manager_ui/src/Form/SelectionConditionEditForm.php
deleted file mode 100644
index 416a724..0000000
--- a/page_manager_ui/src/Form/SelectionConditionEditForm.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\SelectionConditionEditForm.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-/**
- * Provides a form for editing an selection condition.
- */
-class SelectionConditionEditForm extends SelectionConditionFormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_manager_selection_condition_edit_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function prepareCondition($condition_id) {
-    // Load the selection condition directly from the variant.
-    return $this->pageVariant->getSelectionCondition($condition_id);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitButtonText() {
-    return $this->t('Update selection condition');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitMessageText() {
-    return $this->t('The %label selection condition has been updated.', ['%label' => $this->condition->getPluginDefinition()['label']]);
-  }
-
-}
diff --git a/page_manager_ui/src/Form/SelectionConditionFormBase.php b/page_manager_ui/src/Form/SelectionConditionFormBase.php
deleted file mode 100644
index e1087ff..0000000
--- a/page_manager_ui/src/Form/SelectionConditionFormBase.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager_ui\Form\SelectionConditionFormBase.
- */
-
-namespace Drupal\page_manager_ui\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageVariantInterface;
-
-/**
- * Provides a base form for editing and adding a selection condition.
- */
-abstract class SelectionConditionFormBase extends ConditionFormBase {
-
-  /**
-   * The page variant entity.
-   *
-   * @var \Drupal\page_manager\PageVariantInterface
-   */
-  protected $pageVariant;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $condition_id = NULL) {
-    $this->pageVariant = $page_variant;
-    return parent::buildForm($form, $form_state, $condition_id, $page_variant->getContexts());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    parent::submitForm($form, $form_state);
-
-    $configuration = $this->condition->getConfiguration();
-    // If this selection condition is new, add it to the page.
-    if (!isset($configuration['uuid'])) {
-      $this->pageVariant->addSelectionCondition($configuration);
-    }
-
-    // Save the page entity.
-    $this->pageVariant->save();
-
-    $form_state->setRedirectUrl($this->pageVariant->toUrl('edit-form'));
-  }
-
-}
diff --git a/page_manager_ui/src/Form/SelectionConfigure.php b/page_manager_ui/src/Form/SelectionConfigure.php
new file mode 100644
index 0000000..23b933d
--- /dev/null
+++ b/page_manager_ui/src/Form/SelectionConfigure.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\SelectionConfigure.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Form\ConditionConfigure;
+
+class SelectionConfigure extends ConditionConfigure {
+
+  /**
+   * The machine-name of the variant.
+   *
+   * @var string
+   */
+  protected $variantMachineName;
+
+  /**
+   * Get the page variant.
+   *
+   * @param array $cached_values
+   *   The cached values from the wizard.
+   *
+   * @return \Drupal\page_manager\PageVariantInterface
+   */
+  protected function getPageVariant($cached_values) {
+    if (isset($cached_values['page_variant'])) {
+      return $cached_values['page_variant'];
+    }
+
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    return $page->getVariant($this->variantMachineName);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getParentRouteInfo($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+
+    if ($page->isNew()) {
+      return ['entity.page.add_step_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'selection',
+      ]];
+    }
+    else {
+      $page_variant = $this->getPageVariant($cached_values);
+      return ['entity.page.edit_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'page_variant__' . $page_variant->id() . '__selection',
+      ]];
+    }
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $condition = NULL, $tempstore_id = NULL, $machine_name = NULL, $variant_machine_name = NULL) {
+    $this->variantMachineName = $variant_machine_name;
+    return parent::buildForm($form, $form_state, $condition, $tempstore_id, $machine_name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditions($cached_values) {
+    $page_variant = $this->getPageVariant($cached_values);
+    return $page_variant->get('selection_criteria');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setConditions($cached_values, $conditions) {
+    $page_variant = $this->getPageVariant($cached_values);
+    $page_variant->set('selection_criteria', $conditions);
+    return $cached_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\PageVariant */
+    $page_variant = $this->getPageVariant($cached_values);
+    return $page_variant->getContexts();
+  }
+
+}
diff --git a/page_manager_ui/src/Form/SelectionDelete.php b/page_manager_ui/src/Form/SelectionDelete.php
new file mode 100644
index 0000000..6aab426
--- /dev/null
+++ b/page_manager_ui/src/Form/SelectionDelete.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\SelectionDelete.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Form\ConditionDelete;
+
+class SelectionDelete extends ConditionDelete {
+
+  /**
+   * The machine-name of the variant.
+   *
+   * @var string
+   */
+  protected $variantMachineName;
+
+  /**
+   * Get the page variant.
+   *
+   * @param array $cached_values
+   *   The cached values from the wizard.
+   *
+   * @return \Drupal\page_manager\PageVariantInterface
+   */
+  protected function getPageVariant($cached_values) {
+    if (isset($cached_values['page_variant'])) {
+      return $cached_values['page_variant'];
+    }
+
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    return $page->getVariant($this->variantMachineName);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getParentRouteInfo($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+
+    if ($page->isNew()) {
+      return ['entity.page.add_step_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'selection',
+      ]];
+    }
+    else {
+      $page_variant = $this->getPageVariant($cached_values);
+      return ['entity.page.edit_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'page_variant__' . $page_variant->id() . '__selection',
+      ]];
+    }
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $id = NULL, $tempstore_id = NULL, $machine_name = NULL, $variant_machine_name = NULL) {
+    $this->variantMachineName = $variant_machine_name;
+    return parent::buildForm($form, $form_state, $id, $tempstore_id, $machine_name);
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditions($cached_values) {
+    $page_variant = $this->getPageVariant($cached_values);
+    return $page_variant->get('selection_criteria');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setConditions($cached_values, $conditions) {
+    $page_variant = $this->getPageVariant($cached_values);
+    $page_variant->set('selection_criteria', $conditions);
+    return $cached_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    return $page->getContexts();
+  }
+
+}
diff --git a/page_manager_ui/src/Form/StaticContextConfigure.php b/page_manager_ui/src/Form/StaticContextConfigure.php
new file mode 100644
index 0000000..3334ec0
--- /dev/null
+++ b/page_manager_ui/src/Form/StaticContextConfigure.php
@@ -0,0 +1,212 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Form\StaticContextConfigure.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseModalDialogCommand;
+use Drupal\Core\Ajax\RedirectCommand;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\TypedData\TypedDataManagerInterface;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class StaticContextConfigure extends FormBase {
+
+  /**
+   * @var \Drupal\Core\TypedData\TypedDataManagerInterface
+   */
+  protected $typedDataManager;
+
+  /**
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * @var string
+   */
+  protected $tempstore_id;
+
+  /**
+   * @var string
+   */
+  protected $machine_name;
+
+  /**
+   * The machine-name of the variant.
+   *
+   * @var string
+   */
+  protected $variantMachineName;
+
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('typed_data_manager'), $container->get('user.shared_tempstore'));
+  }
+
+  public function __construct(TypedDataManagerInterface $typed_data_manager, SharedTempStoreFactory $tempstore) {
+    $this->typedDataManager = $typed_data_manager;
+    $this->tempstore = $tempstore;
+  }
+
+  protected function getTempstore() {
+    return $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
+  }
+
+  protected function setTempstore($cached_values) {
+    $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values);
+  }
+
+  public function getFormId() {
+    return 'page_manager_configure_static_context';
+  }
+
+  public function buildForm(array $form, FormStateInterface $form_state, $op = 'edit', $data_type = NULL, $tempstore_id = NULL, $machine_name = NULL, $variant_machine_name = NULL) {
+    $this->tempstore_id = $tempstore_id;
+    $this->machine_name = $machine_name;
+    $this->variantMachineName = $variant_machine_name;
+    $cached_values = $this->getTempstore();
+    /** @var \Drupal\page_manager\PageInterface $page */
+    $page_variant = $this->getPageVariant($cached_values);
+    if ($op == 'edit') {
+      $context = $page_variant->getStaticContext($data_type);
+      $form['type'] = [
+        '#type' => 'value',
+        '#value' => $context['type'],
+      ];
+    }
+    else {
+      $form['type'] = [
+        '#type' => 'value',
+        '#value' => $data_type,
+      ];
+    }
+    $form['label'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Context label'),
+      '#description' => $this->t('Provide a label for this context object for use in the user interface.'),
+      '#default_value' => isset($context) ? $context['label'] : '',
+      '#required' => TRUE,
+      '#size' => 32,
+    ];
+    $form['id'] = [
+      '#type' => 'machine_name',
+      '#maxlength' => 128,
+      '#machine_name' => [
+        'exists' => [$this, 'exists'],
+      ],
+      '#description' => $this->t('A unique machine-readable name for this context.'),
+      '#default_value' => ($op == 'edit') ? $data_type : '',
+      '#disabled' => ($op == 'edit'),
+    ];
+    $type = ($op == 'edit' && isset($context)) ? $context['type'] : $data_type;
+    if (substr($type, 0, 7) == 'entity:') {
+      if (isset($context) && ($context['value'] !== NULL)) {
+        $value_entity = \Drupal::entityManager()->loadEntityByUuid(substr($type, 7), $context['value']);
+      }
+      $form['value'] = [
+        '#type' => 'entity_autocomplete',
+        '#title' => $this->t('Choose an entity to reference'),
+        '#target_type' => substr($type, 7),
+        '#maxlength' => 1024,
+        '#default_value' => isset($value_entity) ? $value_entity : NULL,
+      ];
+    }
+    else {
+      $form['value'] = [
+        '#type' => 'textfield',
+        '#title' => $this->t('Enter a value for the context'),
+        '#default_value' => isset($context) ? $context['value'] : NULL,
+      ];
+    }
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Save'),
+      '#ajax' => [
+        'callback' => [$this, 'ajaxSave'],
+      ]
+    ];
+
+    return $form;
+  }
+
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $this->getTempstore();
+    /** @var \Drupal\page_manager\PageInterface $page */
+    $page_variant = $this->getPageVariant($cached_values);
+    $value = $form_state->getValue('value');
+    $type = $form_state->getValue('type');
+    if ((substr($type, 0, 7) == 'entity:') && ($value !== NULL))  {
+      $entity = \Drupal::entityTypeManager()->getStorage(substr($type, 7))->load($value);
+      $value = $entity->uuid();
+    }
+    $config = [
+      'label' => $form_state->getValue('label'),
+      'type' => $form_state->getValue('type'),
+      'value' => $value,
+    ];
+    $page_variant->setStaticContext($form_state->getValue('id'), $config);
+    $this->setTempstore($cached_values);
+    list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
+    $form_state->setRedirect($route_name, $route_parameters);
+  }
+
+  public function ajaxSave(array &$form, FormStateInterface $form_state) {
+    $response = new AjaxResponse();
+    $cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
+    list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
+    $response->addCommand(new RedirectCommand($this->url($route_name, $route_parameters)));
+    $response->addCommand(new CloseModalDialogCommand());
+    return $response;
+  }
+
+  public function exists($key, $element, FormStateInterface $form_state) {
+    $cached_values = $this->getTempstore();
+    /** @var \Drupal\page_manager\PageInterface $page */
+    $page = $cached_values['page'];
+    $contexts = $page->getContexts();
+    return !empty($contexts[$key]);
+  }
+
+  protected function getParentRouteInfo($cached_values) {
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+
+    if ($page->isNew()) {
+      return ['entity.page.add_step_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'contexts',
+      ]];
+    }
+    else {
+      $page_variant = $this->getPageVariant($cached_values);
+      return ['entity.page.edit_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'page_variant__' . $page_variant->id() . '__contexts',
+      ]];
+    }
+  }
+
+  /**
+   * Returns the page variant.
+   *
+   * @param array $cached_values
+   *   The cached values from the wizard.
+   *
+   * @return \Drupal\page_manager\PageVariantInterface
+   */
+  protected function getPageVariant($cached_values) {
+    if (isset($cached_values['page_variant'])) {
+      return $cached_values['page_variant'];
+    }
+
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    return $page->getVariant($this->variantMachineName);
+  }
+
+}
diff --git a/page_manager_ui/src/Form/StaticContextDeleteForm.php b/page_manager_ui/src/Form/StaticContextDeleteForm.php
index 554b27c..702655b 100644
--- a/page_manager_ui/src/Form/StaticContextDeleteForm.php
+++ b/page_manager_ui/src/Form/StaticContextDeleteForm.php
@@ -8,8 +8,10 @@
 namespace Drupal\page_manager_ui\Form;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Drupal\Core\Form\ConfirmFormBase;
-use Drupal\page_manager\PageVariantInterface;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Provides a form for deleting an access condition.
@@ -17,18 +19,41 @@ use Drupal\page_manager\PageVariantInterface;
 class StaticContextDeleteForm extends ConfirmFormBase {
 
   /**
-   * The page variant entity this selection condition belongs to.
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * @var string
+   */
+  protected $tempstore_id;
+
+  /**
+   * @var string
+   */
+  protected $machine_name;
+
+  /**
+   * The machine-name of the variant.
    *
-   * @var \Drupal\page_manager\PageVariantInterface
+   * @var string
    */
-  protected $pageVariant;
+  protected $variantMachineName;
 
   /**
    * The static context's machine name.
    *
    * @var array
    */
-  protected $staticContext;
+  protected $data_type;
+
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('user.shared_tempstore'));
+  }
+
+  public function __construct(SharedTempStoreFactory $tempstore) {
+    $this->tempstore = $tempstore;
+  }
 
   /**
    * {@inheritdoc}
@@ -41,14 +66,33 @@ class StaticContextDeleteForm extends ConfirmFormBase {
    * {@inheritdoc}
    */
   public function getQuestion() {
-    return $this->t('Are you sure you want to delete the static context %label?', ['%label' => $this->pageVariant->getStaticContext($this->staticContext)['label']]);
+    $cached_values = $this->getTempstore();
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page_variant = $this->getPageVariant($cached_values);
+    return $this->t('Are you sure you want to delete the static context %label?', ['%label' => $page_variant->getStaticContext($this->data_type)['label']]);
   }
 
   /**
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->pageVariant->toUrl('edit-form');
+    $cached_values = $this->getTempstore();
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+
+    if ($page->isNew()) {
+      return new Url('entity.page.add_step_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'selection',
+      ]);
+    }
+    else {
+      $page_variant = $this->getPageVariant($cached_values);
+      return new Url('entity.page.edit_form', [
+        'machine_name' => $this->machine_name,
+        'step' => 'page_variant__' . $page_variant->id() . '__contexts',
+      ]);
+    }
   }
 
   /**
@@ -61,9 +105,11 @@ class StaticContextDeleteForm extends ConfirmFormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $name = NULL) {
-    $this->pageVariant = $page_variant;
-    $this->staticContext = $name;
+  public function buildForm(array $form, FormStateInterface $form_state, $tempstore_id = NULL, $machine_name = NULL, $variant_machine_name = NULL, $data_type = NULL) {
+    $this->tempstore_id = $tempstore_id;
+    $this->machine_name = $machine_name;
+    $this->variantMachineName = $variant_machine_name;
+    $this->data_type = $data_type;
     return parent::buildForm($form, $form_state);
   }
 
@@ -71,10 +117,39 @@ class StaticContextDeleteForm extends ConfirmFormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    drupal_set_message($this->t('The static context %label has been removed.', ['%label' => $this->pageVariant->getStaticContext($this->staticContext)['label']]));
-    $this->pageVariant->removeStaticContext($this->staticContext);
-    $this->pageVariant->save();
+    $cached_values = $this->getTempstore();
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page_variant = $this->getPageVariant($cached_values);
+    drupal_set_message($this->t('The static context %label has been removed.', ['%label' => $page_variant->getStaticContext($this->data_type)['label']]));
+    $page_variant->removeStaticContext($this->data_type);
+    $this->setTempstore($cached_values);
     $form_state->setRedirectUrl($this->getCancelUrl());
   }
 
+  protected function getTempstore() {
+    return $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
+  }
+
+  protected function setTempstore($cached_values) {
+    $this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values);
+  }
+
+  /**
+   * Get the page variant.
+   *
+   * @param array $cached_values
+   *   The cached values from the wizard.
+   *
+   * @return \Drupal\page_manager\PageVariantInterface
+   */
+  protected function getPageVariant($cached_values) {
+    if (isset($cached_values['page_variant'])) {
+      return $cached_values['page_variant'];
+    }
+
+    /** @var $page \Drupal\page_manager\PageInterface */
+    $page = $cached_values['page'];
+    return $page->getVariant($this->variantMachineName);
+  }
+
 }
diff --git a/page_manager_ui/src/Form/VariantPluginAddBlockForm.php b/page_manager_ui/src/Form/VariantPluginAddBlockForm.php
index f0709b1..a372a12 100644
--- a/page_manager_ui/src/Form/VariantPluginAddBlockForm.php
+++ b/page_manager_ui/src/Form/VariantPluginAddBlockForm.php
@@ -10,6 +10,7 @@ namespace Drupal\page_manager_ui\Form;
 use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\page_manager\PageVariantInterface;
+use Drupal\user\SharedTempStoreFactory;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -31,7 +32,8 @@ class VariantPluginAddBlockForm extends VariantPluginConfigureBlockFormBase {
    * @param \Drupal\Component\Plugin\PluginManagerInterface $block_manager
    *   The block manager.
    */
-  public function __construct(PluginManagerInterface $block_manager) {
+  public function __construct(SharedTempStoreFactory $tempstore, PluginManagerInterface $block_manager) {
+    parent::__construct($tempstore);
     $this->blockManager = $block_manager;
   }
 
@@ -40,6 +42,7 @@ class VariantPluginAddBlockForm extends VariantPluginConfigureBlockFormBase {
    */
   public static function create(ContainerInterface $container) {
     return new static(
+      $container->get('user.shared_tempstore'),
       $container->get('plugin.manager.block')
     );
   }
@@ -63,8 +66,8 @@ class VariantPluginAddBlockForm extends VariantPluginConfigureBlockFormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, PageVariantInterface $page_variant = NULL, $block_id = NULL) {
-    $form = parent::buildForm($form, $form_state, $page_variant, $block_id);
+  public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, $block_display = NULL, $block_id = NULL) {
+    $form = parent::buildForm($form, $form_state, $block_display, $block_id);
     $form['region']['#default_value'] = $request->query->get('region');
     return $form;
   }
diff --git a/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php b/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php
index 7d440ef..59de6c6 100644
--- a/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php
+++ b/page_manager_ui/src/Form/VariantPluginConfigureBlockFormBase.php
@@ -10,9 +10,12 @@ namespace Drupal\page_manager_ui\Form;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\Context\Context;
 use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
 use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\page_manager\PageVariantInterface;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Provides a base form for configuring a block as part of a variant.
@@ -22,11 +25,18 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase {
   use ContextAwarePluginAssignmentTrait;
 
   /**
-   * The page entity.
+   * Tempstore factory.
    *
-   * @var \Drupal\page_manager\PageVariantInterface
+   * @var \Drupal\user\SharedTempStoreFactory
    */
-  protected $pageVariant;
+  protected $tempstore;
+
+  /**
+   * The variant plugin.
+   *
+   * @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant
+   */
+  protected $variantPlugin;
 
   /**
    * The plugin being configured.
@@ -36,6 +46,43 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase {
   protected $block;
 
   /**
+   * Constructs a new VariantPluginConfigureBlockFormBase.
+   *
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore
+   *   The tempstore factory.
+   */
+  public function __construct(SharedTempStoreFactory $tempstore) {
+    $this->tempstore = $tempstore;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.shared_tempstore')
+    );
+  }
+
+  /**
+   * Get the tempstore id.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.block_display';
+  }
+
+  /**
+   * Get the tempstore.
+   *
+   * @return \Drupal\user\SharedTempStore
+   */
+  protected function getTempstore() {
+    return $this->tempstore->get($this->getTempstoreId());
+  }
+
+  /**
    * Prepares the block plugin based on the block ID.
    *
    * @param string $block_id
@@ -57,10 +104,24 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $block_id = NULL) {
-    $this->pageVariant = $page_variant;
+  public function buildForm(array $form, FormStateInterface $form_state, $block_display = NULL, $block_id = NULL) {
+    $cached_values = $this->tempstore->get('page_manager.block_display')->get($block_display);
+    /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */
+    $this->variantPlugin = $cached_values['plugin'];
+
+    // Rehydrate the contexts on this end.
+    $contexts = [];
+    /**
+     * @var string $context_name
+     * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context_definition
+     */
+    foreach ($cached_values['contexts'] as $context_name => $context_definition) {
+      $contexts[$context_name] = new Context($context_definition);
+    }
+    $this->variantPlugin->setContexts($contexts);
+
     $this->block = $this->prepareBlock($block_id);
-    $form_state->set('page_variant_id', $page_variant->id());
+    $form_state->set('variant_id', $this->getVariantPlugin()->id());
     $form_state->set('block_id', $this->block->getConfiguration()['uuid']);
 
     $form['#tree'] = TRUE;
@@ -78,7 +139,7 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase {
     ];
 
     if ($this->block instanceof ContextAwarePluginInterface) {
-      $form['context_mapping'] = $this->addContextAssignmentElement($this->block, $this->pageVariant->getContexts());
+      $form['context_mapping'] = $this->addContextAssignmentElement($this->block, $this->getVariantPlugin()->getContexts());
     }
 
     $form['actions']['submit'] = [
@@ -120,9 +181,10 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase {
     }
 
     $this->getVariantPlugin()->updateBlock($this->block->getConfiguration()['uuid'], ['region' => $form_state->getValue('region')]);
-    $this->pageVariant->save();
 
-    $form_state->setRedirectUrl($this->pageVariant->toUrl('edit-form'));
+    $cached_values = $this->getTempstore()->get($form_state->get('variant_id'));
+    $cached_values['plugin'] = $this->getVariantPlugin();
+    $this->getTempstore()->set($form_state->get('variant_id'), $cached_values);
   }
 
   /**
@@ -131,7 +193,7 @@ abstract class VariantPluginConfigureBlockFormBase extends FormBase {
    * @return \Drupal\ctools\Plugin\BlockVariantInterface
    */
   protected function getVariantPlugin() {
-    return $this->pageVariant->getVariantPlugin();
+    return $this->variantPlugin;
   }
 
 }
diff --git a/page_manager_ui/src/Form/VariantPluginContentForm.php b/page_manager_ui/src/Form/VariantPluginContentForm.php
new file mode 100644
index 0000000..3d67314
--- /dev/null
+++ b/page_manager_ui/src/Form/VariantPluginContentForm.php
@@ -0,0 +1,257 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\page_manager_ui\Form\VariantPluginContentForm.
+ */
+
+namespace Drupal\page_manager_ui\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\ctools\Form\AjaxFormTrait;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a form for editing a variant.
+ */
+class VariantPluginContentForm extends FormBase {
+
+  use AjaxFormTrait;
+
+  /**
+   * Tempstore factory.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * Constructs a new VariantPluginContentForm.
+   *
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore
+   *   The tempstore factory.
+   */
+  public function __construct(SharedTempStoreFactory $tempstore) {
+    $this->tempstore = $tempstore;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.shared_tempstore')
+    );
+  }
+
+  /**
+   * Get the tempstore ID.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.block_display';
+  }
+
+  /**
+   * Get the tempstore.
+   *
+   * @return \Drupal\user\SharedTempStore
+   */
+  protected function getTempstore() {
+    return $this->tempstore->get($this->getTempstoreId());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_block_page_content';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */
+    $variant_plugin = $cached_values['plugin'];
+
+    // Store the block display plugin so we can get it in our dialogs.
+    if (!empty($this->getTempstore()->get($variant_plugin->id())['plugin'])) {
+      $variant_plugin->setConfiguration($this->getTempstore()->get($variant_plugin->id())['plugin']->getConfiguration());
+      $form_state->setTemporaryValue('wizard', $cached_values);
+    }
+    $context_definitions = [];
+    foreach ($variant_plugin->getContexts() as $context_name => $context) {
+      $context_definitions[$context_name] = $context->getContextDefinition();
+    }
+    $this->getTempstore()->set($variant_plugin->id(), [
+      'plugin' => $variant_plugin,
+      'access' => $cached_values['access'],
+      'contexts' => $context_definitions,
+    ]);
+
+    // Set up the attributes used by a modal to prevent duplication later.
+    $attributes = $this->getAjaxAttributes();
+    $add_button_attributes = $this->getAjaxButtonAttributes();
+
+    if ($block_assignments = $variant_plugin->getRegionAssignments()) {
+      // Build a table of all blocks used by this variant.
+      $form['add'] = [
+        '#type' => 'link',
+        '#title' => $this->t('Add new block'),
+        '#url' => Url::fromRoute('page_manager.block_display_select_block', [
+          'block_display' => $variant_plugin->id(),
+          'destination' => $this->getRequest()->getRequestUri(),
+        ]),
+        '#attributes' => $add_button_attributes,
+        '#attached' => [
+          'library' => [
+            'core/drupal.ajax',
+          ],
+        ],
+      ];
+      $form['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.'),
+      ];
+      // 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['blocks']['#tabledrag'][] = [
+          'action' => 'match',
+          'relationship' => 'sibling',
+          'group' => 'block-region-select',
+          'subgroup' => 'block-region-' . $region,
+          'hidden' => FALSE,
+        ];
+        $form['blocks']['#tabledrag'][] = [
+          'action' => 'order',
+          'relationship' => 'sibling',
+          'group' => 'block-weight',
+          'subgroup' => 'block-weight-' . $region,
+        ];
+        $form['blocks'][$region] = [
+          '#attributes' => [
+            'class' => ['region-title', 'region-title-' . $region],
+            'no_striping' => TRUE,
+          ],
+        ];
+        $form['blocks'][$region]['title'] = [
+          '#markup' => $variant_plugin->getRegionName($region),
+          '#wrapper_attributes' => [
+            'colspan' => 5,
+          ],
+        ];
+        $form['blocks'][$region . '-message'] = [
+          '#attributes' => [
+            'class' => [
+              'region-message',
+              'region-' . $region . '-message',
+              empty($blocks) ? 'region-empty' : 'region-populated',
+            ],
+          ],
+        ];
+        $form['blocks'][$region . '-message']['message'] = [
+          '#markup' => '<em>' . $this->t('No blocks in this region') . '</em>',
+          '#wrapper_attributes' => [
+            'colspan' => 5,
+          ],
+        ];
+
+        /** @var \Drupal\Core\Block\BlockPluginInterface[] $blocks */
+        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' => $variant_plugin->getRegionNames(),
+            '#default_value' => $variant_plugin->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' => $this->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.block_display_edit_block', [
+              'block_display' => $variant_plugin->id(),
+              'block_id' => $block_id,
+              'destination' => $this->getRequest()->getRequestUri(),
+            ]),
+            'attributes' => $attributes,
+          ];
+          $operations['delete'] = [
+            'title' => $this->t('Delete'),
+            'url' => Url::fromRoute('page_manager.block_display_delete_block', [
+              'block_display' => $variant_plugin->id(),
+              'block_id' => $block_id,
+              'destination' => $this->getRequest()->getRequestUri(),
+            ]),
+            'attributes' => $attributes,
+          ];
+
+          $row['operations'] = [
+            '#type' => 'operations',
+            '#links' => $operations,
+          ];
+          $form['blocks'][$block_id] = $row;
+        }
+      }
+    }
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */
+    $variant_plugin = $cached_values['plugin'];
+
+    // If the blocks were rearranged, update their values.
+    if (!$form_state->isValueEmpty('blocks')) {
+      foreach ($form_state->getValue('blocks') as $block_id => $block_values) {
+        $variant_plugin->updateBlock($block_id, $block_values);
+      }
+    }
+
+    // Remove from the tempstore so we refresh from the database the next time
+    // we come here.
+    $this->getTempstore()->delete($variant_plugin->id());
+  }
+
+}
diff --git a/page_manager_ui/src/Form/VariantPluginDeleteBlockForm.php b/page_manager_ui/src/Form/VariantPluginDeleteBlockForm.php
index 02a013e..b451074 100644
--- a/page_manager_ui/src/Form/VariantPluginDeleteBlockForm.php
+++ b/page_manager_ui/src/Form/VariantPluginDeleteBlockForm.php
@@ -17,11 +17,9 @@ use Drupal\page_manager\PageVariantInterface;
 class VariantPluginDeleteBlockForm extends ConfirmFormBase {
 
   /**
-   * The page variant.
-   *
-   * @var \Drupal\page_manager\PageVariantInterface
+   * @var \Drupal\ctools\Plugin\BlockVariantInterface
    */
-  protected $pageVariant;
+  protected $plugin;
 
   /**
    * The plugin being configured.
@@ -31,6 +29,24 @@ class VariantPluginDeleteBlockForm extends ConfirmFormBase {
   protected $block;
 
   /**
+   * Get the tempstore id.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.block_display';
+  }
+
+  /**
+   * Get the tempstore.
+   *
+   * @return \Drupal\user\SharedTempStore
+   */
+  protected function getTempstore() {
+    return \Drupal::service('user.shared_tempstore')->get($this->getTempstoreId());
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getFormId() {
@@ -48,7 +64,7 @@ class VariantPluginDeleteBlockForm extends ConfirmFormBase {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->pageVariant->toUrl('edit-form');
+    return \Drupal::request()->attributes->get('destination');
   }
 
   /**
@@ -61,9 +77,13 @@ class VariantPluginDeleteBlockForm extends ConfirmFormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $block_id = NULL) {
-    $this->pageVariant = $page_variant;
-    $this->block = $this->getVariantPlugin()->getBlock($block_id);
+  public function buildForm(array $form, FormStateInterface $form_state, $block_display = NULL, $block_id = NULL) {
+    $this->plugin = $this->getTempstore()->get($block_display)['plugin'];
+    $this->block = $this->plugin->getBlock($block_id);
+    $form['block_display'] = [
+      '#type' => 'value',
+      '#value' => $block_display
+    ];
     return parent::buildForm($form, $form_state);
   }
 
@@ -71,20 +91,11 @@ class VariantPluginDeleteBlockForm extends ConfirmFormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $this->getVariantPlugin()->removeBlock($this->block->getConfiguration()['uuid']);
-    $this->pageVariant->save();
+    $this->plugin->removeBlock($this->block->getConfiguration()['uuid']);
+    $cached_values = $this->getTempstore()->get($form_state->getValue('block_display'));
+    $cached_values['plugin'] = $this->plugin;
+    $this->getTempstore()->set($form_state->getValue('block_display'), $cached_values);
     drupal_set_message($this->t('The block %label has been removed.', ['%label' => $this->block->label()]));
-
-    $form_state->setRedirectUrl($this->getCancelUrl());
-  }
-
-  /**
-   * Gets the variant plugin for this page variant entity.
-   *
-   * @return \Drupal\ctools\Plugin\BlockVariantInterface
-   */
-  protected function getVariantPlugin() {
-    return $this->pageVariant->getVariantPlugin();
   }
 
 }
diff --git a/page_manager_ui/src/Tests/PageManagerAdminTest.php b/page_manager_ui/src/Tests/PageManagerAdminTest.php
index 9811fd6..3fc0d42 100644
--- a/page_manager_ui/src/Tests/PageManagerAdminTest.php
+++ b/page_manager_ui/src/Tests/PageManagerAdminTest.php
@@ -50,12 +50,13 @@ class PageManagerAdminTest extends WebTestBase {
    */
   public function testAdmin() {
     $this->doTestAddPage();
+    $this->doTestAccessConditions();
+    $this->doTestSelectionCriteria();
     $this->doTestDisablePage();
     $this->doTestAddVariant();
     $this->doTestAddBlock();
     $this->doTestSecondPage();
     $this->doTestEditBlock();
-    $this->doTestAlterBlock();
     $this->doTestEditVariant();
     $this->doTestReorderVariants();
     $this->doTestAddPageWithDuplicatePath();
@@ -81,47 +82,155 @@ class PageManagerAdminTest extends WebTestBase {
     $edit = [
       'id' => 'foo',
       'path' => 'admin/foo',
+      'variant_plugin_id' => 'http_status_code',
+      'use_admin_theme' => TRUE,
+      'description' => 'This is our first test page.',
+      // Go through all available steps (we skip them all in doTestSecondPage())
+      'wizard_options[access]' => TRUE,
+      'wizard_options[selection]' => TRUE,
     ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
-    $this->assertText('Label field is required.');
+    $this->drupalPostForm(NULL, $edit, 'Next');
+    $this->assertText('Administrative title field is required.');
 
     // Add a new page with a label.
     $edit += ['label' => 'Foo'];
-    $this->drupalPostForm(NULL, $edit, 'Save');
-    $this->assertRaw(new FormattableMarkup('The %label page has been added.', ['%label' => 'Foo']));
-
-    // Assert that no variant was added by default.
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->assertText('There are no variants.');
-
-    // Test that it is available immediately.
-    $this->drupalGet('admin/foo');
-    $this->assertResponse(404);
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Add new variant');
-    $this->clickLink('HTTP status code');
+    $this->drupalPostForm(NULL, $edit, 'Next');
+
+    // Test the 'Page access' step.
+    $this->assertTitle('Page access | Drupal');
+    $access_path = 'admin/structure/page_manager/add/foo/access';
+    $this->assertUrl($access_path . '?js=nojs');
+    $this->doTestAccessConditions($access_path, FALSE);
+    $this->drupalPostForm(NULL, [], 'Next');
+
+    // Test the 'Selection criteria' step.
+    $this->assertTitle('Selection criteria | Drupal');
+    $selection_path = 'admin/structure/page_manager/add/foo/selection';
+    $this->assertUrl($selection_path . '?js=nojs');
+    $this->doTestSelectionCriteria($selection_path, FALSE);
+    $this->drupalPostForm(NULL, [], 'Next');
+
+    // Configure the variant.
     $edit = [
-      'id' => 'http_status_code',
-      'label' => 'Status Code',
+      'page_variant_label' => 'Status Code',
       'variant_settings[status_code]' => 200,
     ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
-
-    // There is a variant now, so the empty text is no longer visible.
-    $this->assertNoText('There are no variants.');
+    $this->drupalPostForm(NULL, $edit, 'Finish');
+    $this->assertRaw(new FormattableMarkup('Saved the %label Page.', ['%label' => 'Foo']));
+    // We've gone from the add wizard to the edit wizard.
+    $this->drupalGet('admin/structure/page_manager/manage/foo/general');
 
     $this->drupalGet('admin/foo');
     $this->assertResponse(200);
     $this->assertTitle('Foo | Drupal');
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Edit');
-    $this->drupalPostForm(NULL, ['variant_settings[status_code]' => 403], 'Save');
+
+    // Change the status code to 403.
+    $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-http_status_code-0__general');
+    $edit = [
+      'variant_settings[status_code]' => 403,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Update');
 
     // Set the weight of the 'Status Code' variant to 10.
+    $this->drupalGet('admin/structure/page_manager/manage/foo/reorder_variants');
+    $edit = [
+      'variants[foo-http_status_code-0][weight]' => 10,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Update');
+    $this->drupalPostForm(NULL, [], 'Update and save');
+  }
+
+  /**
+   * Tests access conditions step on both add and edit wizard.
+   *
+   * @param string $path
+   *   The path this step is supposed to be at.
+   * @param bool|TRUE $redirect
+   *   Whether or not to redirect to the path.
+   */
+  protected function doTestAccessConditions($path = 'admin/structure/page_manager/manage/foo/access', $redirect = TRUE) {
+    if ($this->getUrl() !== $path && $redirect) {
+      $this->drupalGet($path);
+    }
+
+    $this->assertRaw('No required conditions have been configured.');
+
+    // Configure a new condition.
+    $edit = [
+      'conditions' => 'user_role',
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Add Condition');
+    $this->assertTitle('Add access condition | Drupal');
+    $edit = [
+      'roles[authenticated]' => TRUE,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertRaw('The user is a member of Authenticated user');
+    // Make sure we're still on the same wizard.
+    $this->assertUrl($path);
+
+    // Edit the condition.
+    $this->clickLink('Edit');
+    $this->assertTitle('Edit access condition | Drupal');
     $edit = [
-      'variants[http_status_code][weight]' => 10,
+      'roles[anonymous]' => TRUE,
     ];
     $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertRaw('The user is a member of Anonymous user, Authenticated user');
+    $this->assertUrl($path);
+
+    // Delete the condition.
+    $this->clickLink('Delete');
+    $this->assertTitle('Are you sure you want to delete the user_role condition? | Drupal');
+    $this->drupalPostForm(NULL, [], 'Delete');
+    $this->assertRaw('No required conditions have been configured.');
+    $this->assertUrl($path);
+  }
+
+  /**
+   * Tests selection criteria step on both add and edit wizard.
+   *
+   * @param string $path
+   *   The path this step is supposed to be at.
+   * @param bool|TRUE $redirect
+   *   Whether or not to redirect to the path.
+   */
+  protected function doTestSelectionCriteria($path = 'admin/structure/page_manager/manage/foo/page_variant__foo-http_status_code-0__selection', $redirect = TRUE) {
+    if ($this->getUrl() !== $path && $redirect) {
+      $this->drupalGet($path);
+    }
+    $this->assertRaw('No required conditions have been configured.');
+
+    // Configure a new condition.
+    $edit = [
+      'conditions' => 'user_role',
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Add Condition');
+    $this->assertTitle('Add new selection condition | Drupal');
+    $edit = [
+      'roles[authenticated]' => TRUE,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertRaw('The user is a member of Authenticated user');
+    // Make sure we're still on the add wizard (not the edit wizard).
+    $this->assertUrl($path);
+
+    // Edit the condition.
+    $this->clickLink('Edit');
+    $this->assertTitle('Edit selection condition | Drupal');
+    $edit = [
+      'roles[anonymous]' => TRUE,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertRaw('The user is a member of Anonymous user, Authenticated user');
+    $this->assertUrl($path);
+
+    // Delete the condition.
+    $this->clickLink('Delete');
+    $this->assertTitle('Are you sure you want to delete the user_role condition? | Drupal');
+    $this->drupalPostForm(NULL, [], 'Delete');
+    $this->assertRaw('No required conditions have been configured.');
+    $this->assertUrl($path);
   }
 
   /**
@@ -148,20 +257,27 @@ class PageManagerAdminTest extends WebTestBase {
    * Tests adding a variant.
    */
   protected function doTestAddVariant() {
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
+    $this->drupalGet('admin/structure/page_manager/manage/foo/general');
 
     // Add a new variant.
-    $this->clickLink('Add new variant');
-    $this->assertNoText('Page with blocks');
-    $this->assertNoText('Simple page');
-    $this->clickLink('Block page');
+    $this->clickLink('Add variant');
     $edit = [
+      'variant_plugin_id' => 'block_display',
       'label' => 'First',
-      'id' => 'block_page',
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Next');
+
+    // Set the page title.
+    $edit = [
       'variant_settings[page_title]' => 'Example title',
     ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
-    $this->assertRaw(new FormattableMarkup('Saved the %label variant.', ['%label' => 'First']));
+    $this->drupalPostForm(NULL, $edit, 'Next');
+
+    // Finish variant wizard without adding blocks.
+    $this->drupalPostForm(NULL, [], 'Finish');
+
+    // Save page to apply variant changes.
+    $this->drupalPostForm(NULL, [], 'Update and save');
 
     // Test that the variant is still used but empty.
     $this->drupalGet('admin/foo');
@@ -175,8 +291,7 @@ class PageManagerAdminTest extends WebTestBase {
    * Tests adding a block to a variant.
    */
   protected function doTestAddBlock() {
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Edit');
+    $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content');
     // Add a block to the variant.
     $this->clickLink('Add new block');
 
@@ -188,6 +303,7 @@ class PageManagerAdminTest extends WebTestBase {
       'region' => 'top',
     ];
     $this->drupalPostForm(NULL, $edit, 'Add block');
+    $this->drupalPostForm(NULL, [], 'Update and save');
 
     // Test that the block is displayed.
     $this->drupalGet('admin/foo');
@@ -216,20 +332,20 @@ class PageManagerAdminTest extends WebTestBase {
       'id' => 'second',
       'label' => 'Second',
       'path' => 'second',
+      'variant_plugin_id' => 'block_display',
     ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
-    $this->assertRaw(new FormattableMarkup('The %label page has been added.', ['%label' => 'Second']));
+    $this->drupalPostForm(NULL, $edit, 'Next');
 
-    // Add a variant.
-    $this->clickLink('Add new variant');
-    $this->clickLink('Block page');
+    // Configure the variant.
     $edit = [
-      'label' => 'Second variant',
-      'id' => 'second_block_page',
+      'page_variant_label' => 'Second variant',
       'variant_settings[page_title]' => 'Second title',
     ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
-    $this->assertRaw(new FormattableMarkup('Saved the %label variant.', ['%label' => 'Second variant']));
+    $this->drupalPostForm(NULL, $edit, 'Next');
+
+    // We're now on the content step, but we don't need to add any blocks.
+    $this->drupalPostForm(NULL, [], 'Finish');
+    $this->assertRaw(new FormattableMarkup('Saved the %label Page.', ['%label' => 'Second']));
 
     // Visit both pages, make sure that they do not interfere with each other.
     $this->drupalGet('admin/foo');
@@ -242,78 +358,43 @@ class PageManagerAdminTest extends WebTestBase {
    * Tests editing a block.
    */
   protected function doTestEditBlock() {
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Edit');
-    $this->clickLink('Edit');
+    $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__general');
     $edit = [
-      'settings[label]' => 'Updated block label',
+      'variant_settings[page_title]' => 'Updated block label',
+      'page_variant_label' => 'Updated block label',
     ];
-    $this->drupalPostForm(NULL, $edit, 'Update block');
+    $this->drupalPostForm(NULL, $edit, 'Update and save');
     // Test that the block is displayed.
     $this->drupalGet('admin/foo');
     $this->assertResponse(200);
     // Check the block label.
-    $this->assertRaw($edit['settings[label]']);
-  }
-
-  /**
-   * Tests altering a block.
-   */
-  protected function doTestAlterBlock() {
-    // Alter the block label.
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Edit');
-    $this->clickLink('Edit');
-    $edit = [
-      'settings[label]' => 'Label to be altered',
-    ];
-    $this->drupalPostForm(NULL, $edit, 'Update block');
-    $this->drupalGet('admin/foo');
-    $this->assertResponse(200);
-    // Check if the block label is altered correctly.
-    $this->assertRaw('Altered label');
-
-    // Re-set the previous block label.
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Edit');
-    $this->clickLink('Edit');
-    $edit = [
-      'settings[label]' => 'Updated block label',
-    ];
-    $this->drupalPostForm(NULL, $edit, 'Update block');
-    $this->drupalGet('admin/foo');
-    $this->assertResponse(200);
-    // Check the block label.
-    $this->assertRaw($edit['settings[label]']);
+    $this->assertRaw($edit['variant_settings[page_title]']);
   }
 
   /**
    * Tests editing a variant.
    */
   protected function doTestEditVariant() {
-    if (!$block = $this->findBlockByLabel('block_page', 'Updated block label')) {
+    if (!$block = $this->findBlockByLabel('foo-block_display-0', 'User account menu')) {
       $this->fail('Block not found');
       return;
     }
 
     $block_config = $block->getConfiguration();
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Edit');
-    $this->assertTitle('Edit First variant | Drupal');
+    $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content');
 
-    $this->assertOptionSelected('edit-variant-plugin-blocks-' . $block_config['uuid'] . '-region', 'top');
-    $this->assertOptionSelected('edit-variant-plugin-blocks-' . $block_config['uuid'] . '-weight', 0);
+    $this->assertOptionSelected('edit-blocks-' . $block_config['uuid'] . '-region', 'top');
+    $this->assertOptionSelected('edit-blocks-' . $block_config['uuid'] . '-weight', 0);
 
-    $form_name = 'variant_plugin[blocks][' . $block_config['uuid'] . ']';
+    $form_name = 'blocks[' . $block_config['uuid'] . ']';
     $edit = [
       $form_name . '[region]' => 'bottom',
       $form_name . '[weight]' => -10,
     ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
-    $this->assertRaw(new FormattableMarkup('Saved the %label variant.', ['%label' => 'First']));
-    $this->clickLink('Edit');
-    $this->assertOptionSelected('edit-variant-plugin-blocks-' . $block_config['uuid'] . '-region', 'bottom');
-    $this->assertOptionSelected('edit-variant-plugin-blocks-' . $block_config['uuid'] . '-weight', -10);
+    $this->drupalPostForm(NULL, $edit, 'Update');
+    $this->assertOptionSelected('edit-blocks-' . $block_config['uuid'] . '-region', 'bottom');
+    $this->assertOptionSelected('edit-blocks-' . $block_config['uuid'] . '-weight', -10);
+    $this->drupalPostForm(NULL, [], 'Update and save');
   }
 
   /**
@@ -330,10 +411,14 @@ class PageManagerAdminTest extends WebTestBase {
     }
     $this->assertEqual($expected, $links);
 
+    $this->drupalGet('admin/structure/page_manager/manage/foo/general');
+    $this->clickLink('Reorder variants');
+
     $edit = [
-      'variants[http_status_code][weight]' => -10,
+      'variants[foo-http_status_code-0][weight]' => -10,
     ];
-    $this->drupalPostForm('admin/structure/page_manager/manage/foo', $edit, 'Save');
+    $this->drupalPostForm(NULL, $edit, 'Update');
+    $this->drupalPostForm(NULL, [], 'Update and save');
     $this->drupalGet('admin/foo');
     $this->assertResponse(403);
   }
@@ -348,7 +433,7 @@ class PageManagerAdminTest extends WebTestBase {
       'id' => 'bar',
       'path' => 'admin/foo',
     ];
-    $this->drupalPostForm('admin/structure/page_manager/add', $edit, 'Save');
+    $this->drupalPostForm('admin/structure/page_manager/add', $edit, 'Next');
     $this->assertText('The page path must be unique.');
     $this->drupalGet('admin/structure/page_manager');
     $this->assertNoText('Bar');
@@ -362,10 +447,11 @@ class PageManagerAdminTest extends WebTestBase {
     $this->drupalGet('admin/foo');
     $this->assertTheme('classy');
 
+    $this->drupalGet('admin/structure/page_manager/manage/foo/general');
     $edit = [
       'use_admin_theme' => FALSE,
     ];
-    $this->drupalPostForm('admin/structure/page_manager/manage/foo', $edit, 'Save');
+    $this->drupalPostForm(NULL, $edit, 'Update and save');
     $this->drupalGet('admin/foo');
     $this->assertTheme('bartik');
 
@@ -377,11 +463,12 @@ class PageManagerAdminTest extends WebTestBase {
    * Tests removing a variant.
    */
   protected function doTestRemoveVariant() {
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Delete');
-    $this->assertRaw(new FormattableMarkup('Are you sure you want to delete %label?', ['%label' => 'Status Code']));
+    $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-http_status_code-0__general');
+    $this->clickLink('Delete this variant');
+    $this->assertRaw('Are you sure you want to delete this variant?');
     $this->drupalPostForm(NULL, [], 'Delete');
     $this->assertRaw(new FormattableMarkup('The variant %label has been removed.', ['%label' => 'Status Code']));
+    $this->drupalPostForm(NULL, [], 'Update and save');
   }
 
   /**
@@ -399,12 +486,12 @@ class PageManagerAdminTest extends WebTestBase {
     }
     $this->assertEqual($expected, $links);
 
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Edit');
+    $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content');
     $this->clickLink('Delete');
-    $this->assertRaw(new FormattableMarkup('Are you sure you want to delete the block %label?', ['%label' => 'Updated block label']));
+    $this->assertRaw(new FormattableMarkup('Are you sure you want to delete the block %label?', ['%label' => 'User account menu']));
     $this->drupalPostForm(NULL, [], 'Delete');
-    $this->assertRaw(new FormattableMarkup('The block %label has been removed.', ['%label' => 'Updated block label']));
+    $this->assertRaw(new FormattableMarkup('The block %label has been removed.', ['%label' => 'User account menu']));
+    $this->drupalPostForm(NULL, [], 'Update and save');
 
     // Assert that the block is now gone.
     $this->drupalGet('admin/foo');
@@ -417,8 +504,7 @@ class PageManagerAdminTest extends WebTestBase {
    * Tests adding a block with #ajax to a variant.
    */
   protected function doTestAddBlockWithAjax() {
-    $this->drupalGet('admin/structure/page_manager/manage/foo');
-    $this->clickLink('Edit');
+    $this->drupalGet('admin/structure/page_manager/manage/foo/page_variant__foo-block_display-0__content');
     // Add a block to the variant.
     $this->clickLink('Add new block');
     $this->clickLink('Page Manager Test Block');
@@ -426,6 +512,7 @@ class PageManagerAdminTest extends WebTestBase {
       'region' => 'top',
     ];
     $this->drupalPostForm(NULL, $edit, 'Add block');
+    $this->drupalPostForm(NULL, [], 'Update and save');
 
     // Test that the block is displayed.
     $this->drupalGet('admin/foo');
@@ -450,20 +537,16 @@ class PageManagerAdminTest extends WebTestBase {
       'label' => 'existing',
       'id' => 'existing',
       'path' => 'admin',
+      'variant_plugin_id' => 'http_status_code',
     ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
-
-    // Regular result is displayed.
-    $this->assertText('The existing page has been added');
+    $this->drupalPostForm(NULL, $edit, 'Next');
 
-    $this->clickLink('Add new variant');
-    $this->clickLink('HTTP status code');
+    // Configure the variant.
     $edit = [
-      'id' => 'http_status_code',
-      'label' => 'Status Code',
+      'page_variant_label' => 'Status Code',
       'variant_settings[status_code]' => 404,
     ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->drupalPostForm(NULL, $edit, 'Finish');
 
     // Ensure the existing path leads to the new page.
     $this->drupalGet('admin');
@@ -496,54 +579,34 @@ class PageManagerAdminTest extends WebTestBase {
    */
   public function testExistingRoutes() {
     // Test that the page without placeholder is accessible.
+    $this->drupalGet('admin/structure/page_manager/add');
     $edit = [
       'label' => 'Placeholder test 2',
       'id' => 'placeholder2',
       'path' => '/page-manager-test',
+      'variant_plugin_id' => 'http_status_code',
     ];
-    $this->drupalPostForm('admin/structure/page_manager/add', $edit, 'Save');
-    $this->drupalGet('page-manager-test');
-    // Without a single variant, it will fall through to the original.
-    $this->assertResponse(200);
+    $this->drupalPostForm(NULL, $edit, 'Next');
 
-    $this->drupalGet('admin/structure/page_manager/manage/placeholder2');
-    $this->clickLink('Add new variant');
-    $this->clickLink('HTTP status code');
     $edit = [
-      'id' => 'http_status_code',
-      'label' => 'Status Code',
-      'variant_settings[status_code]' => 404,
+      'variant_settings[status_code]' => 418,
     ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->drupalPostForm(NULL, $edit, 'Finish');
     $this->drupalGet('page-manager-test');
-    $this->assertResponse(404);
+    $this->assertResponse(418);
 
     // Test that the page test is accessible.
     $page_string = 'test-page';
     $this->drupalGet('page-manager-test/' . $page_string);
     $this->assertResponse(200);
-  }
 
-  /**
-   * Tests the parameters UI.
-   */
-  public function testParameters() {
-    $this->drupalGet('admin/structure/page_manager');
-    $this->clickLink('Add page');
-    $edit = [
-      'id' => 'foo',
-      'label' => 'Foo',
-      'path' => '/foo/{user}',
-    ];
-    $this->drupalPostForm(NULL, $edit, 'Save');
-    $this->assertText('No context assigned');
-
-    $this->clickLink('Edit');
-    $this->drupalPostForm(NULL, ['type' => 'entity:user'], 'Update parameter');
-    $this->assertNoText('No context assigned');
-    $this->assertText('entity:user');
-    $parameters = Page::load('foo')->getParameters();
-    $this->assertIdentical(['user' => ['machine_name' => 'user', 'type' => 'entity:user', 'label' => 'User']], $parameters);
+    // Without a single variant, it will fall through to the original.
+    $this->drupalGet('admin/structure/page_manager/manage/placeholder2/page_variant__placeholder2-http_status_code-0__general');
+    $this->clickLink('Delete this variant');
+    $this->drupalPostForm(NULL, [], 'Delete');
+    $this->drupalPostForm(NULL, [], 'Update and save');
+    $this->drupalGet('page-manager-test');
+    $this->assertResponse(200);
   }
 
   /**
@@ -570,6 +633,7 @@ class PageManagerAdminTest extends WebTestBase {
    *   Either a block plugin, or NULL.
    */
   protected function findBlockByLabel($page_variant_id, $block_label) {
+    /** @var \Drupal\page_manager\Entity\PageVariant $page_variant */
     if ($page_variant = PageVariant::load($page_variant_id)) {
       /** @var \Drupal\ctools\Plugin\BlockVariantInterface $variant_plugin */
       $variant_plugin = $page_variant->getVariantPlugin();
diff --git a/page_manager_ui/src/Wizard/PageAddWizard.php b/page_manager_ui/src/Wizard/PageAddWizard.php
new file mode 100644
index 0000000..28c6a33
--- /dev/null
+++ b/page_manager_ui/src/Wizard/PageAddWizard.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Wizard\PageAddWizard.
+ */
+
+namespace Drupal\page_manager_ui\Wizard;
+
+use Drupal\Core\Display\ContextAwareVariantInterface;
+use Drupal\ctools\Plugin\PluginWizardInterface;
+
+class PageAddWizard extends PageWizardBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteName() {
+    return 'entity.page.add_step_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations($cached_values) {
+    $operations = parent::getOperations($cached_values);
+
+    // Add steps for selection and creating the first variant.
+    $operations['contexts'] = [
+      'title' => $this->t('Contexts'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantContextsForm',
+    ];
+    $operations['selection'] = [
+      'title' => $this->t('Selection criteria'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantSelectionForm',
+    ];
+    $operations['display_variant'] = [
+      'title' => $this->t('Configure variant'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantConfigureForm',
+    ];
+
+    // Hide any optional steps that aren't selected.
+    $optional_steps = ['parameters', 'access', 'contexts', 'selection'];
+    foreach ($optional_steps as $step_name) {
+      if (empty($cached_values['wizard_options'][$step_name])) {
+        unset($operations[$step_name]);
+      }
+    }
+
+    // Add any wizard operations from the plugin itself.
+    if (!empty($cached_values['page_variant'])) {
+      /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
+      $page_variant = $cached_values['page_variant'];
+      $variant_plugin = $page_variant->getVariantPlugin();
+      if ($variant_plugin instanceof PluginWizardInterface) {
+        if ($variant_plugin instanceof ContextAwareVariantInterface) {
+          $variant_plugin->setContexts($page_variant->getContexts());
+        }
+        $cached_values['plugin'] = $variant_plugin;
+        foreach ($variant_plugin->getWizardOperations($cached_values) as $name => $operation) {
+          $operation['values']['plugin'] = $variant_plugin;
+          $operation['submit'][] = '::submitVariantStep';
+          $operations[$name] = $operation;
+        }
+      }
+    }
+
+    return $operations;
+  }
+
+}
diff --git a/page_manager_ui/src/Wizard/PageEditWizard.php b/page_manager_ui/src/Wizard/PageEditWizard.php
new file mode 100644
index 0000000..6b4160d
--- /dev/null
+++ b/page_manager_ui/src/Wizard/PageEditWizard.php
@@ -0,0 +1,299 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Wizard\PageEditWizard.
+ */
+
+namespace Drupal\page_manager_ui\Wizard;
+
+use Drupal\Core\Display\ContextAwareVariantInterface;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\ctools\Plugin\PluginWizardInterface;
+use Drupal\page_manager\PageInterface;
+use Drupal\page_manager\PageVariantInterface;
+
+class PageEditWizard extends PageWizardBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations($cached_values) {
+    $operations = parent::getOperations($cached_values);
+
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+
+    if (!empty($page)) {
+      // Get variants and re-sort by weight or remove variants if the user
+      // has edited the variant.
+      $variants = $page->getVariants();
+      if (!empty($cached_values['deleted_variants'])) {
+        foreach ($cached_values['deleted_variants'] as $page_variant) {
+          unset($variants[$page_variant->id()]);
+        }
+      }
+      // Suppress errors because of https://bugs.php.net/bug.php?id=50688.
+      @uasort($variants, '\Drupal\page_manager\Entity\PageVariant::sort');
+
+      foreach ($variants as $page_variant) {
+        foreach ($this->getVariantOperations($page_variant, $cached_values) as $name => $operation) {
+          $operation['values']['page_variant'] = $page_variant;
+          $operation['breadcrumbs'] = [
+            $this->t('Variants'),
+            $page_variant->label() ?: $this->t('Variant'),
+          ];
+          $operations['page_variant__' . $page_variant->id() . '__' . $name] = $operation;
+        }
+      }
+    }
+
+    return $operations;
+  }
+
+  /**
+   * Get operations for the variant.
+   *
+   * @param \Drupal\page_manager\PageVariantInterface $page_variant
+   *   The page variant entity.
+   * @param mixed $cached_values
+   *   The cached values.
+   *
+   * @returns array
+   */
+  protected function getVariantOperations(PageVariantInterface $page_variant, $cached_values) {
+    $operations = [];
+    $operations['general'] = [
+      'title' => $this->t('General'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantConfigureForm',
+    ];
+    $operations['contexts'] = [
+      'title' => $this->t('Contexts'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantContextsForm',
+    ];
+    $operations['selection'] = [
+      'title' => $this->t('Selection criteria'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantSelectionForm',
+    ];
+
+    // Add any wizard operations from the plugin itself.
+    $variant_plugin = $page_variant->getVariantPlugin();
+    if ($variant_plugin instanceof PluginWizardInterface) {
+      if ($variant_plugin instanceof ContextAwareVariantInterface) {
+        $variant_plugin->setContexts($page_variant->getContexts());
+      }
+      $cached_values['plugin'] = $variant_plugin;
+      foreach ($variant_plugin->getWizardOperations($cached_values) as $name => $operation) {
+        $operation['values']['plugin'] = $variant_plugin;
+        $operation['submit'][] = '::submitVariantStep';
+        $operations[$name] = $operation;
+      }
+    }
+
+    return $operations;
+  }
+
+  /**
+   * Get action links for the page.
+   *
+   * @return array
+   *   An array of associative arrays with the following keys:
+   *   - title: The link text
+   *   - url: A URL object
+   */
+  protected function getPageActionLinks(PageInterface $page) {
+    $links = [];
+
+    $links[] = [
+      'title' => $this->t('Delete page'),
+      'url' => new Url('entity.page.delete_form', [
+        'page' => $this->getMachineName(),
+      ]),
+    ];
+
+    $links[] = [
+      'title' => $this->t('Add variant'),
+      'url' => new Url('entity.page_variant.add_form', [
+        'page' => $this->getMachineName(),
+      ]),
+    ];
+
+    $links[] = [
+      'title' => $this->t('Reorder variants'),
+      'url' => new Url('entity.page.reorder_variants_form', [
+        'machine_name' => $this->getMachineName(),
+      ]),
+    ];
+
+    return $links;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function customizeForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+
+    // The page actions.
+    $form['wizard_actions'] = [
+      '#theme' => 'links',
+      '#links' => [],
+      '#attributes' => [
+        'class' => ['inline'],
+      ]
+    ];
+    foreach ($this->getPageActionLinks($page) as $action) {
+      $form['wizard_actions']['#links'][] = $action + [
+        'attributes' => [
+          'class' => 'use-ajax',
+          'data-dialog-type' => 'modal',
+        ],
+      ];
+    }
+
+    // The tree of wizard steps.
+    $form['wizard_tree'] = [
+      '#theme' => ['page_manager_wizard_tree'],
+      '#wizard' => $this,
+      '#cached_values' => $form_state->getTemporaryValue('wizard'),
+    ];
+
+    $form['#theme'] = 'page_manager_wizard_form';
+    $form['#attached']['library'][] = 'page_manager_ui/admin';
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function actions(FormInterface $form_object, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $operation = $this->getOperation($cached_values);
+
+    $actions = [];
+
+    $actions['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Update'),
+      '#validate' => [
+        '::populateCachedValues',
+        [$form_object, 'validateForm'],
+      ],
+      '#submit' => [
+        [$form_object, 'submitForm'],
+      ],
+    ];
+
+    $actions['update_and_save'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Update and save'),
+      '#button_type' => 'primary',
+      '#validate' => [
+        '::populateCachedValues',
+        [$form_object, 'validateForm'],
+      ],
+      '#submit' => [
+        [$form_object, 'submitForm'],
+      ],
+    ];
+
+    $actions['finish'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Save'),
+      '#validate' => [
+        '::populateCachedValues',
+        [$form_object, 'validateForm'],
+      ],
+      '#submit' => [
+        [$form_object, 'submitForm'],
+      ],
+    ];
+
+    $actions['cancel'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Cancel'),
+      '#submit' => [
+        '::clearTempstore'
+      ],
+    ];
+
+    // Add any submit or validate functions for the step and the global ones.
+    foreach (['submit', 'update_and_save', 'finish'] as $button) {
+      if (isset($operation['validate'])) {
+        $actions[$button]['#validate'] = array_merge($actions[$button]['#validate'], $operation['validate']);
+      }
+      $actions[$button]['#validate'][] = '::validateForm';
+      if (isset($operation['submit'])) {
+        $actions[$button]['#submit'] = array_merge($actions[$button]['#submit'], $operation['submit']);
+      }
+      $actions[$button]['#submit'][] = '::submitForm';
+    }
+    $actions['update_and_save']['#submit'][] = '::finish';
+    $actions['finish']['#submit'][] = '::finish';
+
+    if ($form_state->get('ajax')) {
+      $cached_values = $form_state->getTemporaryValue('wizard');
+      $ajax_parameters = $this->getNextParameters($cached_values);
+      $ajax_parameters['step'] = $this->getStep($cached_values);
+      $actions['submit']['#ajax'] = [
+        'callback' => '::ajaxSubmit',
+        'url' => Url::fromRoute($this->getRouteName(), $ajax_parameters),
+        'options' => ['query' => \Drupal::request()->query->all() + [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]],
+      ];
+      $actions['update_and_save']['#ajax'] = [
+        'callback' => '::ajaxFinish',
+        'url' => Url::fromRoute($this->getRouteName(), $ajax_parameters),
+        'options' => ['query' => \Drupal::request()->query->all() + [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]],
+      ];
+      $actions['finish']['#ajax'] = [
+        'callback' => '::ajaxFinish',
+        'url' => Url::fromRoute($this->getRouteName(), $ajax_parameters),
+        'options' => ['query' => \Drupal::request()->query->all() + [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]],
+      ];
+    }
+
+    return $actions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    // Normally, the wizard only saves the data when the 'Next' button is
+    // clicked, but we want to save the data always when editing.
+    $this->getTempstore()->set($this->getMachineName(), $cached_values);
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function finish(array &$form, FormStateInterface $form_state) {
+    // Delete any of the variants marked for deletion.
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\Entity\Page $page */
+    $page = $cached_values['page'];
+    if (!empty($cached_values['deleted_variants'])) {
+      foreach ($cached_values['deleted_variants'] as $page_variant) {
+        $page->removeVariant($page_variant->id());
+      }
+    }
+
+    parent::finish($form, $form_state);
+  }
+
+  /**
+   * Clears the temporary store.
+   *
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   */
+  public function clearTempstore(array &$form, FormStateInterface $form_state) {
+    $this->getTempstore()->delete($this->getMachineName());
+  }
+
+}
diff --git a/page_manager_ui/src/Wizard/PageVariantAddWizard.php b/page_manager_ui/src/Wizard/PageVariantAddWizard.php
new file mode 100644
index 0000000..ba87ea5
--- /dev/null
+++ b/page_manager_ui/src/Wizard/PageVariantAddWizard.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Wizard\PageVariantAddWizard.
+ */
+
+namespace Drupal\page_manager_ui\Wizard;
+
+use Drupal\Core\Display\ContextAwareVariantInterface;
+use Drupal\ctools\Plugin\PluginWizardInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\page_manager\Access\PageManagerPluginAccess;
+use Drupal\ctools\Wizard\EntityFormWizardBase;
+
+class PageVariantAddWizard extends EntityFormWizardBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEntityType() {
+    return 'page_variant';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function exists() {
+    return '\Drupal\page_manager\Entity\PageVariant::load';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getWizardLabel() {
+    return $this->t('Page Variant');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMachineLabel() {
+    return $this->t('Label');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteName() {
+    return 'entity.page_variant.add_step_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function initValues() {
+    $cached_values = parent::initValues();
+    $cached_values['access'] = new PageManagerPluginAccess();
+    return $cached_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations($cached_values) {
+    $operations = [];
+    $operations['type'] = [
+      'title' => $this->t('Page variant type'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantAddForm',
+    ];
+    $operations['contexts'] = [
+      'title' => $this->t('Contexts'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantContextsForm',
+    ];
+    $operations['selection'] = [
+      'title' => $this->t('Selection criteria'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantSelectionForm',
+    ];
+    $operations['configure'] = [
+      'title' => $this->t('Configure variant'),
+      'form' => '\Drupal\page_manager_ui\Form\PageVariantConfigureForm',
+    ];
+
+    // Hide any optional steps that aren't selected.
+    $optional_steps = ['selection', 'contexts'];
+    foreach ($optional_steps as $step_name) {
+      if (isset($cached_values['wizard_options']) && empty($cached_values['wizard_options'][$step_name])) {
+        unset($operations[$step_name]);
+      }
+    }
+
+    // Add any wizard operations from the plugin itself.
+    if (!empty($cached_values['page_variant']) && !empty($cached_values['variant_plugin_id'])) {
+      /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
+      $page_variant = $cached_values['page_variant'];
+      $variant_plugin = $page_variant->getVariantPlugin();
+      if ($variant_plugin instanceof PluginWizardInterface) {
+        if ($variant_plugin instanceof ContextAwareVariantInterface) {
+          $variant_plugin->setContexts($page_variant->getContexts());
+        }
+        $cached_values['plugin'] = $variant_plugin;
+        foreach ($variant_plugin->getWizardOperations($cached_values) as $name => $operation) {
+          $operation['values']['plugin'] = $variant_plugin;
+          $operations[$name] = $operation;
+        }
+      }
+    }
+
+    return $operations;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function customizeForm(array $form, FormStateInterface $form_state) {
+    $form = parent::customizeForm($form, $form_state);
+
+    // We set the variant id as part of form submission.
+    if ($this->step == 'type' && isset($form['name']['id'])) {
+      unset($form['name']['id']);
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $page = NULL) {
+    $form = parent::buildForm($form, $form_state); // TODO: Change the autogenerated stub
+
+    // Get the page tempstore so we can modify the unsaved page.
+    if (!isset($cached_values['page']) || !$cached_values['page']->id()) {
+      $cached_values = $form_state->getTemporaryValue('wizard');
+      $page_tempstore = $this->tempstore->get('page_manager.page')->get($page);
+      $cached_values['page'] = $page_tempstore['page'];
+      $form_state->setTemporaryValue('wizard', $cached_values);
+    }
+
+    // Hide form elements that are not useful during the add wizard.
+    if ($this->step == 'configure') {
+      $form['page_variant_label']['#type'] = 'value';
+      unset($form['delete']);
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getNextParameters($cached_values) {
+    $parameters = parent::getNextParameters($cached_values);
+
+    // Add the page to the url parameters.
+    $parameters['page'] = $cached_values['page']->id();
+    return $parameters;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPreviousParameters($cached_values) {
+    $parameters = parent::getPreviousParameters($cached_values);
+
+    // Add the page to the url parameters.
+    $parameters['page'] = $cached_values['page']->id();
+    return $parameters;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function finish(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+
+    // Add the variant to the parent page tempstore.
+    $page_tempstore = $this->tempstore->get('page_manager.page')->get($cached_values['page']->id());
+    $page_tempstore['page']->addVariant($cached_values['page_variant']);
+    $this->tempstore->get('page_manager.page')->set($cached_values['page']->id(), $page_tempstore);
+
+    $variant_plugin = $cached_values['page_variant']->getVariantPlugin();
+    drupal_set_message($this->t('The %label @entity_type has been added to the page, but has not been saved. Please save the page to store changes.', array(
+      '%label' => $cached_values['page_variant']->label(),
+      '@entity_type' => $variant_plugin->adminLabel(),
+    )));
+
+    $form_state->setRedirectUrl(new Url('entity.page.edit_form', [
+      'machine_name' => $cached_values['page']->id(),
+      'step' => 'general',
+    ]));
+  }
+
+}
diff --git a/page_manager_ui/src/Wizard/PageWizardBase.php b/page_manager_ui/src/Wizard/PageWizardBase.php
new file mode 100644
index 0000000..ec7ffa6
--- /dev/null
+++ b/page_manager_ui/src/Wizard/PageWizardBase.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Wizard\PageWizardBase.
+ */
+
+namespace Drupal\page_manager_ui\Wizard;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\ctools\Wizard\EntityFormWizardBase;
+use Drupal\page_manager\Access\PageManagerPluginAccess;
+
+class PageWizardBase extends EntityFormWizardBase {
+
+  public function initValues() {
+    $cached_values = parent::initValues();
+    $cached_values['access'] = new PageManagerPluginAccess();
+    return $cached_values;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEntityType() {
+    return 'page';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function exists() {
+    return '\Drupal\page_manager\Entity\Page::load';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getWizardLabel() {
+    return $this->t('Page Manager');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMachineLabel() {
+    return $this->t('Administrative title');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations($cached_values) {
+    $operations = [];
+    $operations['general'] = [
+      'title' => $this->t('Page information'),
+      'form' => '\Drupal\page_manager_ui\Form\PageGeneralForm',
+    ];
+    $operations['parameters'] = [
+      'title' => $this->t('Page parameters'),
+      'form' => '\Drupal\page_manager_ui\Form\PageParametersForm',
+    ];
+    $operations['access'] = [
+      'title' => $this->t('Page access'),
+      'form' => '\Drupal\page_manager_ui\Form\PageAccessForm',
+    ];
+
+    return $operations;
+  }
+
+  /**
+   * Submission callback for the variant plugin steps.
+   */
+  public function submitVariantStep(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
+    $page_variant = $cached_values['page_variant'];
+    /** @var \Drupal\Core\Display\VariantInterface $plugin */
+    $plugin = $cached_values['plugin'];
+
+    // Make sure the variant plugin on the page variant gets the configuration
+    // from the 'plugin' which should have been setup by the variant's steps.
+    if (!empty($plugin) && !empty($page_variant)) {
+      $page_variant->getVariantPlugin()->setConfiguration($plugin->getConfiguration());
+    }
+  }
+
+  public function finish(array &$form, FormStateInterface $form_state) {
+    parent::finish($form, $form_state);
+
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\Entity\Page $page */
+    $page = $cached_values['page'];
+    foreach($page->getVariants() as $variant) {
+      $variant->save();
+    }
+
+    $form_state->setRedirectUrl(new Url('entity.page.edit_form', [
+      'machine_name' => $this->machine_name,
+      'step' => $this->step
+    ]));
+  }
+
+}
diff --git a/page_manager_ui/src/Wizard/RouteParameters.php b/page_manager_ui/src/Wizard/RouteParameters.php
new file mode 100644
index 0000000..e385c80
--- /dev/null
+++ b/page_manager_ui/src/Wizard/RouteParameters.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager_ui\Wizard\RouteParameters.
+ */
+
+namespace Drupal\page_manager_ui\Wizard;
+
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseModalDialogCommand;
+use Drupal\Core\Ajax\RedirectCommand;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Ajax\OpenModalWizardCommand;
+use Drupal\ctools\Wizard\FormWizardBase;
+
+class RouteParameters extends FormWizardBase {
+
+  /**
+   * The parameter to configure.
+   *
+   * @var string
+   */
+  protected $parameter;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations() {
+    return [
+      'assign' => [
+        'title' => $this->t('Assign Parameter Context'),
+        'form' => 'Drupal\page_manager_ui\Form\ParameterAssignContextForm',
+      ],
+      'settings' => [
+        'title' => $this->t('Parameter Settings'),
+        'form' => 'Drupal\page_manager_ui\Form\ParameterSettingsForm',
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteName() {
+    return 'page_manager.route.parameters.configure';
+  }
+
+  /**
+   * Override to get the parameter from the URL and make it available to steps.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $parameter = NULL) {
+    $this->parameter = $parameter;
+    return parent::buildForm($form, $form_state);
+  }
+
+  public function getNextParameters($cached_values) {
+    $parameters = parent::getNextParameters($cached_values);
+    $parameters['parameter'] = $this->parameter;
+    return $parameters;
+  }
+
+  public function getPreviousParameters($cached_values) {
+    $parameters = parent::getPreviousParameters($cached_values);
+    $parameters['parameter'] = $this->parameter;
+    return $parameters;
+  }
+
+  /**
+   * Save the values to the tempstore.
+   */
+  public function finish(array &$form, FormStateInterface $form_state) {
+    $this->getTempstore()->set($this->getMachineName(), $form_state->getTemporaryValue('wizard'));
+  }
+
+  public function ajaxFinish(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $response = new AjaxResponse();
+    $response->addCommand(new RedirectCommand($this->url('entity.page.edit_form', ['machine_name' => $cached_values['id'], 'step' => 'parameters'])));
+    $response->addCommand(new CloseModalDialogCommand());
+    return $response;
+  }
+
+}
diff --git a/page_manager_ui/templates/page-manager-wizard-form.html.twig b/page_manager_ui/templates/page-manager-wizard-form.html.twig
new file mode 100644
index 0000000..62fb633
--- /dev/null
+++ b/page_manager_ui/templates/page-manager-wizard-form.html.twig
@@ -0,0 +1,32 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a 'form' element.
+ *
+ * Available variables
+ * - attributes: A list of HTML attributes for the wrapper element.
+ * - children: The child elements of the form.
+ *
+ * @see template_preprocess_form()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="page-manager-wizard">
+  <div class="page-manager-wizard-actions">
+    {{ form.wizard_actions }}
+  </div>
+  <div class="page-manager-wizard-main clearfix">
+    <div class="page-manager-wizard-tree">
+      {{ form.wizard_tree }}
+    </div>
+    <div class="page-manager-wizard-form">
+      {{ form|without('wizard_actions', 'wizard_tree', 'actions') }}
+    </div>
+  </div>
+
+  <div class="page-manager-wizard-form-actions">
+    {{ form.actions }}
+  </div>
+</div>
+
diff --git a/page_manager_ui/templates/page-manager-wizard-tree.html.twig b/page_manager_ui/templates/page-manager-wizard-tree.html.twig
new file mode 100644
index 0000000..1ae866d
--- /dev/null
+++ b/page_manager_ui/templates/page-manager-wizard-tree.html.twig
@@ -0,0 +1,47 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display wizard tree.
+ *
+ * Available variables:
+ * - step: The current step name.
+ * - tree: A nested list of menu items. Each menu item contains:
+ *   - title: The menu link title.
+ *   - url: The menu link url, instance of \Drupal\Core\Url
+ *   - children: The menu item child items.
+ *   - step: The name of the step.
+ *
+ * @ingroup themeable
+ */
+#}
+{% import _self as page_manager %}
+
+{#
+  We call a macro which calls itself to render the full tree.
+  @see http://twig.sensiolabs.org/doc/tags/macro.html
+#}
+{{ page_manager.wizard_tree(tree, step, 0) }}
+
+{% macro wizard_tree(items, step, menu_level) %}
+  {% import _self as page_manager %}
+  {% if items %}
+    <ul>
+    {% for item in items %}
+      <li>
+        {% if item.url %}
+          {% if step is same as(item.step) %}
+            <strong>{{ link(item.title, item.url) }}</strong>
+          {% else %}
+            {{ link(item.title, item.url) }}
+          {% endif %}
+        {% else %}
+          {{ item.title }}
+        {% endif %}
+        {% if item.children %}
+          {{ page_manager.wizard_tree(item.children, step, menu_level + 1) }}
+        {% endif %}
+      </li>
+    {% endfor %}
+    </ul>
+  {% endif %}
+{% endmacro %}
diff --git a/src/Access/PageManagerPluginAccess.php b/src/Access/PageManagerPluginAccess.php
new file mode 100644
index 0000000..347d636
--- /dev/null
+++ b/src/Access/PageManagerPluginAccess.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Access\PageManagerPluginAccess.
+ */
+
+namespace Drupal\page_manager\Access;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\ctools\Access\AccessInterface;
+
+class PageManagerPluginAccess implements AccessInterface {
+
+  public function access(AccountInterface $account) {
+    return $account->hasPermission('administer pages') ? AccessResult::allowed() : AccessResult::forbidden();
+  }
+
+}
diff --git a/src/Entity/Page.php b/src/Entity/Page.php
index 49c5a8a..537223f 100644
--- a/src/Entity/Page.php
+++ b/src/Entity/Page.php
@@ -34,6 +34,7 @@ use Drupal\page_manager\PageVariantInterface;
  *   config_export = {
  *     "id",
  *     "label",
+ *     "description",
  *     "use_admin_theme",
  *     "path",
  *     "access_logic",
@@ -59,6 +60,13 @@ class Page extends ConfigEntityBase implements PageInterface {
   protected $label;
 
   /**
+   * The description of the page entity.
+   *
+   * @var string
+   */
+  protected $description;
+
+  /**
    * The path of the page entity.
    *
    * @var string
@@ -123,6 +131,13 @@ class Page extends ConfigEntityBase implements PageInterface {
   /**
    * {@inheritdoc}
    */
+  public function getDescription() {
+    return $this->description;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getPath() {
     return $this->path;
   }
@@ -310,7 +325,13 @@ class Page extends ConfigEntityBase implements PageInterface {
    * {@inheritdoc}
    */
   public function addVariant(PageVariantInterface $variant) {
+    // If variants hasn't been initialized, we initialize it before adding the
+    // new variant.
+    if ($this->variants === NULL) {
+      $this->getVariants();
+    }
     $this->variants[$variant->id()] = $variant;
+    $this->sortVariants();
     return $this;
   }
 
@@ -330,6 +351,7 @@ class Page extends ConfigEntityBase implements PageInterface {
    */
   public function removeVariant($variant_id) {
     $this->getVariant($variant_id)->delete();
+    unset($this->variants[$variant_id]);
     return $this;
   }
 
@@ -343,23 +365,19 @@ class Page extends ConfigEntityBase implements PageInterface {
       foreach ($this->variantStorage()->loadByProperties(['page' => $this->id()]) as $variant) {
         $this->variants[$variant->id()] = $variant;
       }
-      // Suppress errors because of https://bugs.php.net/bug.php?id=50688.
-      @uasort($this->variants, [$this, 'variantSortHelper']);
+      $this->sortVariants();
     }
     return $this->variants;
   }
 
   /**
-   * {@inheritdoc}
+   * Sort variants.
    */
-  public function variantSortHelper($a, $b) {
-    $a_weight = $a->getWeight();
-    $b_weight = $b->getWeight();
-    if ($a_weight == $b_weight) {
-      return 0;
+  protected function sortVariants() {
+    if (isset($this->variants)) {
+      // Suppress errors because of https://bugs.php.net/bug.php?id=50688.
+      @uasort($this->variants, '\Drupal\page_manager\Entity\PageVariant::sort');
     }
-
-    return ($a_weight < $b_weight) ? -1 : 1;
   }
 
   /**
@@ -378,25 +396,26 @@ class Page extends ConfigEntityBase implements PageInterface {
   public function __sleep() {
     $vars = parent::__sleep();
 
-    // Ensure any plugin collections are stored correctly before serializing.
-    // @todo Let https://www.drupal.org/node/2650588 handle this instead.
-    foreach ($this->getPluginCollections() as $plugin_config_key => $plugin_collection) {
-      $this->set($plugin_config_key, $plugin_collection->getConfiguration());
-    }
-
-    // Avoid serializing plugin collections and entities as they might contain
-    // references to a lot of objects including the container.
-    $unset_vars = [
-      'variants',
-      'accessConditionCollection',
-    ];
-    foreach ($unset_vars as $unset_var) {
-      if (!empty($this->{$unset_var})) {
-        unset($vars[array_search($unset_var, $vars)]);
-      }
+    // Avoid serializing the page executable as it represents runtime state.
+    $key = array_search('executable', $vars);
+    if ($key !== FALSE) {
+      unset($vars[$key]);
     }
 
     return $vars;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function urlRouteParameters($rel) {
+    if ($rel == 'edit-form') {
+      $uri_route_parameters = [];
+      $uri_route_parameters['machine_name'] = $this->id();
+      $uri_route_parameters['step'] = 'general';
+      return $uri_route_parameters;
+    }
+
+    return parent::urlRouteParameters($rel);
+  }
 }
diff --git a/src/Entity/PageVariant.php b/src/Entity/PageVariant.php
index f6b729c..635dcf9 100644
--- a/src/Entity/PageVariant.php
+++ b/src/Entity/PageVariant.php
@@ -13,6 +13,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
+use Drupal\page_manager\PageInterface;
 use Drupal\page_manager\PageVariantInterface;
 
 /**
@@ -69,7 +70,7 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface {
    *
    * @var int
    */
-  protected $weight = 0;
+  public $weight = 0;
 
   /**
    * The UUID of the page variant entity.
@@ -86,6 +87,13 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface {
   protected $variant;
 
   /**
+   * The loaded page entity this page variant entity belongs to.
+   *
+   * @var \Drupal\page_manager\PageInterface
+   */
+  protected $pageEntity;
+
+  /**
    * The plugin configuration for the variant plugin.
    *
    * @var array
@@ -130,6 +138,13 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface {
   protected $static_context = [];
 
   /**
+   * The page object for the parent page.
+   *
+   * @var \Drupal\page_manager\PageInterface
+   */
+  protected $parentPage;
+
+  /**
    * The plugin collection that holds the single variant plugin instance.
    *
    * @var \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection
@@ -212,6 +227,9 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface {
    */
   protected function getVariantPluginCollection() {
     if (!$this->variantPluginCollection) {
+      if (empty($this->variant_settings['uuid'])) {
+        $this->variant_settings['uuid'] = $this->uuidGenerator()->generate();
+      }
       $this->variantPluginCollection = new DefaultSingleLazyPluginCollection(\Drupal::service('plugin.manager.display_variant'), $this->variant, $this->variant_settings);
     }
     return $this->variantPluginCollection;
@@ -234,11 +252,34 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface {
   /**
    * {@inheritdoc}
    */
+  public function setVariantPluginId($variant) {
+    $this->variant = $variant;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getPage() {
-    if (!$this->page) {
-      throw new \UnexpectedValueException('The page variant has no associated page');
+    if (!$this->pageEntity) {
+      if (!$this->page) {
+        throw new \UnexpectedValueException('The page variant has no associated page');
+      }
+      $this->pageEntity = $this->getPageStorage()->load($this->page);
+      if (!$this->pageEntity) {
+        throw new \UnexpectedValueException(sprintf('The page %s could not be loaded.', $this->page));
+      }
     }
-    return $this->getPageStorage()->load($this->page);
+    return $this->pageEntity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setPageEntity(PageInterface $page) {
+    $this->pageEntity = $page;
+    $this->page = $page->id();
+    return $this;
   }
 
   /**
@@ -246,9 +287,9 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface {
    */
   public function getContexts() {
     if (is_null($this->contexts)) {
-      $static_contexts = $this->getContextMapper()->getContextValues($this->getStaticContexts());
       $page_contexts = $this->getPage()->getContexts();
-      $this->contexts = array_merge($static_contexts, $page_contexts);
+      $variant_contexts = $this->getContextMapper()->getContextValues($this->getStaticContexts());
+      $this->contexts = array_merge($variant_contexts, $page_contexts);
     }
     return $this->contexts;
   }
@@ -429,8 +470,20 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface {
   public function __sleep() {
     $vars = parent::__sleep();
     // Gathered contexts objects should not be serialized.
-    unset($vars[array_search('contexts', $vars)]);
+    $key = array_search('contexts', $vars);
+    if ($key !== FALSE) {
+      unset($vars[$key]);
+    }
     return $vars;
   }
 
+  /**
+   * Wraps the shared tempstore factory.
+   *
+   * @return \Drupal\user\SharedTempStoreFactory
+   */
+  protected function getTempstoreFactory() {
+    return \Drupal::service('user.shared_tempstore');
+  }
+
 }
diff --git a/src/PageInterface.php b/src/PageInterface.php
index 96eaab2..dd424dc 100644
--- a/src/PageInterface.php
+++ b/src/PageInterface.php
@@ -17,6 +17,14 @@ use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
 interface PageInterface extends ConfigEntityInterface, EntityWithPluginCollectionInterface {
 
   /**
+   * Returns the administrative description of the page.
+   *
+   * @return string
+   *   The description of this page.
+   */
+  public function getDescription();
+
+  /**
    * Returns whether the page entity is enabled.
    *
    * @return bool
diff --git a/src/PageVariantInterface.php b/src/PageVariantInterface.php
index 9705d8c..a3003e0 100644
--- a/src/PageVariantInterface.php
+++ b/src/PageVariantInterface.php
@@ -30,6 +30,19 @@ interface PageVariantInterface extends ConfigEntityInterface, EntityWithPluginCo
   public function getVariantPluginId();
 
   /**
+   * Sets the plugin ID of the variant plugin without loading the Plugin
+   *   collections.
+   *
+   * @param string $variant
+   *   The plugin ID of the variant plugin.
+   *
+   * @return $this
+   *
+   * @see \Drupal\page_manager\Entity\PageVariant::getPluginCollections()
+   */
+  public function setVariantPluginId($variant);
+
+  /**
    * Gets the page this variant is on.
    *
    * @return \Drupal\page_manager\PageInterface
diff --git a/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php b/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php
index 654dc4c..4b46141 100644
--- a/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php
+++ b/src/Plugin/DisplayVariant/PageBlockDisplayVariant.php
@@ -21,6 +21,8 @@ use Drupal\Core\Render\Element;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Utility\Token;
 use Drupal\ctools\Plugin\DisplayVariant\BlockDisplayVariant;
+use Drupal\ctools\Plugin\PluginWizardInterface;
+use Drupal\page_manager_ui\Form\VariantPluginContentForm;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -31,7 +33,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *   admin_label = @Translation("Block page")
  * )
  */
-class PageBlockDisplayVariant extends BlockDisplayVariant {
+class PageBlockDisplayVariant extends BlockDisplayVariant implements PluginWizardInterface {
 
   /**
    * The module handler.
@@ -248,6 +250,18 @@ class PageBlockDisplayVariant extends BlockDisplayVariant {
   /**
    * {@inheritdoc}
    */
+  public function getWizardOperations($cached_values) {
+    return [
+      'content' => [
+        'title' => $this->t('Content'),
+        'form' => VariantPluginContentForm::class,
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function defaultConfiguration() {
     return parent::defaultConfiguration() + [
       'page_title' => '',
@@ -300,4 +314,10 @@ class PageBlockDisplayVariant extends BlockDisplayVariant {
     ];
   }
 
+  public function __sleep() {
+    $vars = parent::__sleep();
+    unset($vars[array_search('stringTranslation', $vars)]);
+    return $vars;
+  }
+
 }
diff --git a/src/Tests/StaticContextTest.php b/src/Tests/StaticContextTest.php
index 2b382e7..ad21a0b 100644
--- a/src/Tests/StaticContextTest.php
+++ b/src/Tests/StaticContextTest.php
@@ -53,18 +53,10 @@ class StaticContextTest extends WebTestBase {
       'label' => 'Static node context',
       'id' => 'static_node_context',
       'path' => 'static-context',
+      'variant_plugin_id' => 'block_display',
+      'wizard_options[contexts]' => TRUE,
     ];
-    $this->drupalPostForm('admin/structure/page_manager/add', $edit_page, 'Save');
-
-    // Add a new variant.
-    $this->clickLink('Add new variant');
-    $this->clickLink('Block page');
-    $variant_edit = [
-      'label' => 'Static context blocks',
-      'id' => 'block_page',
-      'variant_settings[page_title]' => 'Static context test page',
-    ];
-    $this->drupalPostForm(NULL, $variant_edit, 'Save');
+    $this->drupalPostForm('admin/structure/page_manager/add', $edit_page, 'Next');
 
     // Add a static context for each node to the page variant.
     $contexts = array(
@@ -80,16 +72,25 @@ class StaticContextTest extends WebTestBase {
       ),
     );
     foreach ($contexts as $context) {
-      $this->clickLink('Add new static context');
-      $edit = array(
+      $edit = [
+        'types' => 'entity:node',
+      ];
+      $this->drupalPostForm(NULL, $edit, 'Add new context');
+      $edit = [
         'label' => $context['title'],
-        'machine_name' => $context['machine_name'],
-        'entity_type' => 'node',
-        'selection' => $context['node']->getTitle(),
-      );
-      $this->drupalPostForm(NULL, $edit, 'Add Static Context');
-      $this->assertText('The ' . $edit['label'] . ' static context has been added.');
+        'id' => $context['machine_name'],
+        'value' => $context['node']->getTitle() . ' (' . $context['node']->id() . ')',
+      ];
+      $this->drupalPostForm(NULL, $edit, 'Save');
+      $this->assertText($context['title']);
     }
+    $this->drupalPostForm(NULL, [], 'Next');
+
+    // Add a new variant.
+    $variant_edit = [
+      'variant_settings[page_title]' => 'Static context test page',
+    ];
+    $this->drupalPostForm(NULL, $variant_edit, 'Next');
 
     // Add a block that renders the node from the first static context.
     $this->clickLink('Add new block');
@@ -115,6 +116,7 @@ class StaticContextTest extends WebTestBase {
     ];
     $this->drupalPostForm(NULL, $edit, 'Add block');
     $this->assertText($edit['settings[label]']);
++    $this->drupalPostForm(NULL, [], 'Finish');
 
     // Open the page and verify that the node from the static context is there.
     $this->drupalGet($edit_page['path']);
@@ -124,16 +126,15 @@ class StaticContextTest extends WebTestBase {
     $this->assertText($node2->get('body')->getValue()[0]['value']);
 
     // Change the second static context to the first node.
-    $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']);
-    $this->clickLink('Edit');
-    $this->clickLink('Edit', 3);
-    $edit = array(
+    $this->drupalGet('admin/structure/page_manager/manage/static_node_context/page_variant__static_node_context-block_display-0__contexts');
+    $this->clickLink('Edit', 1);
+    $edit = [
       'label' => 'Static Node 2 edited',
-      'entity_type' => 'node',
-      'selection' => $node->getTitle(),
-    );
-    $this->drupalPostForm(NULL, $edit, t('Update Static Context'));
-    $this->assertText('The ' . $edit['label'] . ' static context has been updated.');
+      'value' => $node->getTitle(),
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText("Static Node 2 edited");
+    $this->drupalPostForm(NULL, [], 'Update and save');
 
     // Open the page and verify that the node from the static context is there.
     $this->drupalGet($edit_page['path']);
@@ -144,20 +145,20 @@ class StaticContextTest extends WebTestBase {
     $this->assertNoText($node2->get('body')->getValue()[0]['value']);
 
     // Change the first static context to the second node.
-    $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']);
+    $this->drupalGet('admin/structure/page_manager/manage/static_node_context/page_variant__static_node_context-block_display-0__contexts');
     $this->clickLink('Edit');
-    $this->clickLink('Edit', 2);
     $edit = array(
       'label' => 'Static Node edited',
-      'entity_type' => 'node',
-      'selection' => $node2->getTitle(),
+      'value' => $node2->getTitle(),
     );
-    $this->drupalPostForm(NULL, $edit, t('Update Static Context'));
-    $this->assertText('The ' . $edit['label'] . ' static context has been updated.');
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText("Static Node 2 edited");
 
     // Remove the second static context view block from the variant.
+    $this->drupalGet('admin/structure/page_manager/manage/static_node_context/page_variant__static_node_context-block_display-0__content');
     $this->clickLink('Delete', 1);
-    $this->drupalPostForm(NULL, NULL, t('Delete'));
+    $this->drupalPostForm(NULL, [], t('Delete'));
+    $this->drupalPostForm(NULL, [], 'Update and save');
 
     // Make sure only the second static context's node is rendered on the page.
     $this->drupalGet($edit_page['path']);
@@ -167,12 +168,12 @@ class StaticContextTest extends WebTestBase {
     $this->assertText($node2->get('body')->getValue()[0]['value']);
 
     // Delete a static context and verify that it was deleted.
-    $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']);
-    $this->clickLink('Edit');
-    $this->clickLink('Delete', 1);
-    $this->drupalPostForm(NULL, NULL, t('Delete'));
-    $this->assertText('The static context ' . $edit['label'] . ' has been removed.');
-    $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']);
+    $this->drupalGet('admin/structure/page_manager/manage/static_node_context/page_variant__static_node_context-block_display-0__contexts');
+    $this->clickLink('Delete');
+    $this->drupalPostForm(NULL, [], t('Delete'));
+    $this->assertText("The static context Static Node edited has been removed.");
+    // Reload the page to clear the message
+    $this->drupalGet($this->getUrl());
     $this->assertNoText($edit['label']);
   }
 
diff --git a/tests/src/Unit/PageTest.php b/tests/src/Unit/PageTest.php
index 55713fc..de64272 100644
--- a/tests/src/Unit/PageTest.php
+++ b/tests/src/Unit/PageTest.php
@@ -49,10 +49,11 @@ class PageTest extends UnitTestCase {
   public function testGetVariants() {
     $variant1 = $this->prophesize(PageVariantInterface::class);
     $variant1->id()->willReturn('variant1');
-    $variant1->getWeight()->willReturn(0);
+    $variant1->label()->willReturn('Variant 1');
     $variant2 = $this->prophesize(PageVariantInterface::class);
     $variant2->id()->willReturn('variant2');
-    $variant2->getWeight()->willReturn(-10);
+    $variant2->label()->willReturn('Variant 2');
+    $variant2->weight = -10;
 
     $entity_storage = $this->prophesize(EntityStorageInterface::class);
     $entity_storage
