diff --git a/config/schema/page_manager.schema.yml b/config/schema/page_manager.schema.yml
index 0cc98c5..4c2b2bd 100644
--- a/config/schema/page_manager.schema.yml
+++ b/config/schema/page_manager.schema.yml
@@ -8,43 +8,74 @@ 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'
     path:
       type: string
       label: 'Page path'
+    parameters:
+      type: mapping
     display_variants:
       type: sequence
       label: 'Display variants'
       sequence:
-        - type: display_variant.plugin.[id]
+        - type: page_manager.page_variant.[id]
           label: 'Display variant'
-    access_logic:
-      type: string
-      label: 'Access logic'
     access_conditions:
       type: sequence
       label: 'Access Conditions'
       sequence:
         - type: condition.plugin.[id]
           label: 'Access Condition'
-    static_context:
+    access_logic:
+      type: string
+      label: 'Access logic'
+    menu_type:
+      type: string
+      label: 'Menu type'
+    menu_settings:
+      type: page_manager.menu.settings.[id]
+      label: 'Menu settings'
+
+page_manager.page_variant.*:
+  type: config_entity
+  label: 'Page variant configuration'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Label'
+    weight:
+      type: integer
+      label: 'Weight'
+    uuid:
+      type: string
+      label: 'UUID'
+    variant:
+      type: string
+      label: 'Display variant'
+    variant_settings:
+      type: display_variant.plugin.[%parent.variant]
+    page:
+      type: page_manager.page.[id]
+      label: 'Parent page'
+    selection_criteria:
       type: sequence
-      label: Static context list
+      label: 'Selection criteria'
       sequence:
-        - type: mapping
-          label: 'Static context'
-          mapping:
-            label:
-              type: label
-              label: 'Label of the context'
-            type:
-              type: string
-              label: 'Context type'
-            value:
-              type: string
-              label: 'Context value'
+        - type: condition.plugin.[id]
+          label: 'Selection criterion'
+    selection_logic:
+      type: string
+      label: 'Selection logic'
+    contexts:
+      type: mapping
 
 page_manager.block_plugin.*:
   type: block.settings.[id]
diff --git a/page_manager.info.yml b/page_manager.info.yml
index 3a26fe5..7013833 100644
--- a/page_manager.info.yml
+++ b/page_manager.info.yml
@@ -5,4 +5,4 @@ package: Layout
 version: VERSION
 core: 8.x
 dependencies:
-  - block
+  - ctools
diff --git a/page_manager.links.action.yml b/page_manager.links.action.yml
index 713470d..2eae1d5 100644
--- a/page_manager.links.action.yml
+++ b/page_manager.links.action.yml
@@ -2,4 +2,10 @@ entity.page.add_form:
   route_name: entity.page.add_form
   title: 'Add page'
   appears_on:
-    - 'page_manager.page_list'
+    - 'entity.page.collection'
+entity.page_variant.add_form:
+  route_name: 'entity.page_variant.add_form'
+  title: 'Add PageVariant'
+  appears_on:
+    - entity.page_variant.collection
+
diff --git a/page_manager.links.menu.yml b/page_manager.links.menu.yml
index f7d73d2..3870671 100644
--- a/page_manager.links.menu.yml
+++ b/page_manager.links.menu.yml
@@ -1,5 +1,5 @@
-page_manager.page_list:
+entity.page.collection:
   title: 'Pages'
   parent: system.admin_structure
   description: 'Manage pages.'
-  route_name: page_manager.page_list
+  route_name: entity.page.collection
diff --git a/page_manager.routing.yml b/page_manager.routing.yml
index 96b0b61..97564e6 100644
--- a/page_manager.routing.yml
+++ b/page_manager.routing.yml
@@ -1,6 +1,4 @@
-#### Pages
-
-page_manager.page_list:
+entity.page.collection:
   path: '/admin/structure/page_manager'
   defaults:
     _entity_list: 'page'
@@ -11,18 +9,30 @@ page_manager.page_list:
 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\Controller\PageManagerController::editPageTitle'
+    tempstore_id: page_manager.page
   requirements:
-    _entity_access: page.update
+    #_entity_access: page.update
+    _entity_create_access: page
 
 entity.page.delete_form:
   path: '/admin/structure/page_manager/manage/{page}/delete'
@@ -48,6 +58,71 @@ entity.page.disable:
   requirements:
     _entity_access: 'page.update'
 
+entity.page.condition.add:
+  path: '/admin/structure/page_manager/access/add/{machine_name}/{condition}'
+  defaults:
+    _form: 'Drupal\page_manager\Form\AccessConfigure'
+    tempstore_id: page_manager.page
+    _title: 'Add Condition'
+  requirements:
+    _permission: 'administer site configuration'
+
+entity.page.condition.edit:
+  path: '/admin/structure/page_manager/access/edit/{machine_name}/{condition}'
+  defaults:
+    _form: 'Drupal\page_manager\Form\AccessConfigure'
+    tempstore_id: page_manager.page
+    _title: 'Edit Condition'
+  requirements:
+    _permission: 'administer site configuration'
+
+entity.page.condition.delete:
+  path: '/admin/structure/page_manager/access/delete/{machine_name}/{id}'
+  defaults:
+    _form: 'Drupal\page_manager\Form\AccessDelete'
+    tempstore_id: page_manager.page
+    _title: 'Delete Condition'
+  requirements:
+    _permission: 'administer site configuration'
+
+page_manager.route.parameters.configure:
+  path: '/admin/structure/page_manager/parameter/{js}/{parameter}/{machine_name}/{step}'
+  defaults:
+    _wizard: 'Drupal\page_manager\Wizard\RouteParameters'
+    tempstore_id: page_manager.page
+  requirements:
+    _permission: 'administer site configuration'
+
+entity.page_variant.condition.add:
+  path: '/admin/structure/page_manager/selection/add/{machine_name}/{condition}'
+  defaults:
+    _form: 'Drupal\page_manager\Form\SelectionConfigure'
+    tempstore_id: page_manager.page
+    _title: 'Add Condition'
+  requirements:
+    _permission: 'administer site configuration'
+
+entity.page_variant.condition.edit:
+  path: '/admin/structure/page_manager/selection/edit/{machine_name}/{condition}'
+  defaults:
+    _form: 'Drupal\page_manager\Form\SelectionConfigure'
+    tempstore_id: page_manager.page
+    _title: 'Edit Condition'
+  requirements:
+    _permission: 'administer site configuration'
+
+entity.page_variant.condition.delete:
+  path: '/admin/structure/page_manager/selection/delete/{machine_name}/{id}'
+  defaults:
+    _form: 'Drupal\page_manager\Form\SelectionDelete'
+    tempstore_id: page_manager.page
+    _title: 'Delete Condition'
+  requirements:
+    _permission: 'administer site configuration'
+
+
+
+
 #### Access Conditions
 
 page_manager.access_condition_select:
@@ -82,32 +157,6 @@ page_manager.access_condition_delete:
   requirements:
     _entity_access: page.update
 
-#### Static Contexts
-
-page_manager.static_context_add:
-  path: '/admin/structure/page_manager/manage/{page}/context/add'
-  defaults:
-    _form: '\Drupal\page_manager\Form\StaticContextAddForm'
-    _title: 'Add new static context'
-  requirements:
-    _entity_access: page.update
-
-page_manager.static_context_edit:
-  path: '/admin/structure/page_manager/manage/{page}/context/edit/{name}'
-  defaults:
-    _form: '\Drupal\page_manager\Form\StaticContextEditForm'
-    _title_callback: '\Drupal\page_manager\Controller\PageManagerController::editStaticContextTitle'
-  requirements:
-    _entity_access: page.update
-
-page_manager.static_context_delete:
-  path: '/admin/structure/page_manager/manage/{page}/context/delete/{name}'
-  defaults:
-    _form: '\Drupal\page_manager\Form\StaticContextDeleteForm'
-    _title: 'Delete static context'
-  requirements:
-    _entity_access: page.update
-
 #### Display variants
 
 page_manager.display_variant_select:
@@ -153,7 +202,7 @@ page_manager.display_variant_select_block:
 page_manager.display_variant_add_block:
   path: '/admin/structure/page_manager/manage/{page}/manage/{display_variant_id}/block/add/{block_id}'
   defaults:
-    _form: '\Drupal\page_manager\Form\DisplayVariantAddBlockForm'
+    _form: 'Drupal\page_manager\Form\DisplayVariantAddBlockForm'
     _title: 'Add block to display variant'
   requirements:
     _entity_access: page.update
@@ -161,7 +210,7 @@ page_manager.display_variant_add_block:
 page_manager.display_variant_edit_block:
   path: '/admin/structure/page_manager/manage/{page}/manage/{display_variant_id}/block/edit/{block_id}'
   defaults:
-    _form: '\Drupal\page_manager\Form\DisplayVariantEditBlockForm'
+    _form: 'Drupal\page_manager\Form\DisplayVariantEditBlockForm'
     _title: 'Edit block in display variant'
   requirements:
     _entity_access: page.update
@@ -169,7 +218,7 @@ page_manager.display_variant_edit_block:
 page_manager.display_variant_delete_block:
   path: '/admin/structure/page_manager/manage/{page}/manage/{display_variant_id}/block/delete/{block_id}'
   defaults:
-    _form: '\Drupal\page_manager\Form\DisplayVariantDeleteBlockForm'
+    _form: 'Drupal\page_manager\Form\DisplayVariantDeleteBlockForm'
     _title: 'Delete block in display variant'
   requirements:
     _entity_access: page.update
@@ -207,3 +256,36 @@ page_manager.selection_condition_delete:
     _title: 'Delete selection condition'
   requirements:
     _entity_access: page.update
+# PageVariant routing definition
+entity.page_variant.collection:
+  path: '/admin/config/system/page_variant'
+  defaults:
+    _entity_list: 'page_variant'
+    _title: 'PageVariant Configuration'
+  requirements:
+    _permission: 'administer site configuration'
+
+entity.page_variant.add_form:
+  path: '/admin/config/system/page_variant/add'
+  defaults:
+    _entity_form: 'page_variant.add'
+    _title: 'Add PageVariant'
+  requirements:
+    _permission: 'administer site configuration'
+
+entity.page_variant.edit_form:
+  path: '/admin/config/system/page_variant/{page_variant}'
+  defaults:
+    _entity_form: 'page_variant.edit'
+    _title: 'Edit PageVariant'
+  requirements:
+    _permission: 'administer site configuration'
+
+entity.page_variant.delete_form:
+  path: '/admin/config/system/page_variant/{page_variant}/delete'
+  defaults:
+    _entity_form: 'page_variant.delete'
+    _title: 'Delete PageVariant'
+  requirements:
+    _permission: 'administer site configuration'
+
diff --git a/page_manager.services.yml b/page_manager.services.yml
index f4b5c01..951466f 100644
--- a/page_manager.services.yml
+++ b/page_manager.services.yml
@@ -1,4 +1,7 @@
 services:
+  plugin.manager.page_manager_menu:
+    class: Drupal\page_manager\Plugin\PageManagerMenuManager
+    parent: default_plugin_manager
   page_manager.current_user_context:
     class: Drupal\page_manager\EventSubscriber\CurrentUserContext
     arguments: ['@current_user', '@entity.manager']
@@ -9,11 +12,6 @@ services:
     arguments: ['@router.route_provider', '@request_stack']
     tags:
       - { name: 'event_subscriber' }
-  page_manager.static_context:
-    class: Drupal\page_manager\EventSubscriber\StaticContext
-    arguments: ['@entity.manager']
-    tags:
-      - { name: 'event_subscriber' }
   page_manager.page_manager_routes:
     class: Drupal\page_manager\Routing\PageManagerRoutes
     arguments: ['@entity.manager']
diff --git a/src/Annotation/PageManagerMenu.php b/src/Annotation/PageManagerMenu.php
new file mode 100644
index 0000000..2d400ff
--- /dev/null
+++ b/src/Annotation/PageManagerMenu.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Annotation\PageManagerMenu.
+ */
+
+namespace Drupal\page_manager\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a PageManagerMenu annotation object.
+ *
+ * @Annotation
+ */
+class PageManagerMenu extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The label of the menu type.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $label;
+
+}
diff --git a/src/Context/EntityLazyLoadContext.php b/src/Context/EntityLazyLoadContext.php
deleted file mode 100644
index 43bfe98..0000000
--- a/src/Context/EntityLazyLoadContext.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Context\EntityLazyLoadContext.
- */
-
-namespace Drupal\page_manager\Context;
-
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Plugin\Context\Context;
-use Drupal\Core\Plugin\Context\ContextDefinitionInterface;
-
-class EntityLazyLoadContext extends Context {
-
-  /**
-   * The entity UUID.
-   *
-   * @var string
-   */
-  protected $uuid;
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * Construct an EntityLazyLoadContext object.
-   *
-   * @param \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context_definition
-   *   The context definition.
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   * @param string $uuid
-   *   The UUID of the entity.
-   */
-  public function __construct(ContextDefinitionInterface $context_definition, EntityManagerInterface $entity_manager, $uuid) {
-    parent::__construct($context_definition);
-    $this->entityManager = $entity_manager;
-    $this->uuid = $uuid;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getContextValue() {
-    if (!$this->contextValue) {
-      $entity_type_id = substr($this->contextDefinition->getDataType(), 7);
-      $this->contextValue = $this->entityManager->loadEntityByUuid($entity_type_id, $this->uuid);
-    }
-    return $this->contextValue;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function hasContextValue() {
-    // Ensure that the entity is loaded before checking if it exists.
-    if (!$this->contextValue) {
-      $this->getContextValue();
-    }
-    return parent::hasContextValue();
-  }
-
-}
diff --git a/src/Controller/PageManagerController.php b/src/Controller/PageManagerController.php
index be37df5..11fd00f 100644
--- a/src/Controller/PageManagerController.php
+++ b/src/Controller/PageManagerController.php
@@ -148,22 +148,6 @@ class PageManagerController extends ControllerBase {
   }
 
   /**
-   * Route title callback.
-   *
-   * @param \Drupal\page_manager\PageInterface $page
-   *   The page entity.
-   * @param string $name
-   *   The static context name.
-   *
-   * @return string
-   *   The title for the static context edit form.
-   */
-  public function editStaticContextTitle(PageInterface $page, $name) {
-    $static_context = $page->getStaticContext($name);
-    return $this->t('Edit @label static context', ['@label' => $static_context['label']]);
-  }
-
-  /**
    * Enables or disables a Page.
    *
    * @param \Drupal\page_manager\PageInterface $page
@@ -184,7 +168,7 @@ class PageManagerController extends ControllerBase {
       drupal_set_message($this->t('The %label page has been disabled.', ['%label' => $page->label()]));
     }
 
-    return $this->redirect('page_manager.page_list');
+    return $this->redirect('entity.page.collection');
   }
 
   /**
@@ -210,7 +194,7 @@ class PageManagerController extends ControllerBase {
         ]),
         'attributes' => [
           'class' => ['use-ajax'],
-          'data-dialog-type' => 'modal',
+          'data-accepts' => 'application/vnd.drupal-modal',
           'data-dialog-options' => Json::encode([
             'width' => 'auto',
           ]),
@@ -244,7 +228,7 @@ class PageManagerController extends ControllerBase {
         ]),
         'attributes' => [
           'class' => ['use-ajax'],
-          'data-dialog-type' => 'modal',
+          'data-accepts' => 'application/vnd.drupal-modal',
           'data-dialog-options' => Json::encode([
             'width' => 'auto',
           ]),
@@ -281,7 +265,7 @@ class PageManagerController extends ControllerBase {
         ]),
         'attributes' => [
           'class' => ['use-ajax'],
-          'data-dialog-type' => 'modal',
+          'data-accepts' => 'application/vnd.drupal-modal',
           'data-dialog-options' => Json::encode([
             'width' => 'auto',
           ]),
@@ -339,7 +323,7 @@ class PageManagerController extends ControllerBase {
         ]),
         'attributes' => [
           'class' => ['use-ajax'],
-          'data-dialog-type' => 'modal',
+          'data-accepts' => 'application/vnd.drupal-modal',
           'data-dialog-options' => Json::encode([
             'width' => 'auto',
           ]),
diff --git a/src/Controller/PageVariantListBuilder.php b/src/Controller/PageVariantListBuilder.php
new file mode 100644
index 0000000..7426feb
--- /dev/null
+++ b/src/Controller/PageVariantListBuilder.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\page_manager\Controller\PageVariantListBuilder.
+ */
+
+namespace Drupal\page_manager\Controller;
+
+use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides a listing of PageVariant.
+ */
+class PageVariantListBuilder extends ConfigEntityListBuilder {
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header['label'] = $this->t('PageVariant');
+    $header['id'] = $this->t('Machine name');
+    return $header + parent::buildHeader();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row['label'] = $this->getLabel($entity);
+    $row['id'] = $entity->id();
+    // You probably want a few more properties here...
+    return $row + parent::buildRow($entity);
+  }
+
+}
diff --git a/src/Entity/Page.php b/src/Entity/Page.php
index b73ebc5..f2e11d5 100644
--- a/src/Entity/Page.php
+++ b/src/Entity/Page.php
@@ -7,11 +7,14 @@
 
 namespace Drupal\page_manager\Entity;
 
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Plugin\Context\ContextInterface;
 use Drupal\page_manager\PageInterface;
 use Drupal\Core\Condition\ConditionPluginCollection;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\page_manager\Plugin\VariantAwareTrait;
+use Drupal\page_manager\PageVariantInterface;
 
 /**
  * Defines a Page entity class.
@@ -24,9 +27,11 @@ use Drupal\page_manager\Plugin\VariantAwareTrait;
  *     "list_builder" = "Drupal\page_manager\Entity\PageListBuilder",
  *     "view_builder" = "Drupal\page_manager\Entity\PageViewBuilder",
  *     "form" = {
- *       "add" = "Drupal\page_manager\Form\PageAddForm",
- *       "edit" = "Drupal\page_manager\Form\PageEditForm",
- *       "delete" = "Drupal\page_manager\Form\PageDeleteForm",
+ *       "delete" = "Drupal\page_manager\Form\PageDeleteForm"
+ *     },
+ *     "wizard" = {
+ *       "add" = "Drupal\page_manager\Wizard\PageManager",
+ *       "edit" = "Drupal\page_manager\Wizard\PageManager"
  *     }
  *   },
  *   admin_permission = "administer pages",
@@ -35,18 +40,10 @@ use Drupal\page_manager\Plugin\VariantAwareTrait;
  *     "label" = "label",
  *     "status" = "status"
  *   },
- *   config_export = {
- *     "id",
- *     "label",
- *     "use_admin_theme",
- *     "path",
- *     "display_variants",
- *     "access_logic",
- *     "access_conditions",
- *   },
  *   links = {
+ *     "collection" = "/admin/structure/page_manager",
  *     "add-form" = "/admin/structure/page_manager/add",
- *     "edit-form" = "/admin/structure/page_manager/manage/{page}",
+ *     "edit-form" = "/admin/structure/page_manager/manage/{machine_name}/{step}",
  *     "delete-form" = "/admin/structure/page_manager/manage/{page}/delete",
  *     "enable" = "/admin/structure/page_manager/manage/{page}/enable",
  *     "disable" = "/admin/structure/page_manager/manage/{page}/disable"
@@ -55,8 +52,6 @@ use Drupal\page_manager\Plugin\VariantAwareTrait;
  */
 class Page extends ConfigEntityBase implements PageInterface {
 
-  use VariantAwareTrait;
-
   /**
    * The ID of the page entity.
    *
@@ -72,17 +67,38 @@ class Page extends ConfigEntityBase implements PageInterface {
   protected $label;
 
   /**
+   * The description of the page entity.
+   *
+   * @var string
+   */
+  protected $description;
+
+  /**
+   * Indicates if this page should be displayed in the admin theme.
+   *
+   * @var bool
+   */
+  protected $use_admin_theme;
+
+  /**
    * The path of the page entity.
    *
    * @var string
    */
-  protected $path;
+  protected $path = '';
 
   /**
-   * The configuration of the display variants.
+   * The configured parameters of the page.
    *
    * @var array
    */
+  protected $parameters = [];
+
+  /**
+   * The entity ids of the display variants.
+   *
+   * @var \Drupal\page_manager\Entity\PageVariant[].
+   */
   protected $display_variants = [];
 
   /**
@@ -106,21 +122,9 @@ class Page extends ConfigEntityBase implements PageInterface {
    */
   protected $accessConditionCollection;
 
-  /**
-   * Indicates if this page should be displayed in the admin theme.
-   *
-   * @var bool
-   */
-  protected $use_admin_theme;
+  protected $menu_type = 'route';
 
-  /**
-   * Static context references.
-   *
-   * A list of arrays with the keys name, label, type and value.
-   *
-   * @var array[]
-   */
-  protected $static_context = [];
+  protected $menu_settings = [];
 
   /**
    * Stores a reference to the executable version of this page.
@@ -140,6 +144,10 @@ class Page extends ConfigEntityBase implements PageInterface {
     return \Drupal::service('page_manager.executable_factory');
   }
 
+  public function getDescription() {
+    return $this->description;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -158,16 +166,28 @@ class Page extends ConfigEntityBase implements PageInterface {
     $names = [
       'id',
       'label',
+      'description',
+      'use_admin_theme',
       'path',
+      'parameters',
       'display_variants',
       'access_conditions',
       'access_logic',
-      'use_admin_theme',
-      'static_context',
+      'menu_type',
+      'menu_settings',
     ];
     foreach ($names as $name) {
       $properties[$name] = $this->get($name);
     }
+    /**
+     * @var $id string
+     * @var $entity \Drupal\page_manager\Entity\PageVariant
+     *
+     * This may be a bad idea...
+     */
+    foreach ($properties['display_variants'] as $id => $entity) {
+      $properties['display_variants'][$id] = $entity->toArray();
+    }
     return $properties;
   }
 
@@ -186,18 +206,37 @@ class Page extends ConfigEntityBase implements PageInterface {
   }
 
   /**
-   * {@inheritdoc}
+   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
    */
-  public function postCreate(EntityStorageInterface $storage) {
-    parent::postCreate($storage);
-    // Ensure there is at least one display variant.
-    if (!count($this->getVariants())) {
-      $this->addVariant([
-        'id' => 'http_status_code',
-        'label' => 'Default',
-        'weight' => 10,
-      ]);
-    }
+  public function getParameters() {
+    return $this->parameters;
+  }
+
+  /**
+   * @param $parameter
+   * @return \Drupal\Core\Plugin\Context\ContextInterface
+   */
+  public function getParameter($parameter) {
+    return !empty($this->parameters[$parameter]) ? $this->parameters[$parameter] : new Context(new ContextDefinition('any', t('No context assigned')));
+  }
+
+  public function setParameters($parameters) {
+    $this->parameters = $parameters;
+    return $this;
+  }
+
+  public function setParameter($parameter, ContextInterface $context) {
+    $this->parameters[$parameter] = $context;
+    return $this;
+  }
+
+  public function hasParameter($parameter) {
+    return isset($this->parameters[$parameter]);
+  }
+
+  public function deleteParameter($parameter) {
+    unset($this->parameters[$parameter]);
+    return $this;
   }
 
   /**
@@ -206,6 +245,8 @@ class Page extends ConfigEntityBase implements PageInterface {
   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     parent::postSave($storage, $update);
     static::routeBuilder()->setRebuildNeeded();
+    // @todo create a 404 http response variant with high weight to cover
+    // variant selection fallback and non-variant bearing page sanity.
   }
 
   /**
@@ -213,6 +254,12 @@ class Page extends ConfigEntityBase implements PageInterface {
    */
   public static function postDelete(EntityStorageInterface $storage, array $entities) {
     parent::postDelete($storage, $entities);
+    /** @var $entity \Drupal\page_manager\Entity\Page */
+    foreach ($entities as $entity) {
+      foreach ($entity->getVariants() as $variant) {
+        $variant->delete();
+      }
+    }
     static::routeBuilder()->setRebuildNeeded();
   }
 
@@ -248,7 +295,7 @@ class Page extends ConfigEntityBase implements PageInterface {
    */
   public function getPluginCollections() {
     return [
-      'display_variants' => $this->getVariants(),
+      //'display_variants' => $this->getVariants(),
       'access_conditions' => $this->getAccessConditions(),
     ];
   }
@@ -297,49 +344,90 @@ class Page extends ConfigEntityBase implements PageInterface {
   /**
    * {@inheritdoc}
    */
-  public function getStaticContexts() {
-    return $this->static_context;
+  public function getContexts() {
+    //return $this->getExecutable()->getContexts();
+    return $this->getParameters();
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getStaticContext($name) {
-    if (isset($this->static_context[$name])) {
-      return $this->static_context[$name];
-    }
-    return [];
+  public function isFallbackPage() {
+    $fallback_page = $this->configFactory()->get('page_manager.settings')->get('fallback_page');
+    return $this->id() == $fallback_page;
+  }
+
+  public function getMenuType() {
+    return $this->menu_type;
+  }
+
+  public static function preDelete(EntityStorageInterface $storage, array $entities) {
+    parent::preDelete($storage, $entities); // TODO: Change the autogenerated stub
+  }
+
+  public function getMenuSettings() {
+    return $this->menu_settings;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function setStaticContext($name, $configuration) {
-    $this->static_context[$name] = $configuration;
-    return $this;
+  public function addVariant(array $configuration) {
+    $configuration['page'] = $this->id();
+    /** @var $variant \Drupal\page_manager\PageVariantInterface */
+    $variant = \Drupal::entityManager()->getStorage('page_variant')->create($configuration);
+    return $this->addVariantEntity($variant);
+  }
+
+  public function addVariantEntity(PageVariantInterface $variant) {
+    $this->display_variants[$variant->id()] = $variant;
+    return $variant->id();
   }
 
   /**
    * {@inheritdoc}
    */
-  public function removeStaticContext($name) {
-    unset($this->static_context[$name]);
-    return $this;
+  public function getVariant($variant_id) {
+    if (!isset($this->display_variants[$variant_id])) {
+      throw new \Exception('The requested display variant does not exist or is not associated with this page.');
+    }
+    return $this->display_variants[$variant_id];
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getContexts() {
-    return $this->getExecutable()->getContexts();
+  public function removeVariant($variant_id) {
+    unset($this->display_variants[$variant_id]);
+    return $this;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function isFallbackPage() {
-    $fallback_page = $this->configFactory()->get('page_manager.settings')->get('fallback_page');
-    return $this->id() == $fallback_page;
+  public function getVariants() {
+    return $this->display_variants;
+  }
+
+  public function save() {
+    $variants = $this->getVariants();
+    $return = parent::save();
+    if ($return) {
+      foreach ($variants as $variant) {
+        $variant->save();
+      }
+    }
+    return $return;
+  }
+
+  public static function postLoad(EntityStorageInterface $storage, array &$entities) {
+    foreach ($entities as $entity) {
+      $variants = [];
+      foreach (\Drupal::entityManager()->getStorage('page_variant')->loadByProperties(['page' => $entity->id()]) as $variant) {
+        $variants[$variant->uuid()] = $variant;
+      }
+      $entity->set('display_variants', $variants);
+    }
   }
 
   /**
@@ -351,7 +439,6 @@ class Page extends ConfigEntityBase implements PageInterface {
     // Avoid serializing plugin collections and the page executable as they
     // might contain references to a lot of objects including the container.
     $unset_vars = [
-      'variantCollection' => 'display_variants',
       'accessConditionCollection' => 'access_variants',
       'executable' => NULL,
     ];
@@ -363,8 +450,19 @@ class Page extends ConfigEntityBase implements PageInterface {
         unset($vars[array_search($unset_var, $vars)]);
       }
     }
+    foreach ($this->display_variants as $key => $variant) {
+      $this->display_variants[$key] = serialize($variant);
+    }
 
     return $vars;
   }
 
+  public function __wakeup() {
+    parent::__wakeup();
+    foreach ($this->display_variants as $key => $variant) {
+      $this->display_variants[$key] = unserialize($variant);
+    }
+  }
+
+
 }
diff --git a/src/Entity/PageListBuilder.php b/src/Entity/PageListBuilder.php
index 84a61ed..dbf8f00 100644
--- a/src/Entity/PageListBuilder.php
+++ b/src/Entity/PageListBuilder.php
@@ -40,6 +40,16 @@ class PageListBuilder extends ConfigEntityListBuilder {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getDefaultOperations(EntityInterface $entity) {
+    $operations = parent::getDefaultOperations($entity);
+    $operations['edit']['url'] = new Url('entity.page.edit_form', ['machine_name' => $entity->id(), 'step' => 'general']);
+
+    return $operations;
+  }
+
+  /**
    * Gets the displayable path of a page entity.
    *
    * @param \Drupal\page_manager\PageInterface $entity
@@ -66,12 +76,4 @@ class PageListBuilder extends ConfigEntityListBuilder {
     }
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function render() {
-    $build = parent::render();
-    $build['table']['#empty'] = $this->t('There are currently no pages. <a href="@url">Add a new page.</a>', ['@url' => Url::fromRoute('entity.page.add_form')->toString()]);
-    return $build;
-  }
 }
diff --git a/src/Entity/PageVariant.php b/src/Entity/PageVariant.php
new file mode 100644
index 0000000..7ada8bb
--- /dev/null
+++ b/src/Entity/PageVariant.php
@@ -0,0 +1,159 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\page_manager\Entity\PageVariant.
+ */
+
+namespace Drupal\page_manager\Entity;
+
+use Drupal\Core\Condition\ConditionPluginCollection;
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\page_manager\PageVariantInterface;
+use Drupal\page_manager\Plugin\VariantCollection;
+
+/**
+ * Defines the PageVariant entity.
+ *
+ * @ConfigEntityType(
+ *   id = "page_variant",
+ *   label = @Translation("PageVariant"),
+ *   handlers = {
+ *     "list_builder" = "Drupal\page_manager\Controller\PageVariantListBuilder",
+ *     "form" = {
+ *       "add" = "Drupal\page_manager\Form\PageVariantForm",
+ *       "edit" = "Drupal\page_manager\Form\PageVariantForm",
+ *       "delete" = "Drupal\page_manager\Form\PageVariantDeleteForm"
+ *     }
+ *   },
+ *   config_prefix = "page_variant",
+ *   admin_permission = "administer site configuration",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label",
+ *     "uuid" = "uuid"
+ *   },
+ *   links = {
+ *     "edit-form" = "/admin/config/system/page_variant/{page_variant}",
+ *     "delete-form" = "/admin/config/system/page_variant/{page_variant}/delete",
+ *     "collection" = "/admin/config/system/page_variant"
+ *   }
+ * )
+ */
+class PageVariant extends ConfigEntityBase implements PageVariantInterface {
+  /**
+   * The PageVariant ID.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The PageVariant label.
+   *
+   * @var string
+   */
+  protected $label;
+
+  protected $weight;
+
+  protected $uuid;
+
+  protected $variant;
+
+  protected $variant_settings = [];
+
+  /**
+   * The plugin collection that holds the variants.
+   *
+   * @var \Drupal\page_manager\Plugin\VariantCollection
+   */
+  protected $variantCollection;
+
+  /**
+   * @var \Drupal\page_manager\Entity\Page
+   */
+  protected $page;
+
+  protected $selection_criteria = [];
+
+  protected $selection_logic = 'and';
+
+  /**
+   * The plugin collection that holds the selection criteria conditions.
+   *
+   * @var \Drupal\Component\Plugin\LazyPluginCollection
+   */
+  protected $selectionConditionCollection;
+
+  protected $contexts = [];
+
+  /**
+   * @return \Drupal\Core\Display\VariantInterface
+   */
+  public function getVariant() {
+    if (!$this->variantCollection) {
+      $this->variantCollection = \Drupal::service('plugin.manager.display_variant')->createInstance($this->variant, $this->variant_settings);
+    }
+    return $this->variantCollection;
+  }
+
+  public function getVariantId() {
+    return $this->variant;
+  }
+
+  /**
+   * @return \Drupal\Core\Condition\ConditionPluginCollection.
+   */
+  public function getSelectionCriterion() {
+    if (!$this->selectionConditionCollection) {
+      $this->selectionConditionCollection = new ConditionPluginCollection(\Drupal::service('plugin.manager.condition'), $this->get('selection_criteria'));
+    }
+    return $this->selectionConditionCollection;
+  }
+
+  /**
+   * @return \Drupal\page_manager\Entity\Page
+   */
+  public function getPage() {
+    return $this->page;
+  }
+
+  /**
+   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
+   */
+  public function getContexts() {
+    return array_merge($this->page->getContexts(), $this->contexts);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addSelectionCriteria(array $configuration) {
+    $id = count($this->selection_criteria);
+    $this->getSelectionCriterion()->addInstanceId($id, $configuration);
+    return $id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSelectionCriteria($condition_id) {
+    return $this->getSelectionCriterion()->get($condition_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeSelectionCriteria($condition_id) {
+    $this->getSelectionCriterion()->removeInstanceId($condition_id);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAccessLogic() {
+    return $this->selection_logic;
+  }
+}
diff --git a/src/EventSubscriber/StaticContext.php b/src/EventSubscriber/StaticContext.php
deleted file mode 100644
index e8e55fa..0000000
--- a/src/EventSubscriber/StaticContext.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\EventSubscriber\StaticContext.
- */
-
-namespace Drupal\page_manager\EventSubscriber;
-
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Plugin\Context\ContextDefinition;
-use Drupal\page_manager\Context\EntityLazyLoadContext;
-use Drupal\page_manager\Event\PageManagerContextEvent;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\page_manager\Event\PageManagerEvents;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-/**
- * Adds static context.
- */
-class StaticContext implements EventSubscriberInterface {
-
-  use StringTranslationTrait;
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * Constructs a new StaticContext.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   */
-  public function __construct(EntityManagerInterface $entity_manager) {
-    $this->entityManager = $entity_manager;
-  }
-
-  /**
-   * Adds in the current user as a context.
-   *
-   * @param \Drupal\page_manager\Event\PageManagerContextEvent $event
-   *   The page entity context event.
-   */
-  public function onPageContext(PageManagerContextEvent $event) {
-    $executable = $event->getPageExecutable();
-    $static_contexts = $executable->getPage()->getStaticContexts();
-
-    foreach ($static_contexts as $name => $static_context) {
-      $context = new EntityLazyLoadContext(new ContextDefinition($static_context['type'], $static_context['label']), $this->entityManager, $static_context['value']);
-      $executable->addContext($name, $context);
-    }
-
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getSubscribedEvents() {
-    $events[PageManagerEvents::PAGE_CONTEXT][] = 'onPageContext';
-    return $events;
-  }
-
-}
diff --git a/src/Form/AccessConditionAddForm.php b/src/Form/AccessConditionAddForm.php
deleted file mode 100644
index b653424..0000000
--- a/src/Form/AccessConditionAddForm.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\AccessConditionAddForm.
- */
-
-namespace Drupal\page_manager\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/src/Form/AccessConditionDeleteForm.php b/src/Form/AccessConditionDeleteForm.php
deleted file mode 100644
index 9b61987..0000000
--- a/src/Form/AccessConditionDeleteForm.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\AccessConditionDeleteForm.
- */
-
-namespace Drupal\page_manager\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->urlInfo('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/src/Form/AccessConditionEditForm.php b/src/Form/AccessConditionEditForm.php
deleted file mode 100644
index 4389b60..0000000
--- a/src/Form/AccessConditionEditForm.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\AccessConditionEditForm.
- */
-
-namespace Drupal\page_manager\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/src/Form/AccessConditionFormBase.php b/src/Form/AccessConditionFormBase.php
deleted file mode 100644
index a0dc108..0000000
--- a/src/Form/AccessConditionFormBase.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\AccessConditionFormBase.
- */
-
-namespace Drupal\page_manager\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-
-/**
- * Provides a base form for editing and adding an access condition.
- */
-abstract class AccessConditionFormBase extends ConditionFormBase {
-
-  /**
-   * {@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->urlInfo('edit-form'));
-  }
-
-}
diff --git a/src/Form/AccessConfigure.php b/src/Form/AccessConfigure.php
new file mode 100644
index 0000000..fb317ec
--- /dev/null
+++ b/src/Form/AccessConfigure.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\AccessConfigure;
+ */
+
+namespace Drupal\page_manager\Form;
+
+use Drupal\ctools\Form\ConditionConfigure;
+
+class AccessConfigure extends ConditionConfigure {
+  /**
+   * {@inheritdoc}
+   */
+  protected function getParentRouteInfo() {
+    return ['entity.page.edit_form', ['machine_name' => $this->machine_name, 'step' => 'access']];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRouteInfo($condition) {
+    return ['entity.page.condition.edit', ['machine_name' => $this->machine_name, 'condition' => $condition]];
+  }
+
+  /**
+   * {@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 setConditions($cached_values, $conditions) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $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\Entity\Page */
+    $page = $cached_values['page'];
+    return $page->getParameters();
+  }
+
+}
diff --git a/src/Form/AccessDelete.php b/src/Form/AccessDelete.php
new file mode 100644
index 0000000..1296c78
--- /dev/null
+++ b/src/Form/AccessDelete.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\AccessDelete.
+ */
+
+namespace Drupal\page_manager\Form;
+
+use Drupal\ctools\Form\ConditionDelete;
+
+class AccessDelete extends ConditionDelete {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRouteInfo() {
+    return ['entity.page.edit_form', ['machine_name' => $this->machine_name, 'step' => 'access']];
+  }
+
+  /**
+   * {@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 setConditions($cached_values, $conditions) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $page->set('access_conditions', $conditions);
+    return $cached_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    return $page->getParameters();
+  }
+
+}
diff --git a/src/Form/ConditionFormBase.php b/src/Form/ConditionFormBase.php
deleted file mode 100644
index 7ba10f7..0000000
--- a/src/Form/ConditionFormBase.php
+++ /dev/null
@@ -1,119 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\ConditionFormBase.
- */
-
-namespace Drupal\page_manager\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;
-use Drupal\page_manager\PageInterface;
-
-/**
- * Provides a base form for editing and adding a condition.
- */
-abstract class ConditionFormBase extends FormBase {
-
-  use ContextAwarePluginAssignmentTrait;
-
-  /**
-   * The page entity this condition belongs to.
-   *
-   * @var \Drupal\page_manager\PageInterface
-   */
-  protected $page;
-
-  /**
-   * 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, PageInterface $page = NULL, $condition_id = NULL) {
-    $this->page = $page;
-    $this->condition = $this->prepareCondition($condition_id);
-    $temporary = $form_state->getTemporary();
-    $temporary['gathered_contexts'] = $this->page->getContexts();
-    $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/src/Form/DisplayVariantEditForm.php b/src/Form/DisplayVariantEditForm.php
index 161a696..545aec8 100644
--- a/src/Form/DisplayVariantEditForm.php
+++ b/src/Form/DisplayVariantEditForm.php
@@ -42,7 +42,7 @@ class DisplayVariantEditForm extends DisplayVariantFormBase {
     // Set up the attributes used by a modal to prevent duplication later.
     $attributes = [
       'class' => ['use-ajax'],
-      'data-dialog-type' => 'modal',
+      'data-accepts' => 'application/vnd.drupal-modal',
       'data-dialog-options' => Json::encode([
         'width' => 'auto',
       ]),
diff --git a/src/Form/PageAccessForm.php b/src/Form/PageAccessForm.php
new file mode 100644
index 0000000..897e25b
--- /dev/null
+++ b/src/Form/PageAccessForm.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\PageAccessForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+
+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\Form\AccessConfigure';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.page';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getOperationsRouteInfo($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->getParameters();
+  }
+
+  /**
+   * The route to which condition 'add' actions should submit.
+   *
+   * @return string
+   */
+  protected function getAddRoute() {
+    return 'entity.page.condition.add';
+  }
+}
diff --git a/src/Form/PageContextsForm.php b/src/Form/PageContextsForm.php
new file mode 100644
index 0000000..fefa83d
--- /dev/null
+++ b/src/Form/PageContextsForm.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\PageContextsForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+use Drupal\Core\Form\FormBase;
+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;
+
+class PageContextsForm extends FormBase {
+  /**
+   * {@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');
+    $form['items'] = array(
+      '#type' => 'markup',
+      '#prefix' => '<div id="available-contexts">',
+      '#suffix' => '</div>',
+      '#theme' => 'table',
+      '#header' => array($this->t('Context'), $this->t('Type'), $this->t('Operations')),
+      '#rows' => $this->renderRows($cached_values),
+      '#empty' => t('No Contexts configured for this variant.')
+    );
+    $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\PageVariant */
+    $variant = $cached_values['variant'];
+  }
+
+  protected function renderRows($cached_values) {
+    $contexts = [];
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    foreach ($page->getParameters() as $parameter => $context) {
+      /*$build = array(
+        '#type' => 'operations',
+        '#links' => $this->getOperations('page_manager.route.parameters.configure', $cached_values, $row),
+      );*/
+      /** @var $definition \Drupal\Core\Plugin\Context\ContextDefinitionInterface */
+      $definition = $context->getContextDefinition();
+      $data_definition = $definition->getDataDefinition();
+      if ($data_definition instanceof ComplexDataDefinitionInterface) {
+        foreach ($data_definition->getPropertyDefinitions() as $property => $value) {
+          if ($value instanceof ListDataDefinitionInterface) {
+            //drupal_set_message('List ' . $property . ': ' . var_export($value->getItemDefinition()->getDataType(), TRUE));
+            //drupal_set_message(var_export($value->getItemDefinition(), TRUE));
+            $definition = \Drupal::typedDataManager()->create($value->getItemDefinition());
+            drupal_set_message(var_export($definition, TRUE));
+          }
+          else {
+            //drupal_set_message('Primative ' . $property . ': ' . var_export($value->getDataType(), TRUE));
+          }
+        }
+      }
+
+      $contexts[] = [
+        $context->getContextDefinition()->getLabel(),
+        $context->getContextDefinition()->getDataType(),
+        ''
+      ];
+    }
+    return $contexts;
+  }
+
+  protected function getOperations(ContextDefinitionInterface $context) {
+
+  }
+}
\ No newline at end of file
diff --git a/src/Form/PageDeleteForm.php b/src/Form/PageDeleteForm.php
index b98c17e..66afe60 100644
--- a/src/Form/PageDeleteForm.php
+++ b/src/Form/PageDeleteForm.php
@@ -27,7 +27,7 @@ class PageDeleteForm extends EntityConfirmFormBase {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return new Url('page_manager.page_list');
+    return new Url('entity.page.collection');
   }
 
   /**
diff --git a/src/Form/PageEditForm.php b/src/Form/PageEditForm.php
index 5e098d7..f0f15fb 100644
--- a/src/Form/PageEditForm.php
+++ b/src/Form/PageEditForm.php
@@ -9,11 +9,9 @@ namespace Drupal\page_manager\Form;
 
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\NestedArray;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
-use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Provides a form for editing a page entity.
@@ -33,7 +31,7 @@ class PageEditForm extends PageFormBase {
     ];
     $attributes = [
       'class' => ['use-ajax'],
-      'data-dialog-type' => 'modal',
+      'data-accepts' => 'application/vnd.drupal-modal',
       'data-dialog-options' => Json::encode([
         'width' => 'auto',
       ]),
@@ -46,77 +44,6 @@ class PageEditForm extends PageFormBase {
       ]
     ]);
 
-    $form['context'] = [
-      '#type' => 'details',
-      '#title' => $this->t('Available context'),
-      '#open' => TRUE,
-    ];
-    $form['context']['add'] = [
-      '#type' => 'link',
-      '#title' => $this->t('Add new static context'),
-      '#url' => Url::fromRoute('page_manager.static_context_add', [
-        'page' => $this->entity->id(),
-      ]),
-      '#attributes' => $add_button_attributes,
-      '#attached' => [
-        'library' => [
-          'core/drupal.ajax',
-        ],
-      ],
-    ];
-    $form['context']['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 = $this->entity->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 ($this->entity->getStaticContext($name)) {
-        $operations['edit'] = [
-          'title' => $this->t('Edit'),
-          'url' => Url::fromRoute('page_manager.static_context_edit', [
-            'page' => $this->entity->id(),
-            'name' => $name,
-          ]),
-          'attributes' => $attributes,
-        ];
-        $operations['delete'] = [
-          'title' => $this->t('Delete'),
-          'url' => Url::fromRoute('page_manager.static_context_delete', [
-            'page' => $this->entity->id(),
-            'name' => $name,
-          ]),
-          'attributes' => $attributes,
-        ];
-      }
-      $row['operations'] = [
-        '#type' => 'operations',
-        '#links' => $operations,
-      ];
-
-      $form['context']['available_context'][$name] = $row;
-    }
-
     $form['display_variant_section'] = [
       '#type' => 'details',
       '#title' => $this->t('Display variants'),
@@ -274,7 +201,7 @@ class PageEditForm extends PageFormBase {
     }
     parent::save($form, $form_state);
     drupal_set_message($this->t('The %label page has been updated.', ['%label' => $this->entity->label()]));
-    $form_state->setRedirect('page_manager.page_list');
+    $form_state->setRedirect('entity.page.collection');
   }
 
 }
diff --git a/src/Form/PageFormBase.php b/src/Form/PageFormBase.php
deleted file mode 100644
index ae20c31..0000000
--- a/src/Form/PageFormBase.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\PageFormBase.
- */
-
-namespace Drupal\page_manager\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/src/Form/PageGeneralForm.php b/src/Form/PageGeneralForm.php
new file mode 100644
index 0000000..a6417d5
--- /dev/null
+++ b/src/Form/PageGeneralForm.php
@@ -0,0 +1,171 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\PageGeneralForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Display\VariantManager;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+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 DisplayVariantAddForm.
+   *
+   * @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(),
+    ];
+    $variants = [];
+    foreach ($this->variantManager->getDefinitions() as $plugin_id => $definition) {
+      $variants[$plugin_id] = $definition['admin_label'];
+    }
+    $form['display_variant'] = [
+      '#title' => $this->t('Variant type'),
+      '#type' => 'select',
+      '#options' => $variants,
+      '#default_value' => !empty($cached_values['display_variant']) ? $cached_values['display_variant'] : '',
+    ];
+    $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' => [
+        'access' => $this->t('Access controls'),
+        'menu' => $this->t('Visible menu item'),
+        'selection' => $this->t('Selection rules'),
+        //'contexts' => $this->t('Contexts'),
+      ],
+      '#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('id', $form_state->getValue('id'));
+    $page->set('label', $form_state->getValue('label'));
+    $page->set('description', $form_state->getValue('description'));
+    $page->set('path', $form_state->getValue('path'));
+    $parameters = PageParameterForm::getParameters($cached_values);
+    // If we have no url parameters, explicitly reset the parameters in the
+    // cached values.
+    if (!$parameters) {
+      $page->set('parameters', []);
+    }
+    $page->set('use_admin_theme', $form_state->getValue('use_admin_theme'));
+
+    if (empty($cached_values['display_variant'])) {
+      $cached_values['variant'] = \Drupal::entityManager()
+        ->getStorage('page_variant')
+        ->create([
+          'variant' => $form_state->getValue('display_variant'),
+          'page' => $page->id(),
+          'id' => "{$page->id()}-{$form_state->getValue('display_variant')}-0",
+        ]);
+      $cached_values['display_variant'] = $form_state->getValue('display_variant');
+    }
+    if ($cached_values['display_variant'] != $form_state->getValue('display_variant') && !empty($cached_values['variant'])) {
+      $variant = $cached_values['variant'];
+      /** @var $variant \Drupal\page_manager\Entity\PageVariant */
+      $variant->set('variant', $form_state->getValue('display_variant'));
+      $variant->set('variant_settings', []);
+      $cached_values['display_variant'] = $form_state->getValue('display_variant');
+    }
+
+    $cached_values['wizard_options'] = $form_state->getValue('wizard_options');
+    $form_state->setTemporaryValue('wizard', $cached_values);
+  }
+
+  /**
+   * {@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/src/Form/PageMenuForm.php b/src/Form/PageMenuForm.php
new file mode 100644
index 0000000..8129387
--- /dev/null
+++ b/src/Form/PageMenuForm.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\PageMenuForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\RedirectCommand;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\page_manager\Plugin\PageManagerMenuManager;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class PageMenuForm extends FormBase {
+
+  /**
+   * @var \Drupal\page_manager\Plugin\PageManagerMenuManager
+   */
+  protected $manager;
+
+  /**
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('plugin.manager.page_manager_menu'), $container->get('user.shared_tempstore'));
+  }
+
+  function __construct(PageManagerMenuManager $manager, SharedTempStoreFactory $tempstore) {
+    $this->manager = $manager;
+    $this->tempstore = $tempstore;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_menu_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'];
+    // Set the machine_name into the form_state for later use.
+    $form_state->set('machine_name', $page->id());
+    $form['#tree'] = TRUE;
+    $options = [
+      'route' => $this->t('No menu entry'),
+    ];
+    foreach($this->manager->getDefinitions() as $plugin_id => $definition) {
+      $options[$plugin_id] = $definition['label'];
+    }
+    $form['menu_type'] = [
+      '#type' => 'radios',
+      '#title' => $this->t('Menu type'),
+      '#options' => $options,
+      '#default_value' => $page->getMenuType(),
+      '#ajax' => [
+        'callback' => [$this, 'menuSettings'],
+      ]
+    ];
+    if ($page->getMenuType() != 'route') {
+      // @todo remove the array ternary when the entity is complete with defaults.
+      $instance = $this->manager->createInstance($page->getMenuType(), $page->getMenuSettings());
+      $form['instance'] = [
+        '#type' => 'value',
+        '#value' => $instance,
+      ];
+      $form['menu_settings'] = $instance->buildConfigurationForm([], $form_state);
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    if ($form_state->hasValue('menu_settings')) {
+      $settings = (new FormState())->setValues($form_state->getValue('menu_settings'));
+      $instance = $form_state->getValue('instance');
+      $instance->validateConfigurationForm($form, $settings);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    if ($form_state->hasValue('menu_settings')) {
+      $cached_values = $form_state->getTemporaryValue('wizard');
+      /** @var $page \Drupal\page_manager\Entity\Page */
+      $page = $cached_values['page'];
+      $settings = (new FormState())->setValues($form_state->getValue('menu_settings'));
+      $instance = $form_state->getValue('instance');
+      $instance->submitConfigurationForm($form, $settings);
+      $page->set('menu_settings', $instance->getConfiguration());
+      $form_state->setTemporaryValue('wizard', $cached_values);
+    }
+  }
+
+  /**
+   * Ajax menu settings rendering.
+   */
+  public function menuSettings(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $page->set('menu_type', $form_state->getValue('menu_type'));
+    $this->tempstore->get('page_manager.page')->set($cached_values['id'], $cached_values);
+    $response = new AjaxResponse();
+    $response->addCommand(new RedirectCommand($this->url('entity.page.edit_form', ['machine_name' => $cached_values['id'], 'step' => 'menu'])));
+    return $response;
+  }
+
+}
diff --git a/src/Form/PageParameterForm.php b/src/Form/PageParameterForm.php
new file mode 100644
index 0000000..da871b5
--- /dev/null
+++ b/src/Form/PageParameterForm.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\PageParameterForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Routing\RouteCompiler;
+use Drupal\Core\Url;
+
+class PageParameterForm extends FormBase {
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_parameter_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $form['items'] = array(
+      '#type' => 'markup',
+      '#prefix' => '<div id="configured-parameters">',
+      '#suffix' => '</div>',
+      '#theme' => 'table',
+      '#header' => array($this->t('Argument'), $this->t('Position in path'), $this->t('Context assigned'), $this->t('Operations')),
+      '#rows' => $this->renderRows($cached_values),
+      '#empty' => t('No required conditions have been configured.')
+    );
+    $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
+    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'];
+    $parameters = PageParameterForm::getParameters($cached_values);
+    // Unspecified parameters need a generic "any" context definition.
+    foreach (array_keys($parameters) as $key) {
+      if (!$page->hasParameter($key)) {
+        $page->setParameter($key, new Context(new ContextDefinition('any', $this->t('Anything passed to this parameter will be used as a context.'))));
+      }
+    }
+    // Stale parameters should be removed from the page.
+    foreach($page->getParameters() as $key => $context) {
+      if (array_search($key, array_keys($parameters)) === FALSE) {
+        $page->deleteParameter($key);
+      }
+    }
+    $form_state->setTemporaryValue('wizard', $cached_values);
+  }
+
+  protected function renderRows($cached_values) {
+    $configured_parameters = array();
+    foreach (PageParameterForm::getParameters($cached_values) as $row => $parameter) {
+      $build = array(
+        '#type' => 'operations',
+        '#links' => $this->getOperations('page_manager.route.parameters.configure', $cached_values, $row),
+      );
+      $configured_parameters[] = array(
+        $parameter['argument'],
+        $parameter['placement'],
+        $this->getDescription($cached_values, $row),
+        'operations' => [
+          'data' => $build,
+        ],
+      );
+    }
+    return $configured_parameters;
+  }
+
+  public static function getParameters($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $matches = PageParameterForm::getPathParts($page->getPath());
+    $outline = RouteCompiler::getPatternOutline($page->getPath());
+    $outline = explode('/', trim($outline, '/'));
+    $parameters = [];
+    foreach ($outline as $placement => $value) {
+      if ($value == '%') {
+        $argument = array_shift($matches[0]);
+        $parameters[trim($argument, '{}')] = [
+          'placement' => $placement,
+          'argument' => $argument,
+        ];
+      }
+    }
+    return $parameters;
+  }
+
+  public static function getPathParts($path) {
+    $matches = [];
+    preg_match_all('|\{\w+\}|', $path, $matches);
+    return $matches;
+  }
+
+  protected function getOperations($route_name, $cached_values, $parameter) {
+    $operations = [];
+    $operations['assign'] = array(
+      'title' => t('Assign Context'),
+      'url' => new Url($route_name, ['js' => 'nojs', 'machine_name' => $cached_values['id'], 'step' => 'assign', 'parameter' => $parameter]),
+      'weight' => 10,
+      'attributes' => array(
+        'class' => array('use-ajax'),
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => Json::encode([
+          'width' => 700,
+        ]),
+      ),
+    );
+    $operations['settings'] = array(
+      'title' => t('Settings'),
+      'url' => new Url($route_name, ['js' => 'nojs', 'machine_name' => $cached_values['id'], 'step' => 'settings', 'parameter' => $parameter]),
+      'weight' => 10,
+      'attributes' => array(
+        'class' => array('use-ajax'),
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => Json::encode([
+          'width' => 700,
+        ]),
+      ),
+    );
+    return $operations;
+  }
+
+  protected function getDescription($cached_values, $row) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $parameter_settings = $page->getParameter($row);
+    return [
+      'data' => [
+        '#markup' => $parameter_settings->getContextDefinition()->getDataType() . '<br />' .
+          $parameter_settings->getContextDefinition()->getLabel(),
+      ],
+    ];
+  }
+
+}
diff --git a/src/Form/PageSelectionForm.php b/src/Form/PageSelectionForm.php
new file mode 100644
index 0000000..1212952
--- /dev/null
+++ b/src/Form/PageSelectionForm.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\PageAccessForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+
+use Drupal\ctools\Form\ManageConditions;
+
+class PageSelectionForm extends ManageConditions {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_access_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditionClass() {
+    return 'Drupal\page_manager\Form\SelectionConfigure';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getTempstoreId() {
+    return 'page_manager.page';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getOperationsRouteInfo($machine_name, $row) {
+    return ['entity.page_variant.condition', ['machine_name' => $machine_name, 'condition' => $row]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditions($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\PageVariant */
+    $variant = $cached_values['variant'];
+    return $variant->get('selection_criteria');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    return $page->getParameters();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAddRoute() {
+    return 'entity.page_variant.condition.add';
+  }
+
+}
diff --git a/src/Form/PageVariantConfigureForm.php b/src/Form/PageVariantConfigureForm.php
new file mode 100644
index 0000000..f2c4084
--- /dev/null
+++ b/src/Form/PageVariantConfigureForm.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\PageVariant\ConfigureForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+class PageVariantConfigureForm extends FormBase {
+  /**
+   * Returns a unique string identifying the form.
+   *
+   * @return string
+   *   The unique string identifying the form.
+   */
+  public function getFormId() {
+    // @todo this should vary by step/variant plugin id.
+    return 'page_manage_variant_configure_form';
+  }
+
+  /**
+   * Form constructor.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   The form structure.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page_variant \Drupal\page_manager\Entity\PageVariant */
+    $page_variant = $cached_values['variant'];
+    $variant = $page_variant->getVariant();
+    return $variant->buildConfigurationForm($form, $form_state);
+  }
+
+  /**
+   * Form submission handler.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  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['variant'];
+    $variant = $page_variant->getVariant();
+    $variant->submitConfigurationForm($form, $form_state);
+    $configuration = $variant->getConfiguration();
+    $page_variant->set('variant_settings', $configuration);
+    $page_variant->set('label', $configuration['label']);
+    $page->addVariantEntity($page_variant);
+  }
+
+}
diff --git a/src/Form/PageVariantDeleteForm.php b/src/Form/PageVariantDeleteForm.php
new file mode 100644
index 0000000..ab2cde0
--- /dev/null
+++ b/src/Form/PageVariantDeleteForm.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\page_manager\Form\PageVariantDeleteForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+use Drupal\Core\Entity\EntityConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Builds the form to delete a PageVariant.
+ */
+class PageVariantDeleteForm extends EntityConfirmFormBase {
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to delete %name?', array('%name' => $this->entity->label()));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return new Url('entity.page_variant.collection');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->entity->delete();
+
+    drupal_set_message(
+      $this->t('content @type: deleted @label.',
+        [
+          '@type' => $this->entity->bundle(),
+          '@label' => $this->entity->label()
+        ]
+        )
+    );
+
+    $form_state->setRedirectUrl($this->getCancelUrl());
+  }
+
+}
diff --git a/src/Form/PageVariantForm.php b/src/Form/PageVariantForm.php
new file mode 100644
index 0000000..e2409ca
--- /dev/null
+++ b/src/Form/PageVariantForm.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\page_manager\Form\PageVariantForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Class PageVariantForm.
+ *
+ * @package Drupal\page_manager\Form
+ */
+class PageVariantForm extends EntityForm {
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, FormStateInterface $form_state) {
+    $form = parent::form($form, $form_state);
+
+    $page_variant = $this->entity;
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Label'),
+      '#maxlength' => 255,
+      '#default_value' => $page_variant->label(),
+      '#description' => $this->t("Label for the PageVariant."),
+      '#required' => TRUE,
+    );
+
+    $form['id'] = array(
+      '#type' => 'machine_name',
+      '#default_value' => $page_variant->id(),
+      '#machine_name' => array(
+        'exists' => '\Drupal\page_manager\Entity\PageVariant::load',
+      ),
+      '#disabled' => !$page_variant->isNew(),
+    );
+
+    /* You will need additional form elements for your custom properties. */
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, FormStateInterface $form_state) {
+    $page_variant = $this->entity;
+    $status = $page_variant->save();
+
+    if ($status) {
+      drupal_set_message($this->t('Saved the %label PageVariant.', array(
+        '%label' => $page_variant->label(),
+      )));
+    }
+    else {
+      drupal_set_message($this->t('The %label PageVariant was not saved.', array(
+        '%label' => $page_variant->label(),
+      )));
+    }
+    $form_state->setRedirectUrl($page_variant->urlInfo('collection'));
+  }
+
+}
diff --git a/src/Form/ParameterAssignContextForm.php b/src/Form/ParameterAssignContextForm.php
new file mode 100644
index 0000000..585e5b3
--- /dev/null
+++ b/src/Form/ParameterAssignContextForm.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\ParameterAssignContextForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class ParameterAssignContextForm extends FormBase {
+
+  /**
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $manager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('entity.manager'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function __construct(EntityManagerInterface $manager) {
+    $this->manager = $manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_parameter_context_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $parameter = $form_state->getBuildInfo()['args'][0];
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $options = [];
+    foreach ($this->manager->getDefinitions() as $entity_type => $definition) {
+      $options[$entity_type] = $definition->getLabel();
+    }
+    $parameter_settings = $page->getParameter($parameter);
+    $form['context'] = [
+      '#type' => 'radios',
+      '#title' => $this->t('Context assignment'),
+      '#options' => $options,
+      '#required' => TRUE,
+      '#default_value' => substr($parameter_settings->getContextDefinition()->getDataType(), 7),
+    ];
+    $form['parameter'] = [
+      '#type' => 'value',
+      '#value' => $parameter,
+    ];
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $parameter = $form_state->getValue('parameter');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $context = $form_state->getValue('context');
+    $parameter_settings = $page->getParameter($parameter);
+    $parameter_settings->getContextDefinition()->setDataType('entity:' . $context);
+    $page->setParameter($parameter, $parameter_settings);
+    $form_state->setTemporaryValue('wizard', $cached_values);
+  }
+
+}
diff --git a/src/Form/ParameterSettingsForm.php b/src/Form/ParameterSettingsForm.php
new file mode 100644
index 0000000..c25d88a
--- /dev/null
+++ b/src/Form/ParameterSettingsForm.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\ParameterSettingsForm.
+ */
+
+namespace Drupal\page_manager\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+class ParameterSettingsForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_manager_parameter_settings_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $parameter = $form_state->getBuildInfo()['args'][0];
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $form['parameter'] = [
+      '#type' => 'value',
+      '#value' => $parameter,
+    ];
+    $parameter_settings = $page->getParameter($parameter);
+    $form['identifier'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Context identifier'),
+      '#required' => TRUE,
+      '#default_value' => $parameter_settings->getContextDefinition()->getLabel(),
+    ];
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $parameter = $form_state->getValue('parameter');
+    /** @var $page \Drupal\page_manager\Entity\Page */
+    $page = $cached_values['page'];
+    $identifier = $form_state->getValue('identifier');
+    $parameter_settings = $page->getParameter($parameter);
+    $parameter_settings->getContextDefinition()->setLabel($identifier);
+    $page->setParameter($parameter, $parameter_settings);
+    $form_state->setTemporaryValue('wizard', $cached_values);
+  }
+
+}
diff --git a/src/Form/SelectionConditionAddForm.php b/src/Form/SelectionConditionAddForm.php
deleted file mode 100644
index f30d423..0000000
--- a/src/Form/SelectionConditionAddForm.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\SelectionConditionAddForm.
- */
-
-namespace Drupal\page_manager\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/src/Form/SelectionConditionDeleteForm.php b/src/Form/SelectionConditionDeleteForm.php
deleted file mode 100644
index 0b2f406..0000000
--- a/src/Form/SelectionConditionDeleteForm.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\SelectionConditionDeleteForm.
- */
-
-namespace Drupal\page_manager\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageInterface;
-use Drupal\Core\Form\ConfirmFormBase;
-use Drupal\Core\Url;
-
-/**
- * @todo.
- */
-class SelectionConditionDeleteForm extends ConfirmFormBase {
-
-  /**
-   * The page entity this selection condition belongs to.
-   *
-   * @var \Drupal\page_manager\PageInterface
-   */
-  protected $page;
-
-  /**
-   * The display variant.
-   *
-   * @var \Drupal\page_manager\Plugin\ConditionVariantInterface
-   */
-  protected $displayVariant;
-
-  /**
-   * 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 new Url('page_manager.display_variant_edit', [
-      'page' => $this->page->id(),
-      'display_variant_id' => $this->displayVariant->id(),
-    ]);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfirmText() {
-    return $this->t('Delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $display_variant_id = NULL, $condition_id = NULL) {
-    $this->page = $page;
-    $this->displayVariant = $this->page->getVariant($display_variant_id);
-    $this->selectionCondition = $this->displayVariant->getSelectionCondition($condition_id);
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $this->displayVariant->removeSelectionCondition($this->selectionCondition->getConfiguration()['uuid']);
-    $this->page->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/src/Form/SelectionConditionEditForm.php b/src/Form/SelectionConditionEditForm.php
deleted file mode 100644
index 59b6011..0000000
--- a/src/Form/SelectionConditionEditForm.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\SelectionConditionEditForm.
- */
-
-namespace Drupal\page_manager\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 display variant.
-    return $this->displayVariant->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/src/Form/SelectionConditionFormBase.php b/src/Form/SelectionConditionFormBase.php
deleted file mode 100644
index 1244558..0000000
--- a/src/Form/SelectionConditionFormBase.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\SelectionConditionFormBase.
- */
-
-namespace Drupal\page_manager\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageInterface;
-
-/**
- * Provides a base form for editing and adding a selection condition.
- */
-abstract class SelectionConditionFormBase extends ConditionFormBase {
-
-  /**
-   * The display variant.
-   *
-   * @var \Drupal\page_manager\Plugin\ConditionVariantInterface
-   */
-  protected $displayVariant;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $display_variant_id = NULL, $condition_id = NULL) {
-    $this->displayVariant = $page->getVariant($display_variant_id);
-    return parent::buildForm($form, $form_state, $page, $condition_id);
-  }
-
-  /**
-   * {@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->displayVariant->addSelectionCondition($configuration);
-    }
-
-    // Save the page entity.
-    $this->page->save();
-
-    $form_state->setRedirect('page_manager.display_variant_edit', [
-      'page' => $this->page->id(),
-      'display_variant_id' => $this->displayVariant->id(),
-    ]);
-  }
-
-}
diff --git a/src/Form/SelectionConfigure.php b/src/Form/SelectionConfigure.php
new file mode 100644
index 0000000..2282b3b
--- /dev/null
+++ b/src/Form/SelectionConfigure.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\AccessConfigure;
+ */
+
+namespace Drupal\page_manager\Form;
+
+use Drupal\ctools\Form\ConditionConfigure;
+
+class SelectionConfigure extends ConditionConfigure {
+  /**
+   * {@inheritdoc}
+   */
+  protected function getParentRouteInfo() {
+    return ['entity.page.edit_form', ['machine_name' => $this->machine_name, 'step' => 'selection']];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRouteInfo($condition) {
+    return ['entity.page_variant.condition.edit', ['machine_name' => $this->machine_name, 'condition' => $condition]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditions($cached_values) {
+    /** @var $variant \Drupal\page_manager\Entity\PageVariant */
+    $variant = $cached_values['variant'];
+    return $variant->get('selection_criteria');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setConditions($cached_values, $conditions) {
+    /** @var $variant \Drupal\page_manager\Entity\PageVariant */
+    $variant = $cached_values['variant'];
+    $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->getParameters();
+  }
+
+}
diff --git a/src/Form/SelectionDelete.php b/src/Form/SelectionDelete.php
new file mode 100644
index 0000000..43b215f
--- /dev/null
+++ b/src/Form/SelectionDelete.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Form\AccessDelete.
+ */
+
+namespace Drupal\page_manager\Form;
+
+use Drupal\ctools\Form\ConditionDelete;
+
+class SelectionDelete extends ConditionDelete {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRouteInfo() {
+    return ['entity.page.edit_form', ['machine_name' => $this->machine_name, 'step' => 'selection']];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConditions($cached_values) {
+    /** @var $variant \Drupal\page_manager\Entity\PageVariant */
+    $variant = $cached_values['variant'];
+    return $variant->get('selection_criteria');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setConditions($cached_values, $conditions) {
+    /** @var $variant \Drupal\page_manager\Entity\PageVariant */
+    $variant = $cached_values['variant'];
+    $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->getParameters();
+  }
+
+}
diff --git a/src/Form/StaticContextAddForm.php b/src/Form/StaticContextAddForm.php
deleted file mode 100644
index 91d5177..0000000
--- a/src/Form/StaticContextAddForm.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\StaticContextAddForm.
- */
-
-namespace Drupal\page_manager\Form;
-
-use Drupal\Core\Condition\ConditionManager;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides a form for adding a new static context.
- */
-class StaticContextAddForm extends StaticContextFormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_manager_static_context_add_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitButtonText() {
-    return $this->t('Add Static Context');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitMessageText() {
-    return $this->t('The %label static context has been added.', ['%label' => $this->staticContext['label']]);
-  }
-
-}
diff --git a/src/Form/StaticContextDeleteForm.php b/src/Form/StaticContextDeleteForm.php
deleted file mode 100644
index 425b699..0000000
--- a/src/Form/StaticContextDeleteForm.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\StaticContextDeleteForm.
- */
-
-namespace Drupal\page_manager\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 StaticContextDeleteForm extends ConfirmFormBase {
-
-  /**
-   * The page entity this selection condition belongs to.
-   *
-   * @var \Drupal\page_manager\PageInterface
-   */
-  protected $page;
-
-  /**
-   * The static context's machine name.
-   *
-   * @var array
-   */
-  protected $staticContext;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_manager_static_context_delete_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQuestion() {
-    return $this->t('Are you sure you want to delete the static context %label?', ['%label' => $this->page->getStaticContext($this->staticContext)['label']]);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCancelUrl() {
-    return $this->page->urlInfo('edit-form');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfirmText() {
-    return $this->t('Delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $name = NULL) {
-    $this->page = $page;
-    $this->staticContext = $name;
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    drupal_set_message($this->t('The static context %label has been removed.', ['%label' => $this->page->getStaticContext($this->staticContext)['label']]));
-    $this->page->removeStaticContext($this->staticContext);
-    $this->page->save();
-    $form_state->setRedirectUrl($this->getCancelUrl());
-  }
-
-}
diff --git a/src/Form/StaticContextEditForm.php b/src/Form/StaticContextEditForm.php
deleted file mode 100644
index 8cf3a3b..0000000
--- a/src/Form/StaticContextEditForm.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\StaticContextEditForm.
- */
-
-namespace Drupal\page_manager\Form;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageInterface;
-
-/**
- * Provides a form for adding a new static context.
- */
-class StaticContextEditForm extends StaticContextFormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'page_manager_static_context_edit_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitButtonText() {
-    return $this->t('Update Static Context');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function submitMessageText() {
-    return $this->t('The %label static context has been updated.', ['%label' => $this->staticContext['label']]);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $name = '') {
-    $form = parent::buildForm($form, $form_state, $page, $name);
-    // The machine name of an existing context is read-only.
-    $form['machine_name'] = array(
-      '#type' => 'value',
-      '#value' => $name,
-    );
-    return $form;
-  }
-
-}
diff --git a/src/Form/StaticContextFormBase.php b/src/Form/StaticContextFormBase.php
deleted file mode 100644
index 38d2967..0000000
--- a/src/Form/StaticContextFormBase.php
+++ /dev/null
@@ -1,236 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Form\StaticContextFormBase.
- */
-
-namespace Drupal\page_manager\Form;
-
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Entity\Entity;
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides a base form for editing and adding an access condition.
- */
-abstract class StaticContextFormBase extends FormBase {
-
-  /**
-   * The page entity this static context belongs to.
-   *
-   * @var \Drupal\page_manager\PageInterface
-   */
-  protected $page;
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * The static context configuration.
-   *
-   * @var array
-   */
-  protected $staticContext;
-
-  /**
-   * Construct a new StaticContextFormBase.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   */
-  public function __construct(EntityManagerInterface $entity_manager) {
-    $this->entityManager = $entity_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity.manager')
-    );
-  }
-
-  /**
-   * 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, PageInterface $page = NULL, $name = '') {
-    $this->page = $page;
-    $this->staticContext = $this->page->getStaticContext($name);
-
-    // Allow the condition to add to the form.
-    $form['label'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('Label'),
-      '#default_value' => isset($this->staticContext['label']) ? $this->staticContext['label'] : '',
-      '#required' => TRUE,
-    ];
-    $form['machine_name'] = [
-      '#type' => 'machine_name',
-      '#maxlength' => 64,
-      '#required' => TRUE,
-      '#machine_name' => [
-        'exists' => [$this, 'contextExists'],
-        'source' => ['label'],
-      ],
-      '#default_value' => $name,
-    ];
-    $form['entity_type'] = [
-      '#type' => 'select',
-      '#title' => $this->t('Entity type'),
-      '#options' => $this->entityManager->getEntityTypeLabels(TRUE),
-      '#limit_validation_errors' => array(array('entity_type')),
-      '#submit' => ['::rebuildSubmit'],
-      '#executes_submit_callback' => TRUE,
-      '#ajax' => array(
-        'callback' => '::updateEntityType',
-        'wrapper' => 'add-static-context-wrapper',
-        'method' => 'replace',
-      ),
-    ];
-
-    $entity = NULL;
-    if ($form_state->hasValue('entity_type')) {
-      $entity_type = $form_state->getValue('entity_type');
-      if ($this->staticContext['value']) {
-        $entity = $this->entityManager->loadEntityByUuid($entity_type, $this->staticContext['value']);
-      }
-    }
-    elseif (!empty($this->staticContext['type'])) {
-      list(, $entity_type) = explode(':', $this->staticContext['type']);
-      $entity = $this->entityManager->loadEntityByUuid($entity_type, $this->staticContext['value']);
-    }
-    elseif ($this->entityManager->hasDefinition('node')) {
-      $entity_type = 'node';
-    }
-    else {
-      $entity_type = 'user';
-    }
-
-    $form['entity_type']['#default_value'] = $entity_type;
-
-    $form['selection'] = [
-      '#type' => 'entity_autocomplete',
-      '#prefix' => '<div id="add-static-context-wrapper">',
-      '#suffix' => '</div>',
-      '#required' => TRUE,
-      '#target_type' => $entity_type,
-      '#default_value' => $entity,
-      '#title' => $this->t('Select entity'),
-    ];
-
-    $form['actions'] = ['#type' => 'actions'];
-    $form['actions']['submit'] = [
-      '#type' => 'submit',
-      '#value' => $this->submitButtonText(),
-      '#button_type' => 'primary',
-    ];
-
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $selection = $form_state->getValue('selection');
-    $entity_type = $form_state->getValue('entity_type');
-    $entity = $this->getEntityFromSelection($entity_type, $selection);
-
-    $this->staticContext = [
-      'label' => $form_state->getValue('label'),
-      'type' => 'entity:' . $entity_type,
-      'value' => $entity->uuid(),
-    ];
-    $this->page->setStaticContext($form_state->getValue('machine_name'), $this->staticContext);
-    $this->page->save();
-
-    // Set the submission message.
-    drupal_set_message($this->submitMessageText());
-
-    $form_state->setRedirectUrl($this->page->urlInfo('edit-form'));
-  }
-
-  /**
-   * Get the entity from the selection.
-   *
-   * @param string $selection
-   *   The value from the selection box.
-   *
-   * @return \Drupal\Core\Entity\Entity|null
-   *   The entity reference in selection.
-   */
-  protected function getEntityFromSelection($entity_type, $selection) {
-    if (!isset($selection)) {
-      return NULL;
-    }
-    return $this->entityManager->getStorage($entity_type)->load($selection);
-  }
-
-  /**
-   * Determines if a context with that name already exists.
-   *
-   * @param string $name
-   *   The context name
-   *
-   * @return bool
-   *   TRUE if the format exists, FALSE otherwise.
-   */
-  public function contextExists($name) {
-    return isset($this->page->getContexts()[$name]);
-  }
-
-  /**
-   * Submit handler for the entity_type select field.
-   *
-   * @param array $form
-   *   The form array.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The form state object.
-   *
-   * @return $this
-   */
-  public function rebuildSubmit($form, FormStateInterface $form_state) {
-    return $form_state->setRebuild();
-  }
-
-  /**
-   * AJAX callback for the entity_type select field.
-   *
-   * @param array $form
-   *   The form array.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The form state object.
-   *
-   * @return array
-   *   The updated entity auto complete widget on the form.
-   */
-  public function updateEntityType($form, FormStateInterface $form_state) {
-    return $form['selection'];
-  }
-
-}
diff --git a/src/PageExecutable.php b/src/PageExecutable.php
index bcf722c..04e2388 100644
--- a/src/PageExecutable.php
+++ b/src/PageExecutable.php
@@ -60,8 +60,10 @@ class PageExecutable implements PageExecutableInterface {
    * {@inheritdoc}
    */
   public function selectDisplayVariant() {
+    //drupal_set_message(var_export($this->page->toArray(), TRUE));
     if (!$this->selectedDisplayVariant) {
-      foreach ($this->page->getVariants() as $display_variant) {
+      foreach ($this->page->getVariants() as $variant) {
+        $display_variant = $variant->getVariant();
         if ($display_variant instanceof ContextAwareVariantInterface) {
           $display_variant->setContexts($this->getContexts());
         }
diff --git a/src/PageInterface.php b/src/PageInterface.php
index 59bc383..9726a13 100644
--- a/src/PageInterface.php
+++ b/src/PageInterface.php
@@ -17,6 +17,14 @@ use Drupal\page_manager\Plugin\VariantAwareInterface;
 interface PageInterface extends ConfigEntityInterface, EntityWithPluginCollectionInterface, VariantAwareInterface {
 
   /**
+   * 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
@@ -48,6 +56,11 @@ interface PageInterface extends ConfigEntityInterface, EntityWithPluginCollectio
   public function usesAdminTheme();
 
   /**
+   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
+   */
+  public function getParameters();
+
+  /**
    * Returns the conditions used for determining access for this page entity.
    *
    * @return \Drupal\Core\Condition\ConditionInterface[]|\Drupal\Core\Condition\ConditionPluginCollection
@@ -96,47 +109,6 @@ interface PageInterface extends ConfigEntityInterface, EntityWithPluginCollectio
   public function getAccessLogic();
 
   /**
-   * Returns the static context configurations for this page entity.
-   *
-   * @return array[]
-   *   An array of static context configurations.
-   */
-  public function getStaticContexts();
-
-  /**
-   * Retrieves a specific static context.
-   *
-   * @param string $name
-   *   The static context unique name.
-   *
-   * @return array
-   *   The configuration array of the static context
-   */
-  public function getStaticContext($name);
-
-  /**
-   * Adds/updates a given static context.
-   *
-   * @param string $name
-   *   The static context unique machine name.
-   * @param array $configuration
-   *   A new array of configuration for the static context.
-   *
-   * @return $this
-   */
-  public function setStaticContext($name, $configuration);
-
-  /**
-   * Removes a specific static context.
-   *
-   * @param string $name
-   *   The static context unique name.
-   *
-   * @return $this
-   */
-  public function removeStaticContext($name);
-
-  /**
    * Gets the values for all defined contexts.
    *
    * @return \Drupal\Component\Plugin\Context\ContextInterface[]
diff --git a/src/PageVariantInterface.php b/src/PageVariantInterface.php
new file mode 100644
index 0000000..5db75f8
--- /dev/null
+++ b/src/PageVariantInterface.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\page_manager\PageVariantInterface.
+ */
+
+namespace Drupal\page_manager;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
+/**
+ * Provides an interface defining a PageVariant entity.
+ */
+interface PageVariantInterface extends ConfigEntityInterface {
+
+  /**
+   * @return \Drupal\Core\Display\VariantInterface
+   */
+  public function getVariant();
+
+  /**
+   * @return string
+   */
+  public function getVariantId();
+
+  /**
+   * @return \Drupal\page_manager\Entity\Page
+   */
+  public function getPage();
+
+  /**
+   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
+   */
+  public function getContexts();
+
+}
diff --git a/src/Plugin/DisplayVariant/BlockDisplayVariant.php b/src/Plugin/DisplayVariant/BlockDisplayVariant.php
index 7e5e394..16508ba 100644
--- a/src/Plugin/DisplayVariant/BlockDisplayVariant.php
+++ b/src/Plugin/DisplayVariant/BlockDisplayVariant.php
@@ -272,7 +272,7 @@ class BlockDisplayVariant extends VariantBase implements ContextAwareVariantInte
     // Set up the attributes used by a modal to prevent duplication later.
     $attributes = [
       'class' => ['use-ajax'],
-      'data-dialog-type' => 'modal',
+      'data-accepts' => 'application/vnd.drupal-modal',
       'data-dialog-options' => Json::encode([
         'width' => 'auto',
       ]),
diff --git a/src/Plugin/MenuBase.php b/src/Plugin/MenuBase.php
new file mode 100644
index 0000000..c3d2c49
--- /dev/null
+++ b/src/Plugin/MenuBase.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Plugin\MenuBase.
+ */
+
+namespace Drupal\page_manager\Plugin;
+
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\DependencyInjection\DependencySerializationTrait;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+abstract class MenuBase implements PageManagerMenuInterface {
+  use StringTranslationTrait, DependencySerializationTrait;
+
+  /**
+   * The plugin_id.
+   *
+   * @var string
+   */
+  protected $pluginId;
+
+  /**
+   * The plugin implementation definition.
+   *
+   * @var array
+   */
+  protected $pluginDefinition;
+
+  /**
+   * Configuration information passed into the plugin.
+   *
+   * When using an interface like
+   * \Drupal\Component\Plugin\ConfigurablePluginInterface, this is where the
+   * configuration should be stored.
+   *
+   * Plugin configuration is optional, so plugin implementations must provide
+   * their own setters and getters.
+   *
+   * @var array
+   */
+  protected $configuration;
+
+  /**
+   * Constructs a Drupal\Component\Plugin\PluginBase object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+    $this->pluginId = $plugin_id;
+    $this->pluginDefinition = $plugin_definition;
+    $this->setConfiguration($configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfiguration(array $configuration) {
+    $this->configuration = NestedArray::mergeDeep(
+      $this->defaultConfiguration(),
+      $configuration
+    );
+    // Unset keys that are not relevant to this plugin instance.
+    $keys = array_keys($this->defaultConfiguration());
+    foreach ($this->configuration as $key => $value) {
+      if (array_search($key, $keys) === FALSE) {
+        unset($this->configuration[$key]);
+      }
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginId() {
+    return $this->pluginId;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginDefinition() {
+    return $this->pluginDefinition;
+  }
+
+}
diff --git a/src/Plugin/PageManagerMenu/DefaultMenuTab.php b/src/Plugin/PageManagerMenu/DefaultMenuTab.php
new file mode 100644
index 0000000..773de31
--- /dev/null
+++ b/src/Plugin/PageManagerMenu/DefaultMenuTab.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Plugin\PageManagerMenu\DefaultMenuTab.
+ */
+
+namespace Drupal\page_manager\Plugin\PageManagerMenu;
+
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Menu\LocalTaskManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @PageManagerMenu(
+ *   id = "default_menu_tab",
+ *   label = @Translation("Default menu tab")
+ * )
+ */
+class DefaultMenuTab extends MenuTab {
+
+  /**
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $storage;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static($container->get('entity.manager')->getStorage('menu'), $container->get('plugin.manager.menu.local_task'), $configuration, $plugin_id, $plugin_definition);
+  }
+
+  public function __construct(EntityStorageInterface $entity_storage, LocalTaskManagerInterface $task_manager, array $configuration, $plugin_id, $plugin_definition) {
+    $this->storage = $entity_storage;
+    parent::__construct($task_manager, $configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+    $form['parent_menu_type'] = [
+      '#type' => 'radios',
+      '#title' => $this->t('Parent menu type'),
+      '#options' => [
+        'route' => $this->t('No menu entry'),
+        'menu' => $this->t('Normal menu entry'),
+        'task' => $this->t('Menu tab'),
+      ],
+      '#default_value' => 'route',
+    ];
+    $form['parent_title'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Parent title'),
+      '#description' => $this->t('If set to normal or tab, enter the text to use for the menu item.'),
+      '#states' => [
+        'visible' => [
+          ':input[name="parent_menu_type"]' => [['value' => 'menu'], ['value' => 'task']],
+        ]
+      ],
+    ];
+    $menus = [];
+    foreach ($this->storage->loadMultiple() as $id => $menu) {
+      $menus[$id] = $menu->label();
+    }
+    $form['parent_menu'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Parent menu'),
+      '#options' => $menus,
+      '#states' => [
+        'visible' => [
+          ':input[name="parent_menu_type"]' => ['value' => 'menu'],
+        ]
+      ],
+    ];
+
+    $form['parent_weight'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Parent weight'),
+      '#description' => $this->t('The lower the weight the higher/further left it will appear.'),
+      '#default_value' => 0,
+      '#states' => [
+        'visible' => [
+          ':input[name="parent_menu_type"]' => [['value' => 'menu'], ['value' => 'task']],
+        ]
+      ],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    parent::submitConfigurationForm($form, $form_state);
+  }
+
+}
diff --git a/src/Plugin/PageManagerMenu/MenuAction.php b/src/Plugin/PageManagerMenu/MenuAction.php
new file mode 100644
index 0000000..c0b8f04
--- /dev/null
+++ b/src/Plugin/PageManagerMenu/MenuAction.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Plugin\PageManagerMenu\MenuAction
+ */
+
+namespace Drupal\page_manager\Plugin\PageManagerMenu;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Menu\LocalActionManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\page_manager\Plugin\MenuBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @PageManagerMenu(
+ *   id = "menu_action",
+ *   label = @Translation("Local action")
+ * )
+ */
+class MenuAction extends MenuBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * @var \Drupal\Core\Menu\LocalActionManagerInterface
+   */
+  protected $actionManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static($container->get('plugin.manager.menu.local_action'), $configuration, $plugin_id, $plugin_definition);
+  }
+
+  public function __construct(LocalActionManagerInterface $action_manager, array $configuration, $plugin_id, $plugin_definition) {
+    $this->actionManager = $action_manager;
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  public function invalidate() {
+    $this->actionManager->clearCachedDefinitions();
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'title' => '',
+      'weight' => 0,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form['title'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Title'),
+      '#description' => $this->t('If set to normal or tab, enter the text to use for the menu item.'),
+      '#default_value' => $this->configuration['title'],
+    ];
+
+    $form['weight'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Weight'),
+      '#description' => $this->t('The lower the weight the higher/further left it will appear.'),
+      '#default_value' => $this->configuration['weight'],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    $this->configuration['title'] = $form_state->getValue('title');
+    $this->configuration['weight'] = $form_state->getValue('weight');
+  }
+
+}
diff --git a/src/Plugin/PageManagerMenu/MenuTab.php b/src/Plugin/PageManagerMenu/MenuTab.php
new file mode 100644
index 0000000..31501aa
--- /dev/null
+++ b/src/Plugin/PageManagerMenu/MenuTab.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Plugin\PageManagerMenu\MenuTab.
+ */
+
+namespace Drupal\page_manager\Plugin\PageManagerMenu;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Menu\LocalTaskManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\page_manager\Plugin\MenuBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @PageManagerMenu(
+ *   id = "menu_tab",
+ *   label = @Translation("Menu tab")
+ * )
+ */
+class MenuTab extends MenuBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * @var \Drupal\Core\Menu\LocalTaskManagerInterface
+   */
+  protected $taskManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static($container->get('plugin.manager.menu.local_task'), $configuration, $plugin_id, $plugin_definition);
+  }
+
+  public function __construct(LocalTaskManagerInterface $task_manager, array $configuration, $plugin_id, $plugin_definition) {
+    $this->taskManager = $task_manager;
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidate() {
+    $this->taskManager->clearCachedDefinitions();
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'title' => '',
+      'weight' => 0,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form['title'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Title'),
+      '#description' => $this->t('If set to normal or tab, enter the text to use for the menu item.'),
+      '#default_value' => $this->configuration['title'],
+    ];
+
+    $form['weight'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Weight'),
+      '#description' => $this->t('The lower the weight the higher/further left it will appear.'),
+      '#default_value' => $this->configuration['weight'],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    $this->configuration['title'] = $form_state->getValue('title');
+    $this->configuration['weight'] = $form_state->getValue('weight');
+  }
+
+}
diff --git a/src/Plugin/PageManagerMenu/NormalMenu.php b/src/Plugin/PageManagerMenu/NormalMenu.php
new file mode 100644
index 0000000..8884979
--- /dev/null
+++ b/src/Plugin/PageManagerMenu/NormalMenu.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Plugin\PageManagerMenu\NormalMenu
+ */
+
+namespace Drupal\page_manager\Plugin\PageManagerMenu;
+
+
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Menu\MenuLinkManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\page_manager\Plugin\MenuBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @PageManagerMenu(
+ *   id = "normal_menu",
+ *   label = @Translation("Normal menu entry")
+ * )
+ */
+class NormalMenu extends MenuBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * @var \Drupal\Core\Menu\MenuLinkManagerInterface
+   */
+  protected $linkManager;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $menuStorage;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static($container->get('plugin.manager.menu.link'), $container->get('entity.manager')->getStorage('menu'), $configuration, $plugin_id, $plugin_definition);
+  }
+
+  public function __construct(MenuLinkManagerInterface $link_manager, EntityStorageInterface $menu_storage, array $configuration, $plugin_id, $plugin_definition) {
+    $this->linkManager = $link_manager;
+    $this->menuStorage = $menu_storage;
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  public function invalidate() {
+    $this->linkManager->rebuild();
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form['title'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Title'),
+      '#description' => $this->t('If set to normal or tab, enter the text to use for the menu item.'),
+      '#default_value' => $this->configuration['title'],
+    ];
+
+    $menus = [];
+    foreach ($this->menuStorage->loadMultiple() as $id => $menu) {
+      $menus[$id] = $menu->label();
+    }
+    $form['menu'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Menu'),
+      '#options' => $menus,
+      '#default_value' => $this->configuration['menu'],
+    ];
+
+    $form['weight'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Weight'),
+      '#description' => $this->t('The lower the weight the higher/further left it will appear.'),
+      '#default_value' => $this->configuration['weight'],
+    ];
+
+    return $form;
+  }
+
+  public function defaultConfiguration() {
+    return [
+      'title' => '',
+      'menu' => '',
+      'weight' => 0,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    $this->configuration['title'] = $form_state->getValue('title');
+    $this->configuration['menu'] = $form_state->getValue('menu');
+    $this->configuration['weight'] = $form_state->getValue('weight');
+  }
+
+}
diff --git a/src/Plugin/PageManagerMenuInterface.php b/src/Plugin/PageManagerMenuInterface.php
new file mode 100644
index 0000000..902641a
--- /dev/null
+++ b/src/Plugin/PageManagerMenuInterface.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Plugin\PageManagerMenuInterface.
+ */
+
+namespace Drupal\page_manager\Plugin;
+
+
+use Drupal\Component\Plugin\ConfigurablePluginInterface;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Plugin\PluginFormInterface;
+
+interface PageManagerMenuInterface extends PluginFormInterface, ConfigurablePluginInterface, PluginInspectionInterface{
+
+  public function invalidate();
+
+}
diff --git a/src/Plugin/PageManagerMenuManager.php b/src/Plugin/PageManagerMenuManager.php
new file mode 100644
index 0000000..b60e5bf
--- /dev/null
+++ b/src/Plugin/PageManagerMenuManager.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Plugin\PageManagerMenuManager.
+ */
+
+namespace Drupal\page_manager\Plugin;
+
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+
+class PageManagerMenuManager extends DefaultPluginManager {
+
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    $this->alterInfo('page_manager_menu_info');
+    $this->setCacheBackend($cache_backend, 'page_manager_menu_plugins');
+    parent::__construct('Plugin/PageManagerMenu', $namespaces, $module_handler, 'Drupal\page_manager\Plugin\PageManagerMenuInterface', 'Drupal\page_manager\Annotation\PageManagerMenu');
+  }
+
+}
diff --git a/src/Plugin/VariantAwareInterface.php b/src/Plugin/VariantAwareInterface.php
index 0226fb7..5537c65 100644
--- a/src/Plugin/VariantAwareInterface.php
+++ b/src/Plugin/VariantAwareInterface.php
@@ -28,7 +28,7 @@ interface VariantAwareInterface {
    * @param string $variant_id
    *   The variant ID.
    *
-   * @return \Drupal\Core\Display\VariantInterface
+   * @return \Drupal\page_manager\Entity\PageVariant
    *   The variant object.
    */
   public function getVariant($variant_id);
@@ -46,7 +46,7 @@ interface VariantAwareInterface {
   /**
    * Returns the variants available for the entity.
    *
-   * @return \Drupal\Core\Display\VariantInterface[]
+   * @return \Drupal\page_manager\Entity\PageVariant[]
    *   An array of the variants.
    */
   public function getVariants();
diff --git a/src/Plugin/VariantAwareTrait.php b/src/Plugin/VariantAwareTrait.php
index 95677a9..21f5364 100644
--- a/src/Plugin/VariantAwareTrait.php
+++ b/src/Plugin/VariantAwareTrait.php
@@ -14,9 +14,9 @@ trait VariantAwareTrait {
   /**
    * The plugin collection that holds the variants.
    *
-   * @var \Drupal\page_manager\Plugin\VariantCollection
+   * @var \Drupal\page_manager\Entity\PageVariant[].
    */
-  protected $variantCollection;
+  protected $variants;
 
   /**
    * @see \Drupal\page_manager\Plugin\VariantAwareInterface::addVariant()
diff --git a/src/Tests/PageManagerAdminTest.php b/src/Tests/PageManagerAdminTest.php
index 59b14bc..436b288 100644
--- a/src/Tests/PageManagerAdminTest.php
+++ b/src/Tests/PageManagerAdminTest.php
@@ -68,20 +68,16 @@ class PageManagerAdminTest extends WebTestBase {
   protected function doTestAddPage() {
     $this->drupalGet('admin/structure');
     $this->clickLink('Pages');
-    $this->assertText('Add a new page.');
+    $this->assertText('There is no Page yet.');
 
-    // Add a new page without a label.
+    // Add a new page.
     $this->clickLink('Add page');
     $edit = [
+      'label' => 'Foo',
       'id' => 'foo',
       'path' => 'admin/foo',
     ];
     $this->drupalPostForm(NULL, $edit, 'Save');
-    $this->assertText('Label field is required.');
-
-    // Add a new page with a label.
-    $edit += ['label' => 'Foo'];
-    $this->drupalPostForm(NULL, $edit, 'Save');
     $this->assertRaw(SafeMarkup::format('The %label page has been added.', ['%label' => 'Foo']));
 
     // Test that it is available immediately.
diff --git a/src/Tests/StaticContextTest.php b/src/Tests/StaticContextTest.php
deleted file mode 100644
index 4065502..0000000
--- a/src/Tests/StaticContextTest.php
+++ /dev/null
@@ -1,173 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\Tests\StaticContextTest.
- */
-
-namespace Drupal\page_manager\Tests;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Tests static context for pages.
- *
- * @group page_manager
- */
-class StaticContextTest extends WebTestBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static $modules = ['page_manager', 'node'];
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp() {
-    parent::setUp();
-
-    $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
-    $this->drupalLogin($this->drupalCreateUser(['administer pages', 'create article content']));
-  }
-
-  /**
-   * Tests that a node bundle condition controls the node view page.
-   */
-  public function testStaticContext() {
-    // Create a node, and check its page.
-    $node = $this->drupalCreateNode(['type' => 'article']);
-    $node2 = $this->drupalCreateNode(['type' => 'article']);
-    $this->drupalGet('node/' . $node->id());
-    $this->assertResponse(200);
-    $this->assertText($node->label());
-    $this->assertTitle($node->label() . ' | Drupal');
-
-    // Create a new page entity.
-    $edit_page = [
-      'label' => 'Static node context',
-      'id' => 'static_node_context',
-      'path' => 'static-context',
-    ];
-    $this->drupalPostForm('admin/structure/page_manager/add', $edit_page, 'Save');
-
-    // Add a static context for each node to the page.
-    $contexts = array(
-      array(
-        'title' => 'Static Node',
-        'machine_name' => 'static_node',
-        'node' => $node,
-      ),
-      array(
-        'title' => 'Static Node 2',
-        'machine_name' => 'static_node_2',
-        'node' => $node2,
-      ),
-    );
-    foreach ($contexts as $context) {
-      $this->clickLink('Add new static context');
-      $edit = array(
-        '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.');
-    }
-
-    // Add a new display variant.
-    $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']);
-    $this->clickLink('Add new display variant');
-    $this->clickLink('Block page');
-    $variant_edit = [
-      'display_variant[label]' => 'Static context blocks',
-      'display_variant[page_title]' => 'Static context test page',
-    ];
-    $this->drupalPostForm(NULL, $variant_edit, 'Add display variant');
-
-    // Add a block that renders the node from the first static context.
-    $this->clickLink('Add new block');
-    $this->clickLink('Entity view (Content)');
-    $edit = [
-      'settings[label]' => 'Static node view',
-      'settings[label_display]' => 1,
-      'settings[view_mode]' => 'default',
-      'region' => 'top',
-    ];
-    $this->drupalPostForm(NULL, $edit, 'Add block');
-    $this->assertText($edit['settings[label]']);
-
-    // Add a block that renders the node from the second static context.
-    $this->clickLink('Add new block');
-    $this->clickLink('Entity view (Content)');
-    $edit = [
-      'settings[label]' => 'Static node 2 view',
-      'settings[label_display]' => 1,
-      'settings[view_mode]' => 'default',
-      'region' => 'bottom',
-      'context_mapping[entity]' => $contexts[1]['machine_name'],
-    ];
-    $this->drupalPostForm(NULL, $edit, 'Add block');
-    $this->assertText($edit['settings[label]']);
-
-    // Open the page and verify that the node from the static context is there.
-    $this->drupalGet($edit_page['path']);
-    $this->assertText($node->label());
-    $this->assertText($node->get('body')->getValue()[0]['value']);
-    $this->assertText($node2->label());
-    $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(t('Edit'), 1);
-    $edit = array(
-      '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.');
-
-    // Open the page and verify that the node from the static context is there.
-    $this->drupalGet($edit_page['path']);
-    $this->assertText($node->label());
-    $this->assertText($node->get('body')->getValue()[0]['value']);
-    // Also make sure the second node is NOT there.
-    $this->assertNoText($node2->label());
-    $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->clickLink(t('Edit'));
-    $edit = array(
-      'label' => 'Static Node edited',
-      'entity_type' => 'node',
-      'selection' => $node2->getTitle(),
-    );
-    $this->drupalPostForm(NULL, $edit, t('Update Static Context'));
-    $this->assertText('The ' . $edit['label'] . ' static context has been updated.');
-
-    // Edit the page variant and remove one static context view block.
-    $this->clickLink(t('Edit'), 2);
-    // Remove the second static context view block from the display variant.
-    $this->clickLink(t('Delete'), 1);
-    $this->drupalPostForm(NULL, NULL, t('Delete'));
-
-    // Make sure only the second static context's node is rendered on the page.
-    $this->drupalGet($edit_page['path']);
-    $this->assertNoText($node->label());
-    $this->assertNoText($node->get('body')->getValue()[0]['value']);
-    $this->assertText($node2->label());
-    $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(t('Delete'));
-    $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->assertNoText($edit['label']);
-  }
-
-}
diff --git a/src/Wizard/PageManager.php b/src/Wizard/PageManager.php
new file mode 100644
index 0000000..1124c1b
--- /dev/null
+++ b/src/Wizard/PageManager.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Wizard\PageManager.
+ */
+
+namespace Drupal\page_manager\Wizard;
+
+
+use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Wizard\EntityFormWizardBase;
+
+class PageManager extends EntityFormWizardBase {
+
+  /**
+   * {@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}
+   *
+   * Operations are directly manipulated for optional steps, but parameters
+   * need custom handling.
+   */
+  public function getNextParameters($cached_values) {
+    $parameters = parent::getNextParameters($cached_values);
+    if ($parameters['step'] == 'parameters') {
+      $matches = [];
+      /** @var $page \Drupal\page_manager\Entity\Page */
+      $page = $cached_values['page'];
+      preg_match_all('|\{\w+\}|', $page->getPath(), $matches);
+      // If there are no argument in the path, forward to the next step.
+      if (!array_filter($matches)) {
+        $operations = $this->getOperations();
+        unset($operations['general'], $operations['parameters']);
+        $optional_steps = array_filter($cached_values['wizard_options']);
+        if ($optional_steps) {
+          $parameters['step'] = reset($optional_steps);
+        }
+      }
+    }
+    return $parameters;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Operations are directly manipulated for optional steps, but parameters
+   * need custom handling.
+   */
+  public function getPreviousParameters($cached_values) {
+    $parameters = parent::getPreviousParameters($cached_values);
+    if ($parameters['step'] == 'parameters') {
+      $matches = [];
+      /** @var $page \Drupal\page_manager\Entity\Page */
+      $page = $cached_values['page'];
+      preg_match_all('|\{\w+\}|', $page->getPath(), $matches);
+      // If there are no argument in the path, go back to 'general'.
+      if (!array_filter($matches)) {
+        $parameters['step'] = 'general';
+      }
+    }
+    return $parameters;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations() {
+    $cached_values = $this->getTempstore()->get($this->getMachineName());
+    $operations = [];
+    $operations['general'] = [
+      'title' => $this->t('Page Information'),
+      'form' => '\Drupal\page_manager\Form\PageGeneralForm',
+    ];
+    $operations['parameters'] = [
+      'title' => $this->t('Define URL Parameters'),
+      'form' => '\Drupal\page_manager\Form\PageParameterForm',
+    ];
+    if ($cached_values['wizard_options']['access']) {
+      $operations['access'] = [
+        'title' => $this->t('Page Access'),
+        'form' => '\Drupal\page_manager\Form\PageAccessForm',
+      ];
+    }
+    if ($cached_values['wizard_options']['menu']) {
+      $operations['menu'] = [
+        'title' => $this->t('Menu Information'),
+        'form' => '\Drupal\page_manager\Form\PageMenuForm',
+      ];
+    }
+    if ($cached_values['wizard_options']['selection']) {
+      $operations['selection'] = [
+        'title' => $this->t('Selection Criteria'),
+        'form' => '\Drupal\page_manager\Form\PageSelectionForm',
+      ];
+    }
+    if (!empty($cached_values['wizard_options']['contexts'])) {
+      $operations['contexts'] = [
+        'title' => $this->t('Configure Contexts'),
+        'form' => '\Drupal\page_manager\Form\PageContextsForm',
+      ];
+    }
+    $operations['display_variant'] = [
+      'title' => $this->t('Configure Variant'),
+      'form' => '\Drupal\page_manager\Form\PageVariantConfigureForm',
+    ];
+    return $operations;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteName() {
+    return 'entity.page.add_step_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Add a custom "cancel" button.
+   */
+  protected function actions(FormInterface $form_object, FormStateInterface $form_state) {
+    $actions = parent::actions($form_object, $form_state);
+    $actions['cancel'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Cancel'),
+      '#validate' => array(
+        array($this, 'populateCachedValues'),
+      ),
+      '#submit' => array(
+        array($this, 'cancel'),
+      ),
+      '#limit_validation_errors' => array(),
+    ];
+    return $actions;
+  }
+
+  /**
+   * Custom cancel submission handling.
+   */
+  public function cancel(array &$form, FormStateInterface $form_state) {
+    if ($this->getMachineName()) {
+      $this->getTempstore()->delete($this->getMachineName());
+    }
+    $form_state->setRedirect('entity.page.collection');
+  }
+
+}
diff --git a/src/Wizard/RouteParameters.php b/src/Wizard/RouteParameters.php
new file mode 100644
index 0000000..f43b83a
--- /dev/null
+++ b/src/Wizard/RouteParameters.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\page_manager\Wizard\RouteParameters.
+ */
+
+namespace Drupal\page_manager\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\Form\ParameterAssignContextForm',
+      ],
+      'settings' => [
+        'title' => $this->t('Parameter Settings'),
+        'form' => 'Drupal\page_manager\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/tests/src/Unit/BlockDisplayVariantTest.php b/tests/src/Unit/BlockDisplayVariantTest.php
index 20776b5..489284a 100644
--- a/tests/src/Unit/BlockDisplayVariantTest.php
+++ b/tests/src/Unit/BlockDisplayVariantTest.php
@@ -7,19 +7,12 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
-use Drupal\Component\Uuid\UuidInterface;
+use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Block\BlockPluginInterface;
 use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\Context\CacheContextsManager;
-use Drupal\Core\Plugin\Context\ContextHandlerInterface;
 use Drupal\Core\Plugin\ContextAwarePluginInterface;
 use Drupal\Core\Form\FormState;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\Utility\Token;
 use Drupal\page_manager\PageExecutable;
-use Drupal\page_manager\PageInterface;
-use Drupal\page_manager\Plugin\BlockPluginCollection;
-use Drupal\page_manager\Plugin\DisplayVariant\BlockDisplayVariant;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 
@@ -38,7 +31,7 @@ class BlockDisplayVariantTest extends UnitTestCase {
    * @covers ::access
    */
   public function testAccess() {
-    $display_variant = $this->getMockBuilder(BlockDisplayVariant::class)
+    $display_variant = $this->getMockBuilder('Drupal\page_manager\Plugin\DisplayVariant\BlockDisplayVariant')
       ->disableOriginalConstructor()
       ->setMethods(['determineSelectionAccess'])
       ->getMock();
@@ -47,7 +40,7 @@ class BlockDisplayVariantTest extends UnitTestCase {
       ->willReturn(FALSE);
     $this->assertSame(FALSE, $display_variant->access());
 
-    $display_variant = $this->getMockBuilder(BlockDisplayVariant::class)
+    $display_variant = $this->getMockBuilder('Drupal\page_manager\Plugin\DisplayVariant\BlockDisplayVariant')
       ->disableOriginalConstructor()
       ->setMethods(['determineSelectionAccess'])
       ->getMock();
@@ -64,68 +57,99 @@ class BlockDisplayVariantTest extends UnitTestCase {
    */
   public function testBuildNoCache() {
     $container = new ContainerBuilder();
-    $cache_contexts = $this->prophesize(CacheContextsManager::class);
-    $container->set('cache_contexts_manager', $cache_contexts->reveal());
+    $cache_contexts = $this
+      ->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $container->set('cache_contexts_manager', $cache_contexts);
     \Drupal::setContainer($container);
 
-    $account = $this->prophesize(AccountInterface::class);
-
-    $block1 = $this->prophesize(BlockPluginInterface::class);
-    $block1->access($account)->willReturn(TRUE);
-    $block1->build()->willReturn(['#markup' => 'block1_build_value']);
-    $block1->getConfiguration()->willReturn(['label' => 'Block label']);
-    $block1->getPluginId()->willReturn('block_plugin_id');
-    $block1->getBaseId()->willReturn('block_base_plugin_id');
-    $block1->getDerivativeId()->willReturn('block_derivative_plugin_id');
-    $block1->getCacheTags()->willReturn(['block_plugin:block_plugin_id']);
-    $block1->getCacheMaxAge()->willReturn(0);
-    $block1->getCacheContexts()->willReturn(['url']);
-
-    $block2 = $this->prophesize()->willImplement(ContextAwarePluginInterface::class)->willImplement(BlockPluginInterface::class);
-    $block2->access($account)->willReturn(FALSE);
-    $block2->getCacheTags()->willReturn([]);
-    $block2->getCacheContexts()->shouldNotBeCalled();
-    $block2->build()->shouldNotBeCalled();
-
+    $block1 = $this->getMock('Drupal\Core\Block\BlockPluginInterface');
+    $block1->expects($this->once())
+      ->method('access')
+      ->will($this->returnValue(TRUE));
+    $block1->expects($this->once())
+      ->method('build')
+      ->will($this->returnValue([
+        '#markup' => 'block1_build_value',
+      ]));
+    $block1->expects($this->once())
+      ->method('getConfiguration')
+      ->will($this->returnValue(['label' => 'Block label']));
+    $block1->expects($this->once())
+      ->method('getPluginId')
+      ->will($this->returnValue('block_plugin_id'));
+    $block1->expects($this->once())
+      ->method('getBaseId')
+      ->will($this->returnValue('block_base_plugin_id'));
+    $block1->expects($this->once())
+      ->method('getDerivativeId')
+      ->will($this->returnValue('block_derivative_plugin_id'));
+    $block2 = $this->getMock('Drupal\Tests\page_manager\Unit\TestContextAwareBlockPluginInterface');
+    $block2->expects($this->once())
+      ->method('access')
+      ->will($this->returnValue(FALSE));
+    $block1->expects($this->atLeastOnce())
+      ->method('getCacheTags')
+      ->willReturn(array('block_plugin:block_plugin_id'));
+    $block2->expects($this->never())
+      ->method('getCacheTags')
+      ->willReturn(array('block_plugin:block_plugin_id'));
+    $block1->expects($this->once())
+      ->method('getCacheMaxAge')
+      ->willReturn(0);
+    $block1->expects($this->once())
+      ->method('getCacheContexts')
+      ->willReturn(['url']);
+    $block2->expects($this->never())
+      ->method('getCacheContexts');
+    $block2->expects($this->never())
+      ->method('build');
     $blocks = [
       'top' => [
-        'block1' => $block1->reveal(),
-        'block2' => $block2->reveal(),
+        'block1' => $block1,
+        'block2' => $block2,
       ],
     ];
-    $block_collection = $this->getMockBuilder(BlockPluginCollection::class)
+    $block_collection = $this->getMockBuilder('Drupal\page_manager\Plugin\BlockPluginCollection')
       ->disableOriginalConstructor()
       ->getMock();
     $block_collection->expects($this->once())
       ->method('getAllByRegion')
-      ->willReturn($blocks);
-
-    $context_handler = $this->prophesize(ContextHandlerInterface::class);
-    $context_handler->applyContextMapping($block2->reveal(), [])->shouldBeCalledTimes(1);
-
-    $uuid_generator = $this->prophesize(UuidInterface::class);
+      ->will($this->returnValue($blocks));
+
+    $context_handler = $this->getMock('Drupal\Core\Plugin\Context\ContextHandlerInterface');
+    $context_handler->expects($this->once())
+      ->method('applyContextMapping')
+      ->with($block2, []);
+    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
+    $uuid_generator = $this->getMock('Drupal\Component\Uuid\UuidInterface');
     $page_title = 'Page title';
-    $token = $this->getMockBuilder(Token::class)
+    $token = $this->getMockBuilder('Drupal\Core\Utility\Token')
       ->disableOriginalConstructor()
       ->getMock();
-    $display_variant = $this->getMockBuilder(BlockDisplayVariant::class)
-      ->setConstructorArgs([['page_title' => $page_title], 'test', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token])
-      ->setMethods(['getBlockCollection', 'drupalHtmlClass', 'renderPageTitle'])
+    $display_variant = $this->getMockBuilder('Drupal\page_manager\Plugin\DisplayVariant\BlockDisplayVariant')
+      ->setConstructorArgs([['page_title' => $page_title], 'test', array(), $context_handler, $account, $uuid_generator, $token])
+      ->setMethods(array('getBlockCollection', 'drupalHtmlClass', 'renderPageTitle'))
       ->getMock();
 
-    $page = $this->prophesize(PageInterface::class);
-    $page->id()->willReturn('page_id');
-    $page->getCacheTags()->willReturn(['page:page_id']);
-    $page_executable = new PageExecutable($page->reveal());
+    $page = $this->getMock('\Drupal\page_manager\PageInterface');
+    $page->expects($this->atLeastOnce())
+      ->method('id')
+      ->willReturn('page_id');
+    $page->expects($this->atLeastOnce())
+      ->method('getCacheTags')
+      ->willReturn(array('page:page_id'));
+    $page_executable = new PageExecutable($page);
     $display_variant->setExecutable($page_executable);
 
     $display_variant->expects($this->once())
       ->method('getBlockCollection')
-      ->willReturn($block_collection);
+      ->will($this->returnValue($block_collection));
     $display_variant->expects($this->once())
       ->method('renderPageTitle')
       ->with($page_title)
-      ->willReturn($page_title);
+      ->will($this->returnValue($page_title));
 
     $expected_build = [
       'regions' => [
@@ -184,72 +208,96 @@ class BlockDisplayVariantTest extends UnitTestCase {
    */
   public function testBuildCache() {
     $container = new ContainerBuilder();
-    $cache_contexts = $this->prophesize(CacheContextsManager::class);
-    $container->set('cache_contexts_manager', $cache_contexts->reveal());
+    $cache_contexts = $this
+      ->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $container->set('cache_contexts_manager', $cache_contexts);
     \Drupal::setContainer($container);
 
-    $account = $this->prophesize(AccountInterface::class);
-
-    $block1 = $this->prophesize(BlockPluginInterface::class);
-    $block1->access($account)->willReturn(TRUE);
-    $block1->getConfiguration()->willReturn(['label' => 'Block label']);
-    $block1->getPluginId()->willReturn('block_plugin_id');
-    $block1->getBaseId()->willReturn('block_base_plugin_id');
-    $block1->getDerivativeId()->willReturn('block_derivative_plugin_id');
-    $block1->getCacheTags()->willReturn(['block_plugin1:block_plugin_id']);
-    $block1->getCacheMaxAge()->willReturn(3600);
-    $block1->getCacheContexts()->willReturn(['url']);
-
-    $block2 = $this->prophesize()->willImplement(ContextAwarePluginInterface::class)->willImplement(BlockPluginInterface::class);
-    $block2->access($account)->willReturn(TRUE);
-    $block2->getConfiguration()->willReturn([]);
-    $block2->getPluginId()->willReturn('block_plugin_id');
-    $block2->getBaseId()->willReturn('block_base_plugin_id');
-    $block2->getDerivativeId()->willReturn('block_derivative_plugin_id');
-    $block2->getCacheContexts()->willReturn([]);
-    $block2->getCacheMaxAge()->willReturn(Cache::PERMANENT);
-    $block2->getCacheTags()->willReturn(['block_plugin2:block_plugin_id']);
+    $block1 = $this->getMock('Drupal\Core\Block\BlockPluginInterface');
+    $block1->expects($this->once())
+      ->method('access')
+      ->will($this->returnValue(TRUE));
+    $block1->expects($this->once())
+      ->method('getConfiguration')
+      ->will($this->returnValue(['label' => 'Block label']));
+    $block1->expects($this->once())
+      ->method('getPluginId')
+      ->will($this->returnValue('block_plugin_id'));
+    $block1->expects($this->once())
+      ->method('getBaseId')
+      ->will($this->returnValue('block_base_plugin_id'));
+    $block1->expects($this->once())
+      ->method('getDerivativeId')
+      ->will($this->returnValue('block_derivative_plugin_id'));
+    $block2 = $this->getMock('Drupal\Tests\page_manager\Unit\TestContextAwareBlockPluginInterface');
+    $block2->expects($this->once())
+      ->method('access')
+      ->will($this->returnValue(TRUE));
+    $block1->expects($this->once())
+      ->method('getCacheContexts')
+      ->willReturn(['url']);
+    $block2->expects($this->once())
+      ->method('getCacheContexts')
+      ->willReturn([]);
+    $block1->expects($this->once())
+      ->method('getCacheMaxAge')
+      ->willReturn(3600);
+    $block2->expects($this->once())
+      ->method('getCacheMaxAge')
+      ->willReturn(Cache::PERMANENT);
+    $block1->expects($this->atLeastOnce())
+      ->method('getCacheTags')
+      ->willReturn(array('block_plugin1:block_plugin_id'));
+    $block2->expects($this->atLeastOnce())
+      ->method('getCacheTags')
+      ->willReturn(array('block_plugin2:block_plugin_id'));
     $blocks = [
       'top' => [
-        'block1' => $block1->reveal(),
-        'block2' => $block2->reveal(),
+        'block1' => $block1,
+        'block2' => $block2,
       ],
     ];
-    $block_collection = $this->getMockBuilder(BlockPluginCollection::class)
+    $block_collection = $this->getMockBuilder('Drupal\page_manager\Plugin\BlockPluginCollection')
       ->disableOriginalConstructor()
       ->getMock();
     $block_collection->expects($this->once())
       ->method('getAllByRegion')
-      ->willReturn($blocks);
-
-    $context_handler = $this->prophesize(ContextHandlerInterface::class);
-    $context_handler->applyContextMapping($block2->reveal(), [])->shouldBeCalledTimes(1);
-
-    $uuid_generator = $this->prophesize(UuidInterface::class);
+      ->will($this->returnValue($blocks));
+
+    $context_handler = $this->getMock('Drupal\Core\Plugin\Context\ContextHandlerInterface');
+    $context_handler->expects($this->once())
+      ->method('applyContextMapping')
+      ->with($block2, []);
+    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
+    $uuid_generator = $this->getMock('Drupal\Component\Uuid\UuidInterface');
     $page_title = 'Page title';
-    $token = $this->getMockBuilder(Token::class)
+    $token = $this->getMockBuilder('Drupal\Core\Utility\Token')
       ->disableOriginalConstructor()
       ->getMock();
-    $display_variant = $this->getMockBuilder(BlockDisplayVariant::class)
-      ->setConstructorArgs([['page_title' => $page_title, 'uuid' => 'UUID'], 'test', [], $context_handler->reveal(), $account->reveal(), $uuid_generator->reveal(), $token])
-      ->setMethods(['getBlockCollection', 'drupalHtmlClass', 'renderPageTitle'])
+    $display_variant = $this->getMockBuilder('Drupal\page_manager\Plugin\DisplayVariant\BlockDisplayVariant')
+      ->setConstructorArgs([['page_title' => $page_title, 'uuid' => 'UUID'], 'test', [], $context_handler, $account, $uuid_generator, $token])
+      ->setMethods(array('getBlockCollection', 'drupalHtmlClass', 'renderPageTitle'))
       ->getMock();
 
-    $page = $this->prophesize(PageInterface::class);
-    $page->id()->willReturn('page_id');
-    $page->getCacheTags()
-      ->willReturn(['page:page_id'])
-      ->shouldBeCalled();
-    $page_executable = new PageExecutable($page->reveal());
+    $page = $this->getMock('\Drupal\page_manager\PageInterface');
+    $page->expects($this->atLeastOnce())
+      ->method('id')
+      ->willReturn('page_id');
+    $page->expects($this->atLeastOnce())
+      ->method('getCacheTags')
+      ->willReturn(array('page:page_id'));
+    $page_executable = new PageExecutable($page);
     $display_variant->setExecutable($page_executable);
 
     $display_variant->expects($this->once())
       ->method('getBlockCollection')
-      ->willReturn($block_collection);
+      ->will($this->returnValue($block_collection));
     $display_variant->expects($this->once())
       ->method('renderPageTitle')
       ->with($page_title)
-      ->willReturn($page_title);
+      ->will($this->returnValue($page_title));
 
     $expected_cache_block1 = [
       'keys' => ['page_manager_page', 'page_id', 'block', 'block1'],
@@ -277,10 +325,11 @@ class BlockDisplayVariantTest extends UnitTestCase {
     $this->assertSame($expected_cache_block2, $build['regions']['top']['block2']['#cache']);
     $this->assertSame($expected_cache_page, $build['regions']['#cache']);
 
-    $block1->build()->willReturn([
-      '#markup' => 'block1_build_value',
-    ]);
-    $block2->build()->willReturn([]);
+    $block1->expects($this->once())
+      ->method('build')
+      ->will($this->returnValue([
+        '#markup' => 'block1_build_value',
+      ]));
 
     $block1_build = $display_variant->buildBlock($build['regions']['top']['block1']);
     $block2_build = $display_variant->buildBlock($build['regions']['top']['block2']);
@@ -296,7 +345,7 @@ class BlockDisplayVariantTest extends UnitTestCase {
    * @dataProvider providerTestSubmitConfigurationForm
    */
   public function testSubmitConfigurationForm($values, $update_block_count) {
-    $display_variant = $this->getMockBuilder(BlockDisplayVariant::class)
+    $display_variant = $this->getMockBuilder('Drupal\page_manager\Plugin\DisplayVariant\BlockDisplayVariant')
       ->disableOriginalConstructor()
       ->setMethods(['updateBlock'])
       ->getMock();
@@ -338,3 +387,6 @@ class BlockDisplayVariantTest extends UnitTestCase {
   }
 
 }
+
+interface TestContextAwareBlockPluginInterface extends ContextAwarePluginInterface, BlockPluginInterface {
+}
diff --git a/tests/src/Unit/BlockPluginCollectionTest.php b/tests/src/Unit/BlockPluginCollectionTest.php
index 5b77af5..7c37aea 100644
--- a/tests/src/Unit/BlockPluginCollectionTest.php
+++ b/tests/src/Unit/BlockPluginCollectionTest.php
@@ -7,11 +7,8 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
-use Drupal\Core\Block\BlockManagerInterface;
-use Drupal\Core\Block\BlockPluginInterface;
 use Drupal\page_manager\Plugin\BlockPluginCollection;
 use Drupal\Tests\UnitTestCase;
-use Prophecy\Argument;
 
 /**
  * Tests the block plugin collection.
@@ -55,21 +52,25 @@ class BlockPluginCollectionTest extends UnitTestCase {
         'region' => 'bottom',
       ],
     ];
-    $block_manager = $this->prophesize(BlockManagerInterface::class);
     $plugins = [];
+    $plugin_map = [];
     foreach ($blocks as $block_id => $block) {
-      $plugin = $this->prophesize(BlockPluginInterface::class);
-      $plugin->label()->willReturn($block['label']);
-      $plugin->getConfiguration()->willReturn($block);
-      $plugins[$block_id] = $plugin->reveal();
-
-      $block_manager->createInstance($block_id, $block)
-        ->willReturn($plugin->reveal())
-        ->shouldBeCalled();
+      $plugin = $this->getMock('Drupal\Core\Block\BlockPluginInterface');
+      $plugin->expects($this->any())
+        ->method('label')
+        ->will($this->returnValue($block['label']));
+      $plugin->expects($this->any())
+        ->method('getConfiguration')
+        ->will($this->returnValue($block));
+      $plugins[$block_id] = $plugin;
+      $plugin_map[] = [$block_id, $block, $plugin];
     }
+    $block_manager = $this->getMock('Drupal\Core\Block\BlockManagerInterface');
+    $block_manager->expects($this->exactly(4))
+      ->method('createInstance')
+      ->will($this->returnValueMap($plugin_map));
 
-
-    $block_plugin_collection = new BlockPluginCollection($block_manager->reveal(), $blocks);
+    $block_plugin_collection = new BlockPluginCollection($block_manager, $blocks);
     $expected = [
       'bottom' => [
         'bing' => $plugins['bing'],
diff --git a/tests/src/Unit/BlockVariantTraitTest.php b/tests/src/Unit/BlockVariantTraitTest.php
index 5ce0fbc..767ad4d 100644
--- a/tests/src/Unit/BlockVariantTraitTest.php
+++ b/tests/src/Unit/BlockVariantTraitTest.php
@@ -29,13 +29,15 @@ class BlockVariantTraitTest extends UnitTestCase {
    * @dataProvider providerTestGetRegionAssignments
    */
   public function testGetRegionAssignments($expected, $blocks = []) {
-    $block_collection = $this->prophesize(BlockPluginCollection::class);
-    $block_collection->getAllByRegion()
-      ->willReturn($blocks)
-      ->shouldBeCalled();
+    $block_collection = $this->getMockBuilder('Drupal\page_manager\Plugin\BlockPluginCollection')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $block_collection->expects($this->once())
+      ->method('getAllByRegion')
+      ->will($this->returnValue($blocks));
 
     $display_variant = new TestBlockVariantTrait();
-    $display_variant->setBlockPluginCollection($block_collection->reveal());
+    $display_variant->setBlockPluginCollection($block_collection);
 
     $this->assertSame($expected, $display_variant->getRegionAssignments());
   }
@@ -95,7 +97,7 @@ class TestBlockVariantTrait {
   protected $uuidGenerator;
 
   /**
-   * @param BlockPluginCollection $block_plugin_collection
+   * @param \Drupal\page_manager\Plugin\BlockPluginCollection $block_plugin_collection
    *
    * @return $this
    */
diff --git a/tests/src/Unit/CurrentUserContextTest.php b/tests/src/Unit/CurrentUserContextTest.php
index 1bcf9ab..89fc18b 100644
--- a/tests/src/Unit/CurrentUserContextTest.php
+++ b/tests/src/Unit/CurrentUserContextTest.php
@@ -7,15 +7,10 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
-use Drupal\Core\Plugin\Context\Context;
-use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\page_manager\EventSubscriber\CurrentUserContext;
 use Drupal\user\UserInterface;
-use Prophecy\Argument;
 
 /**
  * Tests the current user context.
@@ -26,39 +21,50 @@ use Prophecy\Argument;
  */
 class CurrentUserContextTest extends PageContextTestBase {
 
-  /**
-   * @covers ::onPageContext
-   */
   public function testOnPageContext() {
-    $account = $this->prophesize(AccountInterface::class);
-    $account->id()->willReturn(1);
-    $user = $this->prophesize(UserInterface::class);
+    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
+    $account->expects($this->once())
+      ->method('id')
+      ->will($this->returnValue(1));
+    $user = $this->getMock('Drupal\Tests\page_manager\Unit\TestUserInterface');
 
-    $data_definition = new DataDefinition(['type' => 'entity:user']);
-
-    $this->typedDataManager->create($data_definition, $user)
-      ->willReturn(EntityAdapter::createFromEntity($user->reveal()));
-
-    $this->typedDataManager->getDefaultConstraints($data_definition)
-      ->willReturn([]);
-
-    $this->typedDataManager->createDataDefinition('entity:user')
-      ->will(function () use ($data_definition) {
-        return $data_definition;
-      });
+    $this->typedDataManager->expects($this->any())
+      ->method('create')
+      ->willReturn(EntityAdapter::createFromEntity($user));
+    $this->typedDataManager->expects($this->any())
+      ->method('getDefaultConstraints')
+      ->will($this->returnValue([]));
+    $this->typedDataManager->expects($this->any())
+      ->method('createDataDefinition')
+      ->will($this->returnCallback(function ($type) {
+        return new DataDefinition(['type' => $type]);
+      }));
 
     $this->executable->expects($this->once())
       ->method('addContext')
-      ->with('current_user', $this->isInstanceOf(Context::class));
+      ->with('current_user', $this->isInstanceOf('Drupal\Core\Plugin\Context\Context'));
 
-    $user_storage = $this->prophesize(EntityStorageInterface::class);
-    $user_storage->load(1)->willReturn($user->reveal());
+    $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+    $user_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
+    $user_storage->expects($this->once())
+      ->method('load')
+      ->with(1)
+      ->will($this->returnValue($user));
+    $entity_manager->expects($this->any())
+      ->method('getStorage')
+      ->with('user')
+      ->will($this->returnValue($user_storage));
 
-    $entity_manager = $this->prophesize(EntityManagerInterface::class);
-    $entity_manager->getStorage('user')->willReturn($user_storage->reveal());
-
-    $route_param_context = new CurrentUserContext($account->reveal(), $entity_manager->reveal());
+    $route_param_context = new CurrentUserContext($account, $entity_manager);
     $route_param_context->onPageContext($this->event);
   }
 
 }
+
+/**
+ * Provides a testable version of UserInterface.
+ *
+ * @see https://github.com/sebastianbergmann/phpunit-mock-objects/commit/96a6794
+ */
+interface TestUserInterface extends \Iterator, UserInterface {
+}
diff --git a/tests/src/Unit/PageAccessTest.php b/tests/src/Unit/PageAccessTest.php
index 319eed4..487d085 100644
--- a/tests/src/Unit/PageAccessTest.php
+++ b/tests/src/Unit/PageAccessTest.php
@@ -7,17 +7,8 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
-use Drupal\Core\Cache\Context\CacheContextsManager;
-use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Plugin\Context\ContextHandlerInterface;
-use Drupal\Core\Session\AccountInterface;
 use Drupal\page_manager\Entity\PageAccess;
-use Drupal\page_manager\PageExecutableInterface;
-use Drupal\page_manager\PageInterface;
 use Drupal\Tests\UnitTestCase;
-use Prophecy\Argument;
 
 /**
  * Tests access for Page entities.
@@ -50,56 +41,61 @@ class PageAccessTest extends UnitTestCase {
    */
   public function setUp() {
     parent::setUp();
-    $this->contextHandler = $this->prophesize(ContextHandlerInterface::class);
-    $this->entityType = $this->prophesize(EntityTypeInterface::class);
+    $this->contextHandler = $this->getMock('Drupal\Core\Plugin\Context\ContextHandlerInterface');
+    $this->entityType = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
 
-    $module_handler = $this->prophesize(ModuleHandlerInterface::class);
-    $module_handler->invokeAll(Argument::cetera())->willReturn([]);
+    $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+    $module_handler->expects($this->any())
+      ->method('invokeAll')
+      ->will($this->returnValue([]));
 
-    $this->pageAccess = new PageAccess($this->entityType->reveal(), $this->contextHandler->reveal());
-    $this->pageAccess->setModuleHandler($module_handler->reveal());
+    $this->pageAccess = new PageAccess($this->entityType, $this->contextHandler);
+    $this->pageAccess->setModuleHandler($module_handler);
   }
 
   /**
    * @covers ::checkAccess
    */
   public function testAccessView() {
-    $executable = $this->prophesize(PageExecutableInterface::class);
-    $executable->getContexts()->willReturn([]);
-
-    $page = $this->prophesize(PageInterface::class);
-
-    $page->getExecutable()->willReturn($executable->reveal());
-    $page->getAccessConditions()->willReturn([]);
-    $page->getAccessLogic()->willReturn('and');
-    $page->status()->willReturn(TRUE);
-
-    $page->uuid()->shouldBeCalled();
-    $page->getEntityTypeId()->shouldBeCalled();
-
-    $account = $this->prophesize(AccountInterface::class);
-
-    $this->assertTrue($this->pageAccess->access($page->reveal(), 'view', NULL, $account->reveal()));
+    $executable = $this->getMock('Drupal\page_manager\PageExecutableInterface');
+    $executable->expects($this->once())
+      ->method('getContexts')
+      ->will($this->returnValue([]));
+
+    $page = $this->getMock('Drupal\page_manager\PageInterface');
+    $page->expects($this->once())
+      ->method('getExecutable')
+      ->will($this->returnValue($executable));
+    $page->expects($this->once())
+      ->method('getAccessConditions')
+      ->will($this->returnValue([]));
+    $page->expects($this->once())
+      ->method('getAccessLogic')
+      ->will($this->returnValue('and'));
+    $page->expects($this->once())
+      ->method('status')
+      ->will($this->returnValue(TRUE));
+
+    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
+
+    $this->assertTrue($this->pageAccess->access($page, 'view', NULL, $account));
   }
 
   /**
    * @covers ::checkAccess
    */
   public function testAccessViewDisabled() {
-    $this->setUpCacheContextsManager();
-
-    $page = $this->prophesize(PageInterface::class);
-    $page->status()->willReturn(FALSE);
-    $page->getCacheTags()->willReturn(['page:1']);
-    $page->getCacheContexts()->willReturn([]);
-    $page->getCacheMaxAge()->willReturn(0);
-
-    $page->uuid()->shouldBeCalled();
-    $page->getEntityTypeId()->shouldBeCalled();
+    $page = $this->getMock('Drupal\page_manager\PageInterface');
+    $page->expects($this->once())
+      ->method('status')
+      ->will($this->returnValue(FALSE));
+    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
 
-    $account = $this->prophesize(AccountInterface::class);
+    $page->expects($this->once())
+      ->method('getCacheTags')
+      ->willReturn(['page:1']);
 
-    $this->assertFalse($this->pageAccess->access($page->reveal(), 'view', NULL, $account->reveal()));
+    $this->assertFalse($this->pageAccess->access($page, 'view', NULL, $account));
   }
 
   /**
@@ -108,29 +104,32 @@ class PageAccessTest extends UnitTestCase {
    * @dataProvider providerTestAccessDelete
    */
   public function testAccessDelete($is_new, $is_fallback, $expected) {
-    $this->entityType->getAdminPermission()->willReturn('test permission');
-
-    $page = $this->prophesize(PageInterface::class);
-    $page->isNew()->willReturn($is_new);
-    $page->isFallbackPage()->willReturn($is_fallback);
-
-    $page->uuid()->shouldBeCalled();
-    $page->getEntityTypeId()->shouldBeCalled();
+    $this->entityType->expects($this->any())
+      ->method('getAdminPermission')
+      ->will($this->returnValue('test permission'));
+
+    $page = $this->getMock('Drupal\page_manager\PageInterface');
+    $page->expects($this->any())
+      ->method('isNew')
+      ->will($this->returnValue($is_new));
+    $page->expects($this->any())
+      ->method('isFallbackPage')
+      ->will($this->returnValue($is_fallback));
 
     // Ensure that the cache tag is added for the temporary conditions.
     if ($is_new || $is_fallback) {
-      $this->setUpCacheContextsManager();
-
-      $page->getCacheTags()->willReturn(['page:1']);
-      $page->getCacheContexts()->willReturn([]);
-      $page->getCacheMaxAge()->willReturn(0);
+      $page->expects($this->once())
+        ->method('getCacheTags')
+        ->willReturn(['page:1']);
     }
 
-    $account = $this->prophesize(AccountInterface::class);
-    $account->hasPermission('test permission')->willReturn(TRUE);
-    $account->id()->shouldBeCalled();
+    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
+    $account->expects($this->any())
+      ->method('hasPermission')
+      ->with('test permission')
+      ->will($this->returnValue(TRUE));
 
-    $this->assertSame($expected, $this->pageAccess->access($page->reveal(), 'delete', NULL, $account->reveal()));
+    $this->assertSame($expected, $this->pageAccess->access($page, 'delete', NULL, $account));
   }
 
   /**
@@ -145,15 +144,4 @@ class PageAccessTest extends UnitTestCase {
     return $data;
   }
 
-  /**
-   * Sets up the cache contexts manager in the container.
-   */
-  protected function setUpCacheContextsManager() {
-    $prophecy = $this->prophesize(CacheContextsManager::class);
-    $container = new ContainerBuilder();
-    $container->set('cache_contexts_manager', $prophecy->reveal());
-    \Drupal::setContainer($container);
-    return $this;
-  }
-
 }
diff --git a/tests/src/Unit/PageContextTestBase.php b/tests/src/Unit/PageContextTestBase.php
index 7cdd05b..2bec8e5 100644
--- a/tests/src/Unit/PageContextTestBase.php
+++ b/tests/src/Unit/PageContextTestBase.php
@@ -8,9 +8,7 @@
 namespace Drupal\Tests\page_manager\Unit;
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\TypedData\TypedDataManager;
 use Drupal\page_manager\Event\PageManagerContextEvent;
-use Drupal\page_manager\PageExecutable;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -45,14 +43,16 @@ abstract class PageContextTestBase extends UnitTestCase {
   protected function setUp() {
     parent::setUp();
 
-    $this->typedDataManager = $this->prophesize(TypedDataManager::class);
+    $this->typedDataManager = $this->getMockBuilder('Drupal\Core\TypedData\TypedDataManager')
+      ->disableOriginalConstructor()
+      ->getMock();
 
     $container = new ContainerBuilder();
     $container->set('string_translation', $this->getStringTranslationStub());
-    $container->set('typed_data_manager', $this->typedDataManager->reveal());
+    $container->set('typed_data_manager', $this->typedDataManager);
     \Drupal::setContainer($container);
 
-    $this->executable = $this->getMockBuilder(PageExecutable::class)
+    $this->executable = $this->getMockBuilder('Drupal\page_manager\PageExecutable')
       ->disableOriginalConstructor()
       ->setMethods(['getPage', 'addContext'])
       ->getMock();
diff --git a/tests/src/Unit/PageExecutableTest.php b/tests/src/Unit/PageExecutableTest.php
index bd8c2c0..eafbecd 100644
--- a/tests/src/Unit/PageExecutableTest.php
+++ b/tests/src/Unit/PageExecutableTest.php
@@ -8,17 +8,11 @@
 namespace Drupal\Tests\page_manager\Unit;
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\Display\VariantInterface;
 use Drupal\Core\Plugin\Context\Context;
 use Drupal\Core\Plugin\Context\ContextDefinition;
 use Drupal\page_manager\Event\PageManagerContextEvent;
-use Drupal\page_manager\Event\PageManagerEvents;
 use Drupal\page_manager\PageExecutable;
-use Drupal\page_manager\PageInterface;
-use Drupal\page_manager\Plugin\PageAwareVariantInterface;
 use Drupal\Tests\UnitTestCase;
-use Prophecy\Argument;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Tests the PageExecutable.
@@ -46,35 +40,49 @@ class PageExecutableTest extends UnitTestCase {
    */
   protected function setUp() {
     parent::setUp();
-    $this->page = $this->prophesize(PageInterface::class);
-    $this->exectuable = new PageExecutable($this->page->reveal());
+    $this->page = $this->getMock('Drupal\page_manager\PageInterface');
+    $this->exectuable = new PageExecutable($this->page);
   }
 
   /**
    * @covers ::getPage
    */
   public function testGetPage() {
-    $this->assertSame($this->page->reveal(), $this->exectuable->getPage());
+    $this->assertSame($this->page, $this->exectuable->getPage());
   }
 
   /**
    * @covers ::selectDisplayVariant
    */
   public function testSelectDisplayVariant() {
-    $display_variant1 = $this->prophesize(VariantInterface::class);
-    $display_variant1->access()->willReturn(FALSE);
-
-    $display_variant2 = $this->prophesize(PageAwareVariantInterface::class);
-    $display_variant2->access()->willReturn(TRUE);
-    $display_variant2->setExecutable($this->exectuable)
-      ->willReturn($display_variant2->reveal());
-
-    $this->page->getVariants()->willReturn([
-      'variant1' => $display_variant1->reveal(),
-      'variant2' => $display_variant2->reveal(),
-    ]);
+    $event_dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+    $container = new ContainerBuilder();
+    $container->set('event_dispatcher', $event_dispatcher);
+    \Drupal::setContainer($container);
 
-    $this->assertSame($display_variant2->reveal(), $this->exectuable->selectDisplayVariant());
+    $display_variant1 = $this->getMock('Drupal\Core\Display\VariantInterface');
+    $display_variant1->expects($this->once())
+      ->method('access')
+      ->will($this->returnValue(FALSE));
+    $display_variant1->expects($this->never())
+      ->method('setExecutable');
+
+    $display_variant2 = $this->getMock('Drupal\page_manager\Plugin\PageAwareVariantInterface');
+    $display_variant2->expects($this->once())
+      ->method('access')
+      ->will($this->returnValue(TRUE));
+    $display_variant2->expects($this->once())
+      ->method('setExecutable')
+      ->with($this->exectuable)
+      ->will($this->returnValue($display_variant2));
+    $this->page->expects($this->once())
+      ->method('getVariants')
+      ->will($this->returnValue([
+        'variant1' => $display_variant1,
+        'variant2' => $display_variant2,
+      ]));
+
+    $this->assertSame($display_variant2, $this->exectuable->selectDisplayVariant());
   }
 
   /**
@@ -92,15 +100,15 @@ class PageExecutableTest extends UnitTestCase {
    */
   public function testGetContexts() {
     $context = new Context(new ContextDefinition('bar'));
-
-    $event_dispatcher = $this->prophesize(EventDispatcherInterface::class);
-    $event_dispatcher->dispatch(PageManagerEvents::PAGE_CONTEXT, Argument::type(PageManagerContextEvent::class))
-      ->will(function ($args) use ($context) {
-        $args[1]->getPageExecutable()->addContext('foo', $context);
-      });
+    $event_dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+    $event_dispatcher->expects($this->once())
+      ->method('dispatch')
+      ->will($this->returnCallback(function ($event_name, PageManagerContextEvent $event) use ($context) {
+        $event->getPageExecutable()->addContext('foo', $context);
+      }));
 
     $container = new ContainerBuilder();
-    $container->set('event_dispatcher', $event_dispatcher->reveal());
+    $container->set('event_dispatcher', $event_dispatcher);
     \Drupal::setContainer($container);
 
     $contexts = $this->exectuable->getContexts();
diff --git a/tests/src/Unit/PageManagerRoutesTest.php b/tests/src/Unit/PageManagerRoutesTest.php
index d6df9c7..1d620f3 100644
--- a/tests/src/Unit/PageManagerRoutesTest.php
+++ b/tests/src/Unit/PageManagerRoutesTest.php
@@ -7,10 +7,7 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
-use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Routing\RouteBuildEvent;
-use Drupal\page_manager\PageInterface;
 use Drupal\page_manager\Routing\PageManagerRoutes;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\Routing\Route;
@@ -52,13 +49,13 @@ class PageManagerRoutesTest extends UnitTestCase {
    * @covers ::__construct
    */
   protected function setUp() {
-    $this->pageStorage = $this->prophesize(ConfigEntityStorageInterface::class);
-
-    $this->entityManager = $this->prophesize(EntityManagerInterface::class);
-    $this->entityManager->getStorage('page')
-      ->willReturn($this->pageStorage);
-
-    $this->routeSubscriber = new PageManagerRoutes($this->entityManager->reveal());
+    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+    $this->pageStorage = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
+    $this->entityManager->expects($this->any())
+      ->method('getStorage')
+      ->with('page')
+      ->will($this->returnValue($this->pageStorage));
+    $this->routeSubscriber = new PageManagerRoutes($this->entityManager);
   }
 
   /**
@@ -68,19 +65,20 @@ class PageManagerRoutesTest extends UnitTestCase {
    */
   public function testAlterRoutesWithFallback() {
     // Set up the fallback page.
-    $page = $this->prophesize(PageInterface::class);
-    $page->status()
-      ->willReturn(TRUE)
-      ->shouldBeCalledTimes(1);
-    $page->getPath()->shouldNotBeCalled();
-    $page->isFallbackPage()
-      ->willReturn(TRUE)
-      ->shouldBeCalledTimes(1);
-    $pages['page1'] = $page->reveal();
-
-    $this->pageStorage->loadMultiple()
-      ->willReturn($pages)
-      ->shouldBeCalledTimes(1);
+    $page = $this->getMock('Drupal\page_manager\PageInterface');
+    $page->expects($this->once())
+      ->method('status')
+      ->will($this->returnValue(TRUE));
+    $page->expects($this->never())
+      ->method('getPath');
+    $page->expects($this->once())
+      ->method('isFallbackPage')
+      ->will($this->returnValue(TRUE));
+    $pages['page1'] = $page;
+
+    $this->pageStorage->expects($this->once())
+      ->method('loadMultiple')
+      ->will($this->returnValue($pages));
 
     $collection = new RouteCollection();
     $route_event = new RouteBuildEvent($collection);
@@ -97,33 +95,31 @@ class PageManagerRoutesTest extends UnitTestCase {
    */
   public function testAlterRoutesWithStatus() {
     // Set up a valid page.
-    $page1 = $this->prophesize(PageInterface::class);
-    $page1->status()
-      ->willReturn(TRUE)
-      ->shouldBeCalledTimes(1);
-    $page1->getPath()
-      ->willReturn('/page1')
-      ->shouldBeCalledTimes(1);
-    $page1->isFallbackPage()
-      ->willReturn(FALSE);
-    $page1->label()
-      ->willReturn('Page label')
-      ->shouldBeCalledTimes(1);
-    $page1->usesAdminTheme()
-      ->willReturn(TRUE)
-      ->shouldBeCalledTimes(1);
-    $pages['page1'] = $page1->reveal();
+    $page1 = $this->getMock('Drupal\page_manager\PageInterface');
+    $page1->expects($this->once())
+      ->method('status')
+      ->will($this->returnValue(TRUE));
+    $page1->expects($this->once())
+      ->method('getPath')
+      ->will($this->returnValue('/page1'));
+    $page1->expects($this->once())
+      ->method('label')
+      ->will($this->returnValue('Page label'));
+    $page1->expects($this->once())
+      ->method('usesAdminTheme')
+      ->will($this->returnValue(TRUE));
+    $pages['page1'] = $page1;
 
     // Set up a disabled page.
-    $page2 = $this->prophesize(PageInterface::class);
-    $page2->status()
-      ->willReturn(FALSE)
-      ->shouldBeCalledTimes(1);
-    $pages['page2'] = $page2->reveal();
+    $page2 = $this->getMock('Drupal\page_manager\PageInterface');
+    $page2->expects($this->once())
+      ->method('status')
+      ->will($this->returnValue(FALSE));
+    $pages['page2'] = $page2;
 
-    $this->pageStorage->loadMultiple()
-      ->willReturn($pages)
-      ->shouldBeCalledTimes(1);
+    $this->pageStorage->expects($this->once())
+      ->method('loadMultiple')
+      ->will($this->returnValue($pages));
 
     $collection = new RouteCollection();
     $route_event = new RouteBuildEvent($collection);
@@ -159,20 +155,17 @@ class PageManagerRoutesTest extends UnitTestCase {
    */
   public function testAlterRoutesOverrideExisting() {
     // Set up a page with the same path as an existing route.
-    $page = $this->prophesize(PageInterface::class);
-    $page->status()
-      ->willReturn(TRUE)
-      ->shouldBeCalledTimes(1);
-    $page->getPath()
-      ->willReturn('/test_route')
-      ->shouldBeCalledTimes(1);
-    $page->isFallbackPage()->willReturn(FALSE);
-    $page->label()->willReturn(NULL);
-    $page->usesAdminTheme()->willReturn(FALSE);
-
-    $this->pageStorage->loadMultiple()
-      ->willReturn(['page1' => $page->reveal()])
-      ->shouldBeCalledTimes(1);
+    $page = $this->getMock('Drupal\page_manager\PageInterface');
+    $page->expects($this->once())
+      ->method('status')
+      ->will($this->returnValue(TRUE));
+    $page->expects($this->once())
+      ->method('getPath')
+      ->will($this->returnValue('/test_route'));
+
+    $this->pageStorage->expects($this->once())
+      ->method('loadMultiple')
+      ->will($this->returnValue(['page1' => $page]));
 
     $collection = new RouteCollection();
     $collection->add('test_route', new Route('test_route', [], [], ['parameters' => ['foo' => 'bar']]));
@@ -200,7 +193,7 @@ class PageManagerRoutesTest extends UnitTestCase {
         ],
         'foo' => 'bar',
       ],
-      '_admin_route' => FALSE,
+      '_admin_route' => NULL,
     ];
     $this->assertMatchingRoute($route, '/test_route', $expected_defaults, $expected_requirements, $expected_options);
   }
diff --git a/tests/src/Unit/PageTest.php b/tests/src/Unit/PageTest.php
index bdf6d37..8d82e32 100644
--- a/tests/src/Unit/PageTest.php
+++ b/tests/src/Unit/PageTest.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
-use Drupal\page_manager\Entity\Page;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -25,7 +24,7 @@ class PageTest extends UnitTestCase {
    * @dataProvider providerTestIsFallbackPage
    */
   public function testIsFallbackPage($id, $expected) {
-    $page = $this->getMockBuilder(Page::class)
+    $page = $this->getMockBuilder('Drupal\page_manager\Entity\Page')
       ->setConstructorArgs([['id' => $id], 'page'])
       ->setMethods(['configFactory'])
       ->getMock();
diff --git a/tests/src/Unit/RouteParamContextTest.php b/tests/src/Unit/RouteParamContextTest.php
index cfd0dfc..be65264 100644
--- a/tests/src/Unit/RouteParamContextTest.php
+++ b/tests/src/Unit/RouteParamContextTest.php
@@ -7,13 +7,8 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
-use Drupal\Core\Plugin\Context\Context;
-use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\page_manager\EventSubscriber\RouteParamContext;
-use Drupal\page_manager\PageInterface;
-use Prophecy\Argument;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\Routing\Route;
@@ -28,44 +23,43 @@ use Symfony\Component\Routing\RouteCollection;
  */
 class RouteParamContextTest extends PageContextTestBase {
 
-  /**
-   * @covers ::onPageContext
-   */
   public function testOnPageContext() {
     $collection = new RouteCollection();
-    $route_provider = $this->prophesize(RouteProviderInterface::class);
-    $route_provider->getRoutesByPattern('/test_route')->willReturn($collection);
-
+    $route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
+    $route_provider->expects($this->once())
+      ->method('getRoutesByPattern')
+      ->will($this->returnValue($collection));
     $request = new Request();
     $request_stack = new RequestStack();
     $request_stack->push($request);
 
-    $data_definition = new DataDefinition(['type' => 'entity:user']);
-
-    $typed_data = $this->prophesize(TypedDataInterface::class);
-    $this->typedDataManager->getDefaultConstraints($data_definition)
-      ->willReturn([]);
-
-    $this->typedDataManager->create($data_definition, 'banana')
-      ->willReturn($typed_data->reveal());
-
-    $this->typedDataManager->createDataDefinition('bar')
-      ->will(function () use ($data_definition) {
-        return $data_definition;
-      });
-
-    $page = $this->prophesize(PageInterface::class);
+    $this->typedDataManager->expects($this->any())
+      ->method('getDefaultConstraints')
+      ->will($this->returnValue([]));
+    $this->typedDataManager->expects($this->once())
+      ->method('create')
+      ->with($this->isType('object'), 'banana')
+      ->will($this->returnValue($this->getMock('Drupal\Core\TypedData\TypedDataInterface')));
+    $this->typedDataManager->expects($this->any())
+      ->method('createDataDefinition')
+      ->will($this->returnCallback(function ($type) {
+        return new DataDefinition(['type' => $type]);
+      }));
+
+    $page = $this->getMock('Drupal\page_manager\PageInterface');
     $this->executable->expects($this->once())
       ->method('getPage')
-      ->will($this->returnValue($page->reveal()));
-    $page->getPath()->willReturn('/test_route');
+      ->will($this->returnValue($page));
+    $page->expects($this->once())
+      ->method('getPath')
+      ->will($this->returnValue('/test_route'));
 
     $this->executable->expects($this->at(1))
       ->method('addContext')
-      ->with('foo', $this->isInstanceOf(Context::class));
+      ->with('foo', $this->isInstanceOf('Drupal\Core\Plugin\Context\Context'));
     $this->executable->expects($this->at(2))
       ->method('addContext')
-      ->with('baz', $this->isInstanceOf(Context::class));
+      ->with('baz', $this->isInstanceOf('Drupal\Core\Plugin\Context\Context'));
 
     $collection->add('test_route', new Route('/test_route', [], [], [
       'parameters' => [
@@ -78,7 +72,7 @@ class RouteParamContextTest extends PageContextTestBase {
     // Set up a request with one of the expected parameters as an attribute.
     $request->attributes->add(['foo' => 'banana']);
 
-    $route_param_context = new RouteParamContext($route_provider->reveal(), $request_stack);
+    $route_param_context = new RouteParamContext($route_provider, $request_stack);
     $route_param_context->onPageContext($this->event);
   }
 
diff --git a/tests/src/Unit/VariantAwareTraitTest.php b/tests/src/Unit/VariantAwareTraitTest.php
index bacdd59..a07addc 100644
--- a/tests/src/Unit/VariantAwareTraitTest.php
+++ b/tests/src/Unit/VariantAwareTraitTest.php
@@ -7,14 +7,11 @@
 
 namespace Drupal\Tests\page_manager\Unit;
 
-use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Component\Uuid\UuidInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\Display\VariantInterface;
 use Drupal\page_manager\Plugin\VariantAwareTrait;
 use Drupal\page_manager\Plugin\VariantCollection;
 use Drupal\Tests\UnitTestCase;
-use Prophecy\Argument;
 
 /**
  * Tests the methods of a variant-aware class.
@@ -36,8 +33,8 @@ class VariantAwareTraitTest extends UnitTestCase {
   protected function setUp() {
     parent::setUp();
     $container = new ContainerBuilder();
-    $this->manager = $this->prophesize(PluginManagerInterface::class);
-    $container->set('plugin.manager.display_variant', $this->manager->reveal());
+    $this->manager = $this->getMock('Drupal\Component\Plugin\PluginManagerInterface');
+    $container->set('plugin.manager.display_variant', $this->manager);
     \Drupal::setContainer($container);
   }
 
@@ -46,10 +43,11 @@ class VariantAwareTraitTest extends UnitTestCase {
    */
   public function testGetVariantsEmpty() {
     $trait_object = new TestVariantAwareTrait();
-    $this->manager->createInstance()->shouldNotBeCalled();
+    $this->manager->expects($this->never())
+      ->method('createInstance');
 
     $variants = $trait_object->getVariants();
-    $this->assertInstanceOf(VariantCollection::class, $variants);
+    $this->assertInstanceOf('Drupal\page_manager\Plugin\VariantCollection', $variants);
     $this->assertSame(0, count($variants));
   }
 
@@ -62,14 +60,18 @@ class VariantAwareTraitTest extends UnitTestCase {
       'foo' => ['id' => 'foo_plugin'],
       'bar' => ['id' => 'bar_plugin'],
     ];
+    $plugin = $this->getMock('Drupal\Core\Display\VariantInterface');
+    $map = [];
     foreach ($config as $value) {
-      $plugin = $this->prophesize(VariantInterface::class);
-      $this->manager->createInstance($value['id'], $value)->willReturn($plugin->reveal());
+      $map[] = [$value['id'], $value, $plugin];
     }
+    $this->manager->expects($this->exactly(2))
+      ->method('createInstance')
+      ->will($this->returnValueMap($map));
     $trait_object->setVariantConfig($config);
 
     $variants = $trait_object->getVariants();
-    $this->assertInstanceOf(VariantCollection::class, $variants);
+    $this->assertInstanceOf('Drupal\page_manager\Plugin\VariantCollection', $variants);
     $this->assertSame(2, count($variants));
     return $variants;
   }
@@ -91,23 +93,21 @@ class VariantAwareTraitTest extends UnitTestCase {
     $uuid = 'test-uuid';
     $expected_config = $config + ['uuid' => $uuid];
 
-    $uuid_generator = $this->prophesize(UuidInterface::class);
-    $uuid_generator->generate()
-      ->willReturn($uuid)
-      ->shouldBeCalledTimes(1);
+    $uuid_generator = $this->getMock('Drupal\Component\Uuid\UuidInterface');
+    $uuid_generator->expects($this->once())
+      ->method('generate')
+      ->will($this->returnValue($uuid));
     $trait_object = new TestVariantAwareTrait();
-    $trait_object->setUuidGenerator($uuid_generator->reveal());
+    $trait_object->setUuidGenerator($uuid_generator);
 
-    $plugin_prophecy = $this->prophesize(VariantInterface::class);
-    $plugin_prophecy->getConfiguration()
-      ->willReturn($expected_config)
-      ->shouldBeCalled();
-    $plugin_prophecy->setConfiguration($expected_config)
-      ->willReturn($expected_config)
-      ->shouldBeCalled();
-
-    $this->manager->createInstance('foo', $expected_config)
-      ->willReturn($plugin_prophecy->reveal());
+    $plugin = $this->getMock('Drupal\Core\Display\VariantInterface');
+    $plugin->expects($this->once())
+      ->method('getConfiguration')
+      ->will($this->returnValue($expected_config));
+    $this->manager->expects($this->any())
+      ->method('createInstance')
+      ->with('foo', $expected_config)
+      ->will($this->returnValue($plugin));
 
     $resulting_uuid = $trait_object->addVariant($config);
     $this->assertSame($uuid, $resulting_uuid);
@@ -115,8 +115,8 @@ class VariantAwareTraitTest extends UnitTestCase {
     $variants = $trait_object->getVariants();
     $this->assertSame([$uuid => $uuid], $variants->getInstanceIds());
     $this->assertSame([$uuid => $expected_config], $variants->getConfiguration());
-    $this->assertSame($plugin_prophecy->reveal(), $variants->get($uuid));
-    return [$trait_object, $uuid, $plugin_prophecy->reveal()];
+    $this->assertSame($plugin, $variants->get($uuid));
+    return [$trait_object, $uuid, $plugin];
   }
 
   /**
@@ -126,7 +126,8 @@ class VariantAwareTraitTest extends UnitTestCase {
    */
   public function testGetVariant($data) {
     list($trait_object, $uuid, $plugin) = $data;
-    $this->manager->createInstance()->shouldNotBeCalled();
+    $this->manager->expects($this->never())
+      ->method('createInstance');
 
     $this->assertSame($plugin, $trait_object->getVariant($uuid));
     return [$trait_object, $uuid];
