diff --git a/config/install/page_manager.page.node_view.yml b/config/install/page_manager.page.node_view.yml
index 50239f0..92704b2 100644
--- a/config/install/page_manager.page.node_view.yml
+++ b/config/install/page_manager.page.node_view.yml
@@ -7,4 +7,3 @@ use_admin_theme: false
 path: '/node/{node}'
 access_logic: and
 access_conditions: {  }
-static_context: {  }
diff --git a/config/schema/page_manager.schema.yml b/config/schema/page_manager.schema.yml
index ad1f22c..add031c 100644
--- a/config/schema/page_manager.schema.yml
+++ b/config/schema/page_manager.schema.yml
@@ -23,22 +23,6 @@ page_manager.page.*:
       sequence:
         - type: condition.plugin.[id]
           label: 'Access Condition'
-    static_context:
-      type: sequence
-      label: Static context list
-      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'
 
 page_manager.page_variant.*:
   type: config_entity
@@ -73,8 +57,22 @@ page_manager.page_variant.*:
     selection_logic:
       type: string
       label: 'Selection logic'
-    contexts:
-      type: mapping
+    static_context:
+      type: sequence
+      label: Static context list
+      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'
 
 page_manager.block_plugin.*:
   type: block.settings.[id]
diff --git a/page_manager.routing.yml b/page_manager.routing.yml
index 410e1c3..7ba4827 100644
--- a/page_manager.routing.yml
+++ b/page_manager.routing.yml
@@ -85,28 +85,28 @@ page_manager.access_condition_delete:
 #### Static Contexts
 
 page_manager.static_context_add:
-  path: '/admin/structure/page_manager/manage/{page}/context/add'
+  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/context/add'
   defaults:
     _form: '\Drupal\page_manager\Form\StaticContextAddForm'
     _title: 'Add new static context'
   requirements:
-    _entity_access: page.update
+    _entity_access: page_variant.update
 
 page_manager.static_context_edit:
-  path: '/admin/structure/page_manager/manage/{page}/context/edit/{name}'
+  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/context/edit/{name}'
   defaults:
     _form: '\Drupal\page_manager\Form\StaticContextEditForm'
     _title_callback: '\Drupal\page_manager\Controller\PageManagerController::editStaticContextTitle'
   requirements:
-    _entity_access: page.update
+    _entity_access: page_variant.update
 
 page_manager.static_context_delete:
-  path: '/admin/structure/page_manager/manage/{page}/context/delete/{name}'
+  path: '/admin/structure/page_manager/manage/{page}/variant/{page_variant}/context/delete/{name}'
   defaults:
     _form: '\Drupal\page_manager\Form\StaticContextDeleteForm'
     _title: 'Delete static context'
   requirements:
-    _entity_access: page.update
+    _entity_access: page_variant.update
 
 #### Variants
 
diff --git a/page_manager.services.yml b/page_manager.services.yml
index 37a83bd..4d7db13 100644
--- a/page_manager.services.yml
+++ b/page_manager.services.yml
@@ -9,11 +9,9 @@ services:
     arguments: ['@router.route_provider', '@request_stack']
     tags:
       - { name: 'event_subscriber' }
-  page_manager.static_context:
-    class: Drupal\page_manager\EventSubscriber\StaticContext
+  page_manager.context_mapper:
+    class: Drupal\page_manager\ContextMapper
     arguments: ['@entity.repository']
-    tags:
-      - { name: 'event_subscriber' }
   page_manager.page_manager_routes:
     class: Drupal\page_manager\Routing\PageManagerRoutes
     arguments: ['@entity_type.manager', '@cache_tags.invalidator']
@@ -25,8 +23,6 @@ services:
     tags:
       - { name: route_filter }
       - { name: service_collector, tag: non_lazy_route_enhancer, call: addRouteEnhancer }
-  page_manager.executable_factory:
-    class: Drupal\page_manager\PageExecutableFactory
   page_manager.route_name_response_subscriber:
     class: Drupal\page_manager\EventSubscriber\RouteNameResponseSubscriber
     tags:
diff --git a/src/ContextMapper.php b/src/ContextMapper.php
new file mode 100644
index 0000000..101bf2f
--- /dev/null
+++ b/src/ContextMapper.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\ContextMapper.
+ */
+
+namespace Drupal\page_manager;
+
+use Drupal\Core\Entity\EntityRepositoryInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\page_manager\Context\EntityLazyLoadContext;
+
+/**
+ * Maps context configurations to context objects.
+ */
+class ContextMapper implements ContextMapperInterface {
+
+  /**
+   * The entity repository.
+   *
+   * @var \Drupal\Core\Entity\EntityRepositoryInterface
+   */
+  protected $entityRepository;
+
+  /**
+   * Constructs a new ContextMapper.
+   *
+   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
+   *   The entity repository.
+   */
+  public function __construct(EntityRepositoryInterface $entity_repository) {
+    $this->entityRepository = $entity_repository;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getContextValues(array $context_configurations) {
+    $contexts = [];
+    foreach ($context_configurations as $name => $context_configuration) {
+      $context_definition = new ContextDefinition($context_configuration['type'], $context_configuration['label']);
+      if (strpos($context_configuration['type'], 'entity:') === 0) {
+        $context = new EntityLazyLoadContext($context_definition, $this->entityRepository, $context_configuration['value']);
+      }
+      else {
+        $context = new Context($context_definition, $context_configuration['value']);
+      }
+      $contexts[$name] = $context;
+    }
+    return $contexts;
+  }
+
+}
diff --git a/src/ContextMapperInterface.php b/src/ContextMapperInterface.php
new file mode 100644
index 0000000..9f76cc1
--- /dev/null
+++ b/src/ContextMapperInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_manager\ContextMapperInterface.
+ */
+
+namespace Drupal\page_manager;
+
+/**
+ * Provides an interface for mapping context configurations to context objects.
+ */
+interface ContextMapperInterface {
+
+  /**
+   * Gathers the static context values.
+   *
+   * @param array[] $static_context_configurations
+   *   An array of static context configurations
+   *
+   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
+   *   An array of set context values, keyed by context name.
+   */
+  public function getContextValues(array $static_context_configurations);
+
+}
diff --git a/src/Controller/PageManagerController.php b/src/Controller/PageManagerController.php
index a0dc64f..8afdca1 100644
--- a/src/Controller/PageManagerController.php
+++ b/src/Controller/PageManagerController.php
@@ -145,16 +145,16 @@ public function editSelectionConditionTitle(PageVariantInterface $page_variant,
   /**
    * Route title callback.
    *
-   * @param \Drupal\page_manager\PageInterface $page
-   *   The page entity.
+   * @param \Drupal\page_manager\PageVariantInterface $page_variant
+   *   The page variant 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);
+  public function editStaticContextTitle(PageVariantInterface $page_variant, $name) {
+    $static_context = $page_variant->getStaticContext($name);
     return $this->t('Edit @label static context', ['@label' => $static_context['label']]);
   }
 
diff --git a/src/Entity/Page.php b/src/Entity/Page.php
index fa1011e..47e74dc 100644
--- a/src/Entity/Page.php
+++ b/src/Entity/Page.php
@@ -7,6 +7,9 @@
 
 namespace Drupal\page_manager\Entity;
 
+use Drupal\Component\Plugin\Context\ContextInterface;
+use Drupal\page_manager\Event\PageManagerContextEvent;
+use Drupal\page_manager\Event\PageManagerEvents;
 use Drupal\page_manager\PageInterface;
 use Drupal\Core\Condition\ConditionPluginCollection;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
@@ -41,7 +44,6 @@
  *     "path",
  *     "access_logic",
  *     "access_conditions",
- *     "static_context",
  *   },
  *   links = {
  *     "collection" = "/admin/structure/page_manager",
@@ -84,6 +86,13 @@ class Page extends ConfigEntityBase implements PageInterface {
   protected $variants;
 
   /**
+   * An array of collected contexts.
+   *
+   * @var \Drupal\Component\Plugin\Context\ContextInterface[]
+   */
+  protected $contexts = [];
+
+  /**
    * The configuration of access conditions.
    *
    * @var array
@@ -112,43 +121,6 @@ class Page extends ConfigEntityBase implements PageInterface {
   protected $use_admin_theme;
 
   /**
-   * Static context references.
-   *
-   * A list of arrays with the keys name, label, type and value.
-   *
-   * @var array[]
-   */
-  protected $static_context = [];
-
-  /**
-   * Stores a reference to the executable version of this page.
-   *
-   * This is only used on runtime, and is not stored.
-   *
-   * @var \Drupal\page_manager\PageExecutable
-   */
-  protected $executable;
-
-  /**
-   * Returns a factory for page executables.
-   *
-   * @return \Drupal\page_manager\PageExecutableFactoryInterface
-   */
-  protected function executableFactory() {
-    return \Drupal::service('page_manager.executable_factory');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getExecutable() {
-    if (!isset($this->executable)) {
-      $this->executable = $this->executableFactory()->get($this);
-    }
-    return $this->executable;
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function getPath() {
@@ -257,41 +229,18 @@ public function getAccessLogic() {
   /**
    * {@inheritdoc}
    */
-  public function getStaticContexts() {
-    return $this->static_context;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getStaticContext($name) {
-    if (isset($this->static_context[$name])) {
-      return $this->static_context[$name];
-    }
-    return [];
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setStaticContext($name, $configuration) {
-    $this->static_context[$name] = $configuration;
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function removeStaticContext($name) {
-    unset($this->static_context[$name]);
-    return $this;
+  public function addContext($name, ContextInterface $value) {
+    $this->contexts[$name] = $value;
   }
 
   /**
    * {@inheritdoc}
    */
   public function getContexts() {
-    return $this->getExecutable()->getContexts();
+    if (!$this->contexts) {
+      $this->eventDispatcher()->dispatch(PageManagerEvents::PAGE_CONTEXT, new PageManagerContextEvent($this));
+    }
+    return $this->contexts;
   }
 
   /**
@@ -351,23 +300,34 @@ public function variantSortHelper($a, $b) {
   }
 
   /**
+   * Wraps the event dispatcher.
+   *
+   * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   *   The event dispatcher.
+   */
+  protected function eventDispatcher() {
+    return \Drupal::service('event_dispatcher');
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function __sleep() {
     $vars = parent::__sleep();
 
-    // Avoid serializing plugin collections and the page executable as they
-    // might contain references to a lot of objects including the container.
+    // Ensure any plugin collections are stored correctly before serializing.
+    foreach ($this->getPluginCollections() as $plugin_config_key => $plugin_collection) {
+      $this->set($plugin_config_key, $plugin_collection->getConfiguration());
+    }
+
+    // Avoid serializing plugin collections and entities as they might contain
+    // references to a lot of objects including the container.
     $unset_vars = [
-      'variants' => NULL,
-      'accessConditionCollection' => 'access_variants',
-      'executable' => NULL,
+      'variants',
+      'accessConditionCollection',
     ];
-    foreach ($unset_vars as $unset_var => $configuration_key) {
-      if (!empty($this->$unset_var)) {
-        if ($configuration_key) {
-          $this->set($configuration_key, $this->$unset_var->getConfiguration());
-        }
+    foreach ($unset_vars as $unset_var) {
+      if (!empty($this->{$unset_var})) {
         unset($vars[array_search($unset_var, $vars)]);
       }
     }
diff --git a/src/Entity/PageAccess.php b/src/Entity/PageAccess.php
index 7fb9582..c4f4d1d 100644
--- a/src/Entity/PageAccess.php
+++ b/src/Entity/PageAccess.php
@@ -75,7 +75,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
       }
 
       $conditions = $entity->getAccessConditions();
-      $contexts = $entity->getExecutable()->getContexts();
+      $contexts = $entity->getContexts();
       foreach ($conditions as $condition) {
         if ($condition instanceof ContextAwarePluginInterface) {
           $this->contextHandler()->applyContextMapping($condition, $contexts);
diff --git a/src/Entity/PageVariant.php b/src/Entity/PageVariant.php
index b5650ef..bf5d9ee 100644
--- a/src/Entity/PageVariant.php
+++ b/src/Entity/PageVariant.php
@@ -46,7 +46,7 @@
  *     "weight",
  *     "selection_criteria",
  *     "selection_logic",
- *     "contexts"
+ *     "static_context",
  *   },
  *   links = {
  *     "edit-form" = "/admin/structure/page_manager/manage/{page}/variant/{page_variant}",
@@ -130,6 +130,15 @@ class PageVariant extends ConfigEntityBase implements PageVariantInterface {
   protected $contexts = [];
 
   /**
+   * Static context references.
+   *
+   * A list of arrays with the keys name, label, type and value.
+   *
+   * @var array[]
+   */
+  protected $static_context = [];
+
+  /**
    * The plugin collection that holds the single variant plugin instance.
    *
    * @var \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection
@@ -232,11 +241,9 @@ public function getVariantPluginId() {
   }
 
   /**
-   * Gets the page this variant is on.
-   *
-   * @return \Drupal\page_manager\Entity\Page
+   * {@inheritdoc}
    */
-  protected function getPage() {
+  public function getPage() {
     if (!$this->page) {
       throw new \UnexpectedValueException('The page variant has no associated page');
     }
@@ -247,7 +254,11 @@ protected function getPage() {
    * {@inheritdoc}
    */
   public function getContexts() {
-    return array_merge($this->getPage()->getContexts(), $this->contexts);
+    if (!$this->contexts) {
+      $contexts = $this->getContextMapper()->getContextValues($this->getStaticContexts());
+      $this->contexts = array_merge($this->getPage()->getContexts(), $contexts);
+    }
+    return $this->contexts;
   }
 
   /**
@@ -316,6 +327,39 @@ public function removeSelectionCondition($condition_id) {
   /**
    * {@inheritdoc}
    */
+  public function getStaticContexts() {
+    return $this->static_context;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getStaticContext($name) {
+    if (isset($this->static_context[$name])) {
+      return $this->static_context[$name];
+    }
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setStaticContext($name, $configuration) {
+    $this->static_context[$name] = $configuration;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeStaticContext($name) {
+    unset($this->static_context[$name]);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   protected function urlRouteParameters($rel) {
     $parameters = parent::urlRouteParameters($rel);
     $parameters['page'] = $this->get('page');
@@ -357,4 +401,13 @@ protected function getConditionManager() {
     return \Drupal::service('plugin.manager.condition');
   }
 
+  /**
+   * Wraps the context mapper.
+   *
+   * @return \Drupal\page_manager\ContextMapperInterface
+   */
+  protected function getContextMapper() {
+    return \Drupal::service('page_manager.context_mapper');
+  }
+
 }
diff --git a/src/Event/PageManagerContextEvent.php b/src/Event/PageManagerContextEvent.php
index d7a2b5e..5d443a4 100644
--- a/src/Event/PageManagerContextEvent.php
+++ b/src/Event/PageManagerContextEvent.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\page_manager\Event;
 
-use Drupal\page_manager\PageExecutableInterface;
+use Drupal\page_manager\PageInterface;
 use Symfony\Component\EventDispatcher\Event;
 
 /**
@@ -18,7 +18,7 @@
 class PageManagerContextEvent extends Event {
 
   /**
-   * The page the context is gathered for.
+   * The page entity the context is gathered for.
    *
    * @var \Drupal\page_manager\PageInterface
    */
@@ -27,20 +27,20 @@ class PageManagerContextEvent extends Event {
   /**
    * Creates a new PageManagerContextEvent.
    *
-   * @param \Drupal\page_manager\PageExecutableInterface $page
-   *   The page executable.
+   * @param \Drupal\page_manager\PageInterface $page
+   *   The page entity.
    */
-  public function __construct(PageExecutableInterface $page) {
+  public function __construct(PageInterface $page) {
     $this->page = $page;
   }
 
   /**
-   * Returns the page executable for this event.
+   * Returns the page entity for this event.
    *
-   * @return \Drupal\page_manager\PageExecutable
-   *   The page executable.
+   * @return \Drupal\page_manager\PageInterface
+   *   The page entity.
    */
-  public function getPageExecutable() {
+  public function getPage() {
     return $this->page;
   }
 
diff --git a/src/EventSubscriber/CurrentUserContext.php b/src/EventSubscriber/CurrentUserContext.php
index 2870954..7d60cfe 100644
--- a/src/EventSubscriber/CurrentUserContext.php
+++ b/src/EventSubscriber/CurrentUserContext.php
@@ -65,7 +65,7 @@ public function onPageContext(PageManagerContextEvent $event) {
     $cacheability = new CacheableMetadata();
     $cacheability->setCacheContexts(['user']);
     $context->addCacheableDependency($cacheability);
-    $event->getPageExecutable()->addContext('current_user', $context);
+    $event->getPage()->addContext('current_user', $context);
   }
 
   /**
diff --git a/src/EventSubscriber/RouteParamContext.php b/src/EventSubscriber/RouteParamContext.php
index 68ec72c..557351e 100644
--- a/src/EventSubscriber/RouteParamContext.php
+++ b/src/EventSubscriber/RouteParamContext.php
@@ -59,8 +59,8 @@ public function __construct(RouteProviderInterface $route_provider, RequestStack
    */
   public function onPageContext(PageManagerContextEvent $event) {
     $request = $this->requestStack->getCurrentRequest();
-    $executable = $event->getPageExecutable();
-    $routes = $this->routeProvider->getRoutesByPattern($executable->getPage()->getPath())->all();
+    $page = $event->getPage();
+    $routes = $this->routeProvider->getRoutesByPattern($page->getPath())->all();
     $route = reset($routes);
 
     if ($route && $route_contexts = $route->getOption('parameters')) {
@@ -84,7 +84,7 @@ public function onPageContext(PageManagerContextEvent $event) {
         $context = new Context(new ContextDefinition($route_context['type'], $context_name, FALSE), $value);
         $context->addCacheableDependency($cacheability);
 
-        $executable->addContext($route_context_name, $context);
+        $page->addContext($route_context_name, $context);
       }
     }
   }
diff --git a/src/EventSubscriber/StaticContext.php b/src/EventSubscriber/StaticContext.php
deleted file mode 100644
index c6b39b2..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\EntityRepositoryInterface;
-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 repository.
-   *
-   * @var \Drupal\Core\Entity\EntityRepositoryInterface
-   */
-  protected $entityRepository;
-
-  /**
-   * Constructs a new StaticContext.
-   *
-   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
-   *   The entity repository.
-   */
-  public function __construct(EntityRepositoryInterface $entity_repository) {
-    $this->entityRepository = $entity_repository;
-  }
-
-  /**
-   * 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->entityRepository, $static_context['value']);
-      $executable->addContext($name, $context);
-    }
-
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getSubscribedEvents() {
-    $events[PageManagerEvents::PAGE_CONTEXT][] = 'onPageContext';
-    return $events;
-  }
-
-}
diff --git a/src/Form/PageEditForm.php b/src/Form/PageEditForm.php
index 62f7242..eb60c33 100644
--- a/src/Form/PageEditForm.php
+++ b/src/Form/PageEditForm.php
@@ -33,76 +33,6 @@ public function form(array $form, FormStateInterface $form_state) {
     $attributes = $this->getAjaxAttributes();
     $add_button_attributes = $this->getAjaxButtonAttributes();
 
-    $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['variant_section'] = [
       '#type' => 'details',
diff --git a/src/Form/PageVariantEditForm.php b/src/Form/PageVariantEditForm.php
index 4a25382..76cc2cc 100644
--- a/src/Form/PageVariantEditForm.php
+++ b/src/Form/PageVariantEditForm.php
@@ -38,6 +38,8 @@ public function form(array $form, FormStateInterface $form_state) {
 
     $form['selection_section'] = $this->buildSelectionForm();
 
+    $form['context'] = $this->buildContextForm();
+
     return $form;
   }
 
@@ -291,6 +293,96 @@ protected function buildSelectionForm() {
   }
 
   /**
+   * Builds the context form for a variant.
+   *
+   * @return array
+   */
+  protected function buildContextForm() {
+    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
+    $page_variant = $this->getEntity();
+
+    // Set up the attributes used by a modal to prevent duplication later.
+    $attributes = $this->getAjaxAttributes();
+    $add_button_attributes = $this->getAjaxButtonAttributes();
+
+    $form = [
+      '#type' => 'details',
+      '#title' => $this->t('Available context'),
+      '#open' => TRUE,
+    ];
+    $form['add'] = [
+      '#type' => 'link',
+      '#title' => $this->t('Add new static context'),
+      '#url' => Url::fromRoute('page_manager.static_context_add', [
+        'page' => $page_variant->get('page'),
+        'page_variant' => $page_variant->id(),
+      ]),
+      '#attributes' => $add_button_attributes,
+      '#attached' => [
+        'library' => [
+          'core/drupal.ajax',
+        ],
+      ],
+    ];
+    $form['available_context'] = [
+      '#type' => 'table',
+      '#header' => [
+        $this->t('Label'),
+        $this->t('Name'),
+        $this->t('Type'),
+        $this->t('Operations'),
+      ],
+      '#empty' => $this->t('There is no available context.'),
+    ];
+    $contexts = $page_variant->getContexts();
+    foreach ($contexts as $name => $context) {
+      $context_definition = $context->getContextDefinition();
+
+      $row = [];
+      $row['label'] = [
+        '#markup' => $context_definition->getLabel(),
+      ];
+      $row['machine_name'] = [
+        '#markup' => $name,
+      ];
+      $row['type'] = [
+        '#markup' => $context_definition->getDataType(),
+      ];
+
+      // Add operation links if the context is a static context.
+      $operations = [];
+      if ($page_variant->getStaticContext($name)) {
+        $operations['edit'] = [
+          'title' => $this->t('Edit'),
+          'url' => Url::fromRoute('page_manager.static_context_edit', [
+            'page' => $page_variant->get('page'),
+            'page_variant' => $page_variant->id(),
+            'name' => $name,
+          ]),
+          'attributes' => $attributes,
+        ];
+        $operations['delete'] = [
+          'title' => $this->t('Delete'),
+          'url' => Url::fromRoute('page_manager.static_context_delete', [
+            'page' => $page_variant->get('page'),
+            'page_variant' => $page_variant->id(),
+            'name' => $name,
+          ]),
+          'attributes' => $attributes,
+        ];
+      }
+      $row['operations'] = [
+        '#type' => 'operations',
+        '#links' => $operations,
+      ];
+
+      $form['available_context'][$name] = $row;
+    }
+    return $form;
+
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
diff --git a/src/Form/StaticContextDeleteForm.php b/src/Form/StaticContextDeleteForm.php
index 725ef49..c9ccf47 100644
--- a/src/Form/StaticContextDeleteForm.php
+++ b/src/Form/StaticContextDeleteForm.php
@@ -8,8 +8,8 @@
 namespace Drupal\page_manager\Form;
 
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageInterface;
 use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\page_manager\PageVariantInterface;
 
 /**
  * Provides a form for deleting an access condition.
@@ -17,11 +17,11 @@
 class StaticContextDeleteForm extends ConfirmFormBase {
 
   /**
-   * The page entity this selection condition belongs to.
+   * The page variant entity this selection condition belongs to.
    *
-   * @var \Drupal\page_manager\PageInterface
+   * @var \Drupal\page_manager\PageVariantInterface
    */
-  protected $page;
+  protected $pageVariant;
 
   /**
    * The static context's machine name.
@@ -41,14 +41,14 @@ public function getFormId() {
    * {@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']]);
+    return $this->t('Are you sure you want to delete the static context %label?', ['%label' => $this->pageVariant->getStaticContext($this->staticContext)['label']]);
   }
 
   /**
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->page->toUrl('edit-form');
+    return $this->pageVariant->toUrl('edit-form');
   }
 
   /**
@@ -61,8 +61,8 @@ public function getConfirmText() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $name = NULL) {
-    $this->page = $page;
+  public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $name = NULL) {
+    $this->pageVariant = $page_variant;
     $this->staticContext = $name;
     return parent::buildForm($form, $form_state);
   }
@@ -71,9 +71,9 @@ public function buildForm(array $form, FormStateInterface $form_state, PageInter
    * {@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();
+    drupal_set_message($this->t('The static context %label has been removed.', ['%label' => $this->pageVariant->getStaticContext($this->staticContext)['label']]));
+    $this->pageVariant->removeStaticContext($this->staticContext);
+    $this->pageVariant->save();
     $form_state->setRedirectUrl($this->getCancelUrl());
   }
 
diff --git a/src/Form/StaticContextEditForm.php b/src/Form/StaticContextEditForm.php
index 8cf3a3b..f576870 100644
--- a/src/Form/StaticContextEditForm.php
+++ b/src/Form/StaticContextEditForm.php
@@ -8,7 +8,7 @@
 namespace Drupal\page_manager\Form;
 
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageInterface;
+use Drupal\page_manager\PageVariantInterface;
 
 /**
  * Provides a form for adding a new static context.
@@ -39,8 +39,8 @@ protected function submitMessageText() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $name = '') {
-    $form = parent::buildForm($form, $form_state, $page, $name);
+  public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $name = '') {
+    $form = parent::buildForm($form, $form_state, $page_variant, $name);
     // The machine name of an existing context is read-only.
     $form['machine_name'] = array(
       '#type' => 'value',
diff --git a/src/Form/StaticContextFormBase.php b/src/Form/StaticContextFormBase.php
index 5416ee7..32b612c 100644
--- a/src/Form/StaticContextFormBase.php
+++ b/src/Form/StaticContextFormBase.php
@@ -13,7 +13,7 @@
 use Drupal\Core\Entity\EntityTypeRepositoryInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\page_manager\PageInterface;
+use Drupal\page_manager\PageVariantInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -22,11 +22,11 @@
 abstract class StaticContextFormBase extends FormBase {
 
   /**
-   * The page entity this static context belongs to.
+   * The page variant entity this static context belongs to.
    *
-   * @var \Drupal\page_manager\PageInterface
+   * @var \Drupal\page_manager\PageVariantInterface
    */
-  protected $page;
+  protected $pageVariant;
 
   /**
    * The static context configuration.
@@ -102,9 +102,9 @@ public static function create(ContainerInterface $container) {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, PageInterface $page = NULL, $name = '') {
-    $this->page = $page;
-    $this->staticContext = $this->page->getStaticContext($name);
+  public function buildForm(array $form, FormStateInterface $form_state, PageVariantInterface $page_variant = NULL, $name = '') {
+    $this->pageVariant = $page_variant;
+    $this->staticContext = $this->pageVariant->getStaticContext($name);
 
     // Allow the condition to add to the form.
     $form['label'] = [
@@ -190,13 +190,13 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       'type' => 'entity:' . $entity_type,
       'value' => $entity->uuid(),
     ];
-    $this->page->setStaticContext($form_state->getValue('machine_name'), $this->staticContext);
-    $this->page->save();
+    $this->pageVariant->setStaticContext($form_state->getValue('machine_name'), $this->staticContext);
+    $this->pageVariant->save();
 
     // Set the submission message.
     drupal_set_message($this->submitMessageText());
 
-    $form_state->setRedirectUrl($this->page->toUrl('edit-form'));
+    $form_state->setRedirectUrl($this->pageVariant->toUrl('edit-form'));
   }
 
   /**
@@ -227,7 +227,7 @@ protected function getEntityFromSelection($entity_type, $selection) {
    *   TRUE if the format exists, FALSE otherwise.
    */
   public function contextExists($name) {
-    return isset($this->page->getContexts()[$name]);
+    return isset($this->pageVariant->getContexts()[$name]);
   }
 
   /**
diff --git a/src/PageExecutable.php b/src/PageExecutable.php
deleted file mode 100644
index 190684f..0000000
--- a/src/PageExecutable.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\PageExecutable.
- */
-
-namespace Drupal\page_manager;
-
-use Drupal\Component\Plugin\Context\ContextInterface;
-use Drupal\page_manager\Event\PageManagerContextEvent;
-use Drupal\page_manager\Event\PageManagerEvents;
-
-/**
- * Represents a page entity during runtime execution.
- */
-class PageExecutable implements PageExecutableInterface {
-
-  /**
-   * The page entity.
-   *
-   * @var \Drupal\page_manager\PageInterface
-   */
-  protected $page;
-
-  /**
-   * An array of collected contexts.
-   *
-   * @var \Drupal\Component\Plugin\Context\ContextInterface[]
-   */
-  protected $contexts = [];
-
-  /**
-   * Constructs a new PageExecutable.
-   *
-   * @param \Drupal\page_manager\PageInterface $page
-   *   The page entity.
-   */
-  public function __construct(PageInterface $page) {
-    $this->page = $page;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPage() {
-    return $this->page;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getContexts() {
-    if (!$this->contexts) {
-      $this->eventDispatcher()->dispatch(PageManagerEvents::PAGE_CONTEXT, new PageManagerContextEvent($this));
-    }
-    return $this->contexts;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function addContext($name, ContextInterface $value) {
-    $this->contexts[$name] = $value;
-  }
-
-  /**
-   * Wraps the event dispatcher.
-   *
-   * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
-   *   The event dispatcher.
-   */
-  protected function eventDispatcher() {
-    return \Drupal::service('event_dispatcher');
-  }
-
-}
diff --git a/src/PageExecutableFactory.php b/src/PageExecutableFactory.php
deleted file mode 100644
index 752cb23..0000000
--- a/src/PageExecutableFactory.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\PageExecutableFactory.
- */
-
-namespace Drupal\page_manager;
-
-/**
- * Provides a factory for page executables.
- */
-class PageExecutableFactory implements PageExecutableFactoryInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function get(PageInterface $page) {
-    return new PageExecutable($page);
-  }
-
-}
diff --git a/src/PageExecutableFactoryInterface.php b/src/PageExecutableFactoryInterface.php
deleted file mode 100644
index b4908e2..0000000
--- a/src/PageExecutableFactoryInterface.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\PageExecutableFactoryInterface.
- */
-
-namespace Drupal\page_manager;
-
-/**
- * Interface implemented by factories for page executables.
- */
-interface PageExecutableFactoryInterface {
-
-  /**
-   * Instantiates a PageExecutable class for the given page.
-   *
-   * @param \Drupal\page_manager\PageInterface $page
-   *   The page entity.
-   */
-  public function get(PageInterface $page);
-
-}
diff --git a/src/PageExecutableInterface.php b/src/PageExecutableInterface.php
deleted file mode 100644
index e1bd96c..0000000
--- a/src/PageExecutableInterface.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\page_manager\PageExecutableInterface.
- */
-
-namespace Drupal\page_manager;
-
-use Drupal\Component\Plugin\Context\ContextInterface;
-
-/**
- * Interface implemented by page executables.
- *
- * A page executable represents a page entity during runtime execution.
- *
- * @see \Drupal\page_manager\PageInterface
- */
-interface PageExecutableInterface {
-
-  /**
-   * Retrieves the underlying page entity.
-   *
-   * @return \Drupal\page_manager\PageInterface
-   *   The page entity.
-   */
-  public function getPage();
-
-  /**
-   * Gets the values for all defined contexts.
-   *
-   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
-   *   An array of set context values, keyed by context name.
-   */
-  public function getContexts();
-
-  /**
-   * Sets the context for a given name.
-   *
-   * @param string $name
-   *   The name of the context.
-   * @param \Drupal\Component\Plugin\Context\ContextInterface $value
-   *   The context to add.
-   *
-   * @return $this
-   */
-  public function addContext($name, ContextInterface $value);
-
-}
diff --git a/src/PageInterface.php b/src/PageInterface.php
index 08d0cd3..cf09049 100644
--- a/src/PageInterface.php
+++ b/src/PageInterface.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\page_manager;
 
+use Drupal\Component\Plugin\Context\ContextInterface;
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
 
@@ -24,13 +25,6 @@
   public function status();
 
   /**
-   * Returns the executable instance for this page.
-   *
-   * @return \Drupal\page_manager\PageExecutableInterface
-   */
-  public function getExecutable();
-
-  /**
    * Returns the path for the page entity.
    *
    * @return string
@@ -134,52 +128,23 @@ public function removeAccessCondition($condition_id);
   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.
+   * Gets the values for all defined contexts.
    *
-   * @return $this
+   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
+   *   An array of set context values, keyed by context name.
    */
-  public function setStaticContext($name, $configuration);
+  public function getContexts();
 
   /**
-   * Removes a specific static context.
+   * Sets the context for a given name.
    *
    * @param string $name
-   *   The static context unique name.
+   *   The name of the context.
+   * @param \Drupal\Component\Plugin\Context\ContextInterface $value
+   *   The context to add.
    *
    * @return $this
    */
-  public function removeStaticContext($name);
-
-  /**
-   * Gets the values for all defined contexts.
-   *
-   * @return \Drupal\Component\Plugin\Context\ContextInterface[]
-   *   An array of set context values, keyed by context name.
-   */
-  public function getContexts();
+  public function addContext($name, ContextInterface $value);
 
 }
diff --git a/src/PageVariantInterface.php b/src/PageVariantInterface.php
index 455e7de..12110c7 100644
--- a/src/PageVariantInterface.php
+++ b/src/PageVariantInterface.php
@@ -30,6 +30,13 @@ public function getVariantPlugin();
   public function getVariantPluginId();
 
   /**
+   * Gets the page this variant is on.
+   *
+   * @return \Drupal\page_manager\PageInterface
+   */
+  public function getPage();
+
+  /**
    * Gets the values for all defined contexts.
    *
    * @return \Drupal\Core\Plugin\Context\ContextInterface[]
@@ -100,4 +107,45 @@ public function removeSelectionCondition($condition_id);
    */
   public function getSelectionLogic();
 
+  /**
+   * 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);
+
 }
diff --git a/src/Tests/StaticContextTest.php b/src/Tests/StaticContextTest.php
index 2e8f8d9..31af705 100644
--- a/src/Tests/StaticContextTest.php
+++ b/src/Tests/StaticContextTest.php
@@ -53,7 +53,17 @@ public function testStaticContext() {
     ];
     $this->drupalPostForm('admin/structure/page_manager/add', $edit_page, 'Save');
 
-    // Add a static context for each node to the page.
+    // Add a new variant.
+    $this->clickLink('Add new variant');
+    $this->clickLink('Block page');
+    $variant_edit = [
+      'label' => 'Static context blocks',
+      'id' => 'block_page',
+      'variant_settings[page_title]' => 'Static context test page',
+    ];
+    $this->drupalPostForm(NULL, $variant_edit, 'Save');
+
+    // Add a static context for each node to the page variant.
     $contexts = array(
       array(
         'title' => 'Static Node',
@@ -78,17 +88,6 @@ public function testStaticContext() {
       $this->assertText('The ' . $edit['label'] . ' static context has been added.');
     }
 
-    // Add a new variant.
-    $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']);
-    $this->clickLink('Add new variant');
-    $this->clickLink('Block page');
-    $variant_edit = [
-      'label' => 'Static context blocks',
-      'id' => 'block_page',
-      'variant_settings[page_title]' => 'Static context test page',
-    ];
-    $this->drupalPostForm(NULL, $variant_edit, 'Save');
-
     // Add a block that renders the node from the first static context.
     $this->clickLink('Add new block');
     $this->clickLink('Entity view (Content)');
@@ -123,7 +122,8 @@ public function testStaticContext() {
 
     // Change the second static context to the first node.
     $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']);
-    $this->clickLink(t('Edit'), 1);
+    $this->clickLink('Edit');
+    $this->clickLink('Edit', 3);
     $edit = array(
       'label' => 'Static Node 2 edited',
       'entity_type' => 'node',
@@ -142,7 +142,8 @@ public function testStaticContext() {
 
     // Change the first static context to the second node.
     $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']);
-    $this->clickLink(t('Edit'));
+    $this->clickLink('Edit');
+    $this->clickLink('Edit', 2);
     $edit = array(
       'label' => 'Static Node edited',
       'entity_type' => 'node',
@@ -151,10 +152,8 @@ public function testStaticContext() {
     $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 variant.
-    $this->clickLink(t('Delete'), 1);
+    $this->clickLink('Delete', 1);
     $this->drupalPostForm(NULL, NULL, t('Delete'));
 
     // Make sure only the second static context's node is rendered on the page.
@@ -166,7 +165,8 @@ public function testStaticContext() {
 
     // 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->clickLink('Edit');
+    $this->clickLink('Delete', 1);
     $this->drupalPostForm(NULL, NULL, t('Delete'));
     $this->assertText('The static context ' . $edit['label'] . ' has been removed.');
     $this->drupalGet('admin/structure/page_manager/manage/' . $edit_page['id']);
diff --git a/tests/src/Unit/ContextMapperTest.php b/tests/src/Unit/ContextMapperTest.php
new file mode 100644
index 0000000..fd8067c
--- /dev/null
+++ b/tests/src/Unit/ContextMapperTest.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\page_manager\Unit\ContextMapperTest.
+ */
+
+namespace Drupal\Tests\page_manager\Unit;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Entity\EntityRepositoryInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
+use Drupal\Core\TypedData\TypedDataManager;
+use Drupal\page_manager\Context\EntityLazyLoadContext;
+use Drupal\page_manager\ContextMapper;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\page_manager\ContextMapper
+ *
+ * @group PageManager
+ */
+class ContextMapperTest extends UnitTestCase {
+
+  /**
+   * The typed data manager.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManager|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $typedDataManager;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityRepositoryInterface|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $entityRepository;
+
+  /**
+   * @var \Drupal\page_manager\ContextMapper
+   */
+  protected $staticContext;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->typedDataManager = $this->prophesize(TypedDataManager::class);
+    $this->entityRepository = $this->prophesize(EntityRepositoryInterface::class);
+    $this->staticContext = new ContextMapper($this->entityRepository->reveal());
+
+    $container = new ContainerBuilder();
+    $container->set('typed_data_manager', $this->typedDataManager->reveal());
+    \Drupal::setContainer($container);
+  }
+
+  /**
+   * @covers ::getContextValues
+   */
+  public function testGetContextValues() {
+    $input = [];
+    $actual = $this->staticContext->getContextValues($input);
+    $this->assertEquals([], $actual);
+  }
+
+  /**
+   * @covers ::getContextValues
+   */
+  public function testGetContextValuesContext() {
+    $data_definition = DataDefinition::createFromDataType('integer');
+    $typed_data = IntegerData::createInstance($data_definition);
+    $this->typedDataManager->createDataDefinition('integer')->willReturn($data_definition);
+    $this->typedDataManager->getDefaultConstraints($data_definition)->willReturn([]);
+    $this->typedDataManager->create($data_definition, 5)->willReturn($typed_data);
+
+    $input = [
+      'foo' => [
+        'label' => 'Foo',
+        'type' => 'integer',
+        'value' => 5,
+      ],
+    ];
+    $expected = new Context(new ContextDefinition('integer', 'Foo'), 5);
+    $actual = $this->staticContext->getContextValues($input)['foo'];
+    $this->assertEquals($expected, $actual);
+  }
+
+  /**
+   * @covers ::getContextValues
+   */
+  public function testGetContextValuesEntityContext() {
+    $input = [
+      'foo' => [
+        'label' => 'Foo',
+        'type' => 'entity:node',
+        'value' => 'the_node_uuid',
+      ],
+    ];
+    $expected = new EntityLazyLoadContext(new ContextDefinition('entity:node', 'Foo'), $this->entityRepository->reveal(), 'the_node_uuid');
+    $actual = $this->staticContext->getContextValues($input)['foo'];
+    $this->assertEquals($expected, $actual);
+  }
+
+}
diff --git a/tests/src/Unit/CurrentUserContextTest.php b/tests/src/Unit/CurrentUserContextTest.php
index 6ec9b5c..ec7ab04 100644
--- a/tests/src/Unit/CurrentUserContextTest.php
+++ b/tests/src/Unit/CurrentUserContextTest.php
@@ -47,9 +47,7 @@ public function testOnPageContext() {
         return $data_definition;
       });
 
-    $this->executable->expects($this->once())
-      ->method('addContext')
-      ->with('current_user', $this->isInstanceOf(Context::class));
+    $this->page->addContext('current_user', Argument::type(Context::class))->shouldBeCalled();
 
     $user_storage = $this->prophesize(EntityStorageInterface::class);
     $user_storage->load(1)->willReturn($user->reveal());
diff --git a/tests/src/Unit/PageAccessTest.php b/tests/src/Unit/PageAccessTest.php
index a909e7e..f3ce5ed 100644
--- a/tests/src/Unit/PageAccessTest.php
+++ b/tests/src/Unit/PageAccessTest.php
@@ -15,7 +15,6 @@
 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;
@@ -75,12 +74,9 @@ public function setUp() {
    * @covers ::checkAccess
    */
   public function testAccessView() {
-    $executable = $this->prophesize(PageExecutableInterface::class);
-    $executable->getContexts()->willReturn([]);
-
     $page = $this->prophesize(PageInterface::class);
 
-    $page->getExecutable()->willReturn($executable->reveal());
+    $page->getContexts()->willReturn([]);
     $page->getAccessConditions()->willReturn([]);
     $page->getAccessLogic()->willReturn('and');
     $page->status()->willReturn(TRUE);
diff --git a/tests/src/Unit/PageContextTestBase.php b/tests/src/Unit/PageContextTestBase.php
index 7cdd05b..4172675 100644
--- a/tests/src/Unit/PageContextTestBase.php
+++ b/tests/src/Unit/PageContextTestBase.php
@@ -10,7 +10,7 @@
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\TypedData\TypedDataManager;
 use Drupal\page_manager\Event\PageManagerContextEvent;
-use Drupal\page_manager\PageExecutable;
+use Drupal\page_manager\PageInterface;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -21,16 +21,16 @@
   /**
    * The typed data manager.
    *
-   * @var \Drupal\Core\TypedData\TypedDataManager|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\TypedData\TypedDataManager|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $typedDataManager;
 
   /**
-   * The executable for the page entity.
+   * The page entity.
    *
-   * @var \Drupal\page_manager\PageExecutable|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\page_manager\PageInterface|\Prophecy\Prophecy\ProphecyInterface
    */
-  protected $executable;
+  protected $page;
 
   /**
    * The event.
@@ -52,12 +52,9 @@ protected function setUp() {
     $container->set('typed_data_manager', $this->typedDataManager->reveal());
     \Drupal::setContainer($container);
 
-    $this->executable = $this->getMockBuilder(PageExecutable::class)
-      ->disableOriginalConstructor()
-      ->setMethods(['getPage', 'addContext'])
-      ->getMock();
+    $this->page = $this->prophesize(PageInterface::class);
 
-    $this->event = new PageManagerContextEvent($this->executable);
+    $this->event = new PageManagerContextEvent($this->page->reveal());
   }
 
 }
diff --git a/tests/src/Unit/PageExecutableTest.php b/tests/src/Unit/PageExecutableTest.php
deleted file mode 100644
index 739452c..0000000
--- a/tests/src/Unit/PageExecutableTest.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\page_manager\Unit\PageExecutableTest.
- */
-
-namespace Drupal\Tests\page_manager\Unit;
-
-use Drupal\Core\DependencyInjection\ContainerBuilder;
-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\Tests\UnitTestCase;
-use Prophecy\Argument;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-
-/**
- * Tests the PageExecutable.
- *
- * @coversDefaultClass \Drupal\page_manager\PageExecutable
- *
- * @group PageManager
- */
-class PageExecutableTest extends UnitTestCase {
-
-  /**
-   * @var \Drupal\page_manager\PageInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $page;
-
-  /**
-   * @var \Drupal\page_manager\PageExecutable
-   */
-  protected $exectuable;
-
-  /**
-   * {@inheritdoc}
-   *
-   * @covers ::__construct
-   */
-  protected function setUp() {
-    parent::setUp();
-    $this->page = $this->prophesize(PageInterface::class);
-    $this->exectuable = new PageExecutable($this->page->reveal());
-  }
-
-  /**
-   * @covers ::getPage
-   */
-  public function testGetPage() {
-    $this->assertSame($this->page->reveal(), $this->exectuable->getPage());
-  }
-
-  /**
-   * @covers ::addContext
-   */
-  public function testAddContext() {
-    $context = new Context(new ContextDefinition('bar'));
-    $this->exectuable->addContext('foo', $context);
-    $contexts = $this->exectuable->getContexts();
-    $this->assertSame(['foo' => $context], $contexts);
-  }
-
-  /**
-   * @covers ::getContexts
-   */
-  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);
-      });
-
-    $container = new ContainerBuilder();
-    $container->set('event_dispatcher', $event_dispatcher->reveal());
-    \Drupal::setContainer($container);
-
-    $contexts = $this->exectuable->getContexts();
-    $this->assertSame(['foo' => $context], $contexts);
-  }
-
-}
diff --git a/tests/src/Unit/PageTest.php b/tests/src/Unit/PageTest.php
index ca7ebfc..1e48b75 100644
--- a/tests/src/Unit/PageTest.php
+++ b/tests/src/Unit/PageTest.php
@@ -10,9 +10,15 @@
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
 use Drupal\page_manager\Entity\Page;
+use Drupal\page_manager\Event\PageManagerContextEvent;
+use Drupal\page_manager\Event\PageManagerEvents;
 use Drupal\page_manager\PageVariantInterface;
 use Drupal\Tests\UnitTestCase;
+use Prophecy\Argument;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Tests the Page entity.
@@ -24,6 +30,20 @@
 class PageTest extends UnitTestCase {
 
   /**
+   * @var \Drupal\page_manager\Entity\Page
+   */
+  protected $page;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->page = new Page(['id' => 'the_page'], 'page');
+  }
+
+  /**
    * @covers ::getVariants
    */
   public function testGetVariants() {
@@ -47,11 +67,40 @@ public function testGetVariants() {
     $container->set('entity_type.manager', $entity_type_manager->reveal());
     \Drupal::setContainer($container);
 
-    $page = new Page(['id' => 'the_page'], 'page');
-    $variants = $page->getVariants();
+    $variants = $this->page->getVariants();
     $this->assertSame(['variant2' => $variant2->reveal(), 'variant1' => $variant1->reveal()], $variants);
-    $variants = $page->getVariants();
+    $variants = $this->page->getVariants();
     $this->assertSame(['variant2' => $variant2->reveal(), 'variant1' => $variant1->reveal()], $variants);
   }
 
+  /**
+   * @covers ::addContext
+   */
+  public function testAddContext() {
+    $context = new Context(new ContextDefinition('bar'));
+    $this->page->addContext('foo', $context);
+    $contexts = $this->page->getContexts();
+    $this->assertSame(['foo' => $context], $contexts);
+  }
+
+  /**
+   * @covers ::getContexts
+   */
+  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]->getPage()->addContext('foo', $context);
+      });
+
+    $container = new ContainerBuilder();
+    $container->set('event_dispatcher', $event_dispatcher->reveal());
+    \Drupal::setContainer($container);
+
+    $contexts = $this->page->getContexts();
+    $this->assertSame(['foo' => $context], $contexts);
+  }
+
 }
diff --git a/tests/src/Unit/RouteParamContextTest.php b/tests/src/Unit/RouteParamContextTest.php
index 7029eb4..603a08a 100644
--- a/tests/src/Unit/RouteParamContextTest.php
+++ b/tests/src/Unit/RouteParamContextTest.php
@@ -12,7 +12,6 @@
 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;
@@ -54,18 +53,11 @@ public function testOnPageContext() {
         return $data_definition;
       });
 
-    $page = $this->prophesize(PageInterface::class);
-    $this->executable->expects($this->once())
-      ->method('getPage')
-      ->will($this->returnValue($page->reveal()));
-    $page->getPath()->willReturn('/test_route');
+    $this->page->getPath()->willReturn('/test_route');
 
-    $this->executable->expects($this->at(1))
-      ->method('addContext')
-      ->with('foo', $this->isInstanceOf(Context::class));
-    $this->executable->expects($this->at(2))
-      ->method('addContext')
-      ->with('baz', $this->isInstanceOf(Context::class));
+    $this->page->addContext('foo', Argument::type(Context::class))->shouldBeCalled();
+    $this->page->addContext('baz', Argument::type(Context::class))->shouldBeCalled();
+    $this->page->addContext('page', Argument::type(Context::class))->shouldBeCalled();
 
     $collection->add('test_route', new Route('/test_route', [], [], [
       'parameters' => [
@@ -94,14 +86,9 @@ public function testOnPageContextEmpty() {
     $request_stack = new RequestStack();
     $request_stack->push($request);
 
-    $page = $this->prophesize(PageInterface::class);
-    $this->executable->expects($this->once())
-      ->method('getPage')
-      ->will($this->returnValue($page->reveal()));
-    $page->getPath()->willReturn('/test_route');
+    $this->page->getPath()->willReturn('/test_route');
 
-    $this->executable->expects($this->never())
-      ->method('addContext');
+    $this->page->addContext(Argument::cetera())->shouldNotBeCalled();
 
     // Set up a request with one of the expected parameters as an attribute.
     $request->attributes->add(['foo' => 'banana']);
