diff --git a/config/schema/panelizer.schema.yml b/config/schema/panelizer.schema.yml
index 9b91bd3..8b873a9 100644
--- a/config/schema/panelizer.schema.yml
+++ b/config/schema/panelizer.schema.yml
@@ -1,4 +1,14 @@
 # Config schema for Panelizer.
+panelizer.default.display:
+  type: display_variant.plugin.panels_variant
+  label: 'Panelizer display default'
+  mapping:
+    static_context:
+      type: ctools.context
+      label: 'Contexts'
+    pattern:
+      type: string
+      label: 'Pattern plugin ID'
 
 core.entity_view_display.*.*.*.third_party.panelizer:
   type: mapping
@@ -9,10 +19,15 @@ core.entity_view_display.*.*.*.third_party.panelizer:
     custom:
       type: boolean
       description: 'Are custom overrides allowed?'
+    allow:
+      type: boolean
+      description: 'Is choosing between available defaults during entity creation allowed?'
+    default:
+      type: string
+      description: 'Default display ID'
     displays:
       type: sequence
       description: 'Default displays'
       sequence:
-        type: display_variant.plugin.panels_variant
-        description: 'Default display'
-
+        type: panelizer.default.display
+        description: 'Default displays'
diff --git a/css/panelizer.admin.css b/css/panelizer.admin.css
new file mode 100644
index 0000000..1aa3794
--- /dev/null
+++ b/css/panelizer.admin.css
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * Styles for Panelizer wizard admin.
+ */
+
+/* Narrow screens */
+
+.panelizer-wizard-tree,
+.panelizer-wizard-form {
+  box-sizing: border-box;
+}
+
+/**
+ * Wizard actions across the top.
+ */
+.panelizer-wizard-actions {
+  text-align: right; /* LTR */
+}
+.panelizer-wizard-actions ul.inline,
+.panelizer-wizard-actions ul.inline li {
+  display: inline-block;
+  margin: 0;
+}
+.panelizer-wizard-actions ul.inline {
+  border-top: 1px solid black;
+  border-left: 1px solid black;
+}
+.panelizer-wizard-actions ul.inline li {
+  border-right: 1px solid black;
+  padding: .5em;
+}
+
+/**
+ * The tree of wizard steps.
+ */
+.panelizer-wizard-tree ul {
+  margin: 0;
+  padding: 0;
+  list-style: none;
+}
+.panelizer-wizard-tree ul > li > ul {
+  margin-left: 1em;
+}
+.panelizer-wizard-tree > ul {
+  border: 1px solid black;
+  padding-bottom: .5em;
+  margin-bottom: 20px;
+}
+.panelizer-wizard-tree li {
+  border-bottom: 1px solid black;
+  padding: .5em;
+  padding-right: 0;
+}
+.panelizer-wizard-tree li:last-child {
+  border-bottom: 0;
+  padding-bottom: 0;
+}
+
+/**
+ * The wizard form.
+ */
+.panelizer-wizard-form {
+  border: 1px solid black;
+  padding: 1em;
+  margin-bottom: 20px;
+}
+
+/* Wide screens */
+@media
+  screen and (min-width: 780px),
+  (orientation: landscape) and (min-device-height: 780px) {
+
+  /**
+   * Overall layout.
+   */
+  .panelizer-wizard-tree {
+    float: left; /* LTR */
+    width: 20%;
+  }
+  .panelizer-wizard-form {
+    float: left; /* LTR */
+    width: 80%;
+  }
+  .panelizer-wizard-form-actions {
+    margin-left: 20%; /* LTR */
+  }
+
+  /**
+   * Make the borders look nice.
+   */
+  .panelizer-wizard-tree > ul {
+    border-right: 0; /* LTR */
+  }
+  .panelizer-wizard-form {
+    min-height: 400px;
+  }
+
+  /**
+   * Right-to-left support.
+   */
+  [dir="rtl"] .panelizer-wizard-tree,
+  [dir="rtl"] .panelizer-wizard-form {
+    float: right;
+  }
+  [dir="rtl"] .panelizer-wizard-form-actions {
+    margin-left: 0;
+    margin-right: 20%;
+  }
+  [dir="rtl"] .panelizer-wizard-tree > ul {
+    border-right: 1px solid black;
+    border-left: 0;
+  }
+}
diff --git a/js/panelizer-default-form.js b/js/panelizer-default-form.js
index 2713e2e..76aafe8 100644
--- a/js/panelizer-default-form.js
+++ b/js/panelizer-default-form.js
@@ -10,10 +10,10 @@
       function update_form() {
         var $core_form = $('#field-display-overview-wrapper');
         if ($panelizer_checkbox.is(':checked')) {
-          $core_form.hide();
+          $core_form.fadeOut();
         }
         else {
-          $core_form.show();
+          $core_form.fadeIn();
         }
       }
 
diff --git a/js/panels_ipe/panels_ipe.js b/js/panels_ipe/panels_ipe.js
index 5101a64..215a754 100644
--- a/js/panels_ipe/panels_ipe.js
+++ b/js/panels_ipe/panels_ipe.js
@@ -32,10 +32,7 @@
         revert_tab.set({loading: true});
         $.ajax({
           url: drupalSettings.path.baseUrl + 'admin/panelizer/panels_ipe/' + entity.entity_type_id + '/' + entity.entity_id + '/' + entity.view_mode + '/revert_to_default',
-          data: {
-            // @todo: Use the default this entity is on.
-            default: 'default'
-          },
+          data: {},
           type: 'POST'
         }).done(function (data) {
           location.reload();
diff --git a/panelizer.libraries.yml b/panelizer.libraries.yml
index 6e9a5b2..50b9c76 100644
--- a/panelizer.libraries.yml
+++ b/panelizer.libraries.yml
@@ -17,4 +17,8 @@ panels_ipe:
       css/panels_ipe.css: {}
   dependencies:
     - panels_ipe/panels_ipe
-
+wizard_admin:
+  version: VERSION
+  css:
+    layout:
+      css/panelizer.admin.css: {}
diff --git a/panelizer.links.action.yml b/panelizer.links.action.yml
new file mode 100644
index 0000000..14e71da
--- /dev/null
+++ b/panelizer.links.action.yml
@@ -0,0 +1,5 @@
+panelizer.default.add:
+  route_name: panelizer.wizard.add
+  title: 'Add panelizer default'
+  class: \Drupal\panelizer\Menu\AddDefaultLocalAction
+  deriver: \Drupal\panelizer\Plugin\AddDefaultLinkDeriver
diff --git a/panelizer.module b/panelizer.module
index 235bd45..2c817a6 100644
--- a/panelizer.module
+++ b/panelizer.module
@@ -4,10 +4,11 @@
  * Hook implementations for the Panelizer module.
  */
 
-use \Drupal\Core\Entity\FieldableEntityInterface;
-use \Drupal\Core\Entity\RevisionableInterface;
-use \Drupal\Core\Form\FormStateInterface;
-use \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Entity\RevisionableInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant;
 
 /**
  * Implements hook_theme().
@@ -17,10 +18,62 @@ function panelizer_theme() {
     'panelizer_view_mode' => [
       'render element' => 'element',
     ],
+    'panelizer_wizard_form' => [
+      'render element' => 'form',
+    ],
+    'panelizer_wizard_tree' => [
+      'variables' => [
+        'wizard' => NULL,
+        'cached_values' => [],
+        'tree' => [],
+        'divider' => ' » ',
+        'step' => NULL,
+      ],
+    ],
   ];
 }
 
 /**
+ * Preprocess function for panelizer-wizard-tree.html.twig.
+ */
+function template_preprocess_panelizer_wizard_tree(&$variables) {
+  /** @var $wizard \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface */
+  $wizard = $variables['wizard'];
+  $cached_values = $variables['cached_values'];
+  $tree = $variables['tree'];
+  $variables['step'] = $wizard->getStep($cached_values);
+
+  foreach ($wizard->getOperations($cached_values) as $step => $operation) {
+    $parameters = $wizard->getNextParameters($cached_values);
+    // Override step to be the step we want.
+    $parameters['step'] = $step;
+
+    // Fill in parents if there are breadcrumbs.
+    $parent =& $tree;
+    if (isset($operation['breadcrumbs'])) {
+      foreach ($operation['breadcrumbs'] as $breadcrumb) {
+        $breadcrumb_string = (string) $breadcrumb;
+        if (!isset($parent[$breadcrumb_string])) {
+          $parent[$breadcrumb_string] = [
+            'title' => $breadcrumb,
+            'children' => [],
+          ];
+        }
+        $parent =& $parent[$breadcrumb_string]['children'];
+      }
+    }
+
+    $parent[$step] = [
+      'title' => !empty($operation['title']) ? $operation['title'] : '',
+      'url' => new Url($wizard->getRouteName(), $parameters),
+      'step' => $step,
+    ];
+  }
+
+  $variables['tree'] = $tree;
+}
+
+/**
  * Implements hook_entity_type_alter().
  */
 function panelizer_entity_type_alter(array &$entity_types) {
@@ -60,7 +113,7 @@ function panelizer_panels_build_alter(&$build, PanelsDisplayVariant $panels_disp
     list (,, $view_mode) = explode(':', $panels_display->getStorageId());
 
     // Get the default storage id.
-    $default = 'default';
+    list(,,, $default) = explode(':', $panels_display->getStorageId());
     if ($panels_display->getStorageType() == 'panelizer_field') {
       $panelizer_default_storage_id = implode(':', [$entity->getEntityTypeId(), $entity->bundle(), $view_mode, $default]);
     }
@@ -148,7 +201,7 @@ function panelizer_panels_ipe_panels_display_presave(PanelsDisplayVariant $panel
 }
 
 /**
- * Implements hook_form_alter().
+ * Implements hook_form_FORM_ID_alter().
  */
 function panelizer_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state) {
   /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
@@ -161,8 +214,15 @@ function panelizer_form_entity_view_display_edit_form_alter(&$form, FormStateInt
   /** @var \Drupal\panelizer\PanelizerInterface $panelizer */
   $panelizer = \Drupal::service('panelizer');
 
-  if ($panelizer_manager->hasDefinition($display->getTargetEntityTypeId())) {
-    $settings = $panelizer->getPanelizerSettings($display->getTargetEntityTypeId(), $display->getTargetBundle(), $display->getMode(), $display);
+  $entity_type_id = $display->getTargetEntityTypeId();
+  $bundle = $display->getTargetBundle();
+  $mode = $display->getMode();
+
+  if ($panelizer_manager->hasDefinition($entity_type_id)) {
+    $settings = $panelizer->getPanelizerSettings($entity_type_id, $bundle, $mode, $display);
+
+    // Always put the field table below the Panelizer options.
+    $form['fields']['#weight'] = 10;
 
     $form['panelizer'] = [
       '#tree' => TRUE,
@@ -170,42 +230,115 @@ function panelizer_form_entity_view_display_edit_form_alter(&$form, FormStateInt
     $form['panelizer']['enable'] = [
       '#type' => 'checkbox',
       '#title' => t('Panelize this view mode'),
-      '#default_value' => !empty($settings['enable']),
+      '#default_value' => isset($settings['enable']) ? $settings['enable'] : FALSE,
     ];
-    $form['panelizer']['custom'] = [
-      '#type' => 'checkbox',
-      '#title' => t('Allow custom overrides of each entity'),
-      '#default_value' => !empty($settings['custom']),
+
+    $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
+
+    $form['panelizer']['options'] = [
+      '#type' => 'details',
+      '#open' => TRUE,
+      '#title' => t('Panelizer options'),
       '#states' => [
         'visible' => [
-          ':input[name="panelizer[enable]"]' => ['checked' => TRUE],
-        ]
-      ]
+          '#edit-panelizer-enable' => ['checked' => TRUE],
+        ],
+      ],
+      '#parents' => ['panelizer'],
     ];
 
-    $form['#attached']['library'][] = 'panelizer/panelizer_default_form';
+    $form['panelizer']['options']['custom'] = [
+      '#type' => 'checkbox',
+      '#title' => t('Allow custom layouts for individual @entities', [
+        '@entities' => $entity_type->getPluralLabel(),
+      ]),
+      '#default_value' => isset($settings['custom']) ? $settings['custom'] : FALSE,
+    ];
+    $form['panelizer']['options']['allow'] = [
+      '#type' => 'checkbox',
+      '#title' => t('Allow users to select which layout to use'),
+      '#description' => t('When multiple defaults for a view mode are available it can be useful to allow content creators to choose which layout to use for a given @entity.', [
+        '@entity' => $entity_type->getSingularLabel(),
+      ]),
+      '#default_value' => isset($settings['allow']) ? $settings['allow'] : FALSE,
+    ];
+
+    // If this display mode is panelized, then show a link to its settings.
+    if (!empty($settings['enable'])) {
+      $form['#cache']['tags'][] = "panelizer_default:$entity_type_id:$bundle:$mode";
+      $form['panelizer']['displays'] = [
+        '#type' => 'table',
+        '#caption' => t('Panelized Displays'),
+        '#header' => [t('Name'), t('Use as default'), t('Operations')],
+      ];
+      foreach ($display->getThirdPartySetting('panelizer', 'displays', []) as $machine_name => $panels_display) {
+        // Reset operations when in the foreach loop.
+        $operations = [];
+        $display_name = $machine_name;
+        $machine_name ="{$entity_type_id}__{$bundle}__{$mode}__$machine_name";
+        $operations['edit'] = [
+          'title' => t('Edit'),
+          'url' => Url::fromRoute('panelizer.wizard.edit', ['machine_name' => $machine_name]),
+        ];
+        if ($settings['default'] != $display_name) {
+          $operations['set_default'] = [
+            'title' => t('Use as default'),
+            'url' => Url::fromRoute('panelizer.default.select', ['machine_name' => $machine_name]),
+          ];
+          $operations['delete'] = [
+            'title' => t('Delete'),
+            'url' => Url::fromRoute('panelizer.default.delete', ['machine_name' => $machine_name]),
+          ];
+        }
+        $form['panelizer']['displays'][$machine_name] = [
+          'label' => ['#markup' => $panels_display['label']],
+          'default' => ['#markup' => $settings['default'] == $display_name ? '&#10003;' : ''],
+          'operations' => [
+            'data' => [
+              '#type' => 'operations',
+              '#links' => $operations,
+            ]
+          ]
+        ];
+      }
+      $form['fields']['#access'] = FALSE;
+    }
 
-    $form['actions']['submit']['#submit'][] = 'panelizer_form_entity_view_display_edit_form_submit';
+    $form['#attached']['library'][] = 'panelizer/panelizer_default_form';
+    $form['actions']['submit']['#submit'][] = 'panelizer_form_entity_view_display_edit_submit';
   }
 }
 
-/**
- * Form submission callback for entity_view_display_edit_form.
- */
-function panelizer_form_entity_view_display_edit_form_submit($form, FormStateInterface $form_state) {
+function panelizer_form_entity_view_display_edit_submit(&$form, FormStateInterface $form_state) {
+  $rebuild = FALSE;
   /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
   $form_object = $form_state->getFormObject();
   /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
   $display = $form_object->getEntity();
-
-  $settings = [
-    'enable' => $form_state->getValue(['panelizer', 'enable']),
-    'custom' => $form_state->getValue(['panelizer', 'custom']),
-  ];
-
-  /** @var \Drupal\panelizer\PanelizerInterface $panelizer */
+  /** @var \Drupal\panelizer\Plugin\PanelizerEntityManager $panelizer_manager */
+  $panelizer_manager = \Drupal::service('plugin.manager.panelizer_entity');
+  /** @var \Drupal\panelizer\Panelizer $panelizer */
   $panelizer = \Drupal::service('panelizer');
-  $panelizer->setPanelizerSettings($display->getTargetEntityTypeId(), $display->getTargetBundle(), $display->getMode(), $settings, $display);
+
+  if ($panelizer_manager->hasDefinition($display->getTargetEntityTypeId())) {
+    $settings = $panelizer->getPanelizerSettings($display->getTargetEntityTypeId(), $display->getTargetBundle(), $display->getMode(), $display);
+    if ($settings['enable'] != $form_state->getValue(['panelizer', 'enable'])) {
+      $rebuild = TRUE;
+    }
+    $settings['enable'] = $form_state->getValue(['panelizer', 'enable']);
+    $settings['custom'] = $form_state->getValue(['panelizer', 'custom']);
+    $settings['allow'] = $form_state->getValue(['panelizer', 'allow']);
+    $panelizer->setPanelizerSettings($display->getTargetEntityTypeId(), $display->getTargetBundle(), $display->getMode(), $settings, $display);
+    if ($rebuild) {
+      \Drupal::service('router.builder')->rebuild();
+      /** @var \Drupal\Core\Menu\LocalActionManager $local_action_manager */
+      $local_action_manager = \Drupal::service('plugin.manager.menu.local_action');
+      $local_action_manager->clearCachedDefinitions();
+      // Manually reinitialize these.
+      $local_action_manager->getDefinitions();
+      \Drupal::service('cache.render')->invalidateAll();
+    }
+  }
 }
 
 /**
diff --git a/panelizer.routing.yml b/panelizer.routing.yml
index feac328..04189a6 100644
--- a/panelizer.routing.yml
+++ b/panelizer.routing.yml
@@ -12,3 +12,79 @@ panelizer.panels_ipe.revert_to_default:
     _method: 'POST'
     _permission: 'access panels in-place editing'
     _custom_access: '\Drupal\panelizer\Controller\PanelizerPanelsIPEController::accessRevertToDefault'
+
+# Wizard
+panelizer.wizard.add:
+  path: '/admin/structure/panelizer/add/{entity_type_id}/{bundle}/{view_mode_name}'
+  defaults:
+    _wizard: '\Drupal\panelizer\Wizard\PanelizerAddWizard'
+    _title: 'Panelizer Wizard'
+    tempstore_id: 'panelizer.wizard'
+  requirements:
+    _panelizer_default_access: 'TRUE'
+    _permission: 'administer panelizer'
+
+panelizer.wizard.add.step:
+  path: '/admin/structure/panelizer/add/{machine_name}/{step}'
+  defaults:
+    _wizard: '\Drupal\panelizer\Wizard\PanelizerAddWizard'
+    _title: 'Panelizer Wizard'
+    tempstore_id: 'panelizer.wizard'
+  requirements:
+    _permission: 'administer panelizer'
+
+panelizer.wizard.edit:
+  path: '/admin/structure/panelizer/edit/{machine_name}/{step}'
+  defaults:
+    _wizard: '\Drupal\panelizer\Wizard\PanelizerEditWizard'
+    _title: 'Panelizer Wizard'
+    tempstore_id: 'panelizer.wizard'
+    step: 'general'
+  requirements:
+    _permission: 'administer panelizer'
+
+panelizer.default.delete:
+  path: '/admin/structure/panelizer/delete/{machine_name}'
+  defaults:
+    _form: '\Drupal\panelizer\Form\PanelizerDefaultDelete'
+    _title: 'Delete panelizer default'
+  requirements:
+    _panelizer_field_ui_view_mode_access: 'TRUE'
+    _custom_access: '\Drupal\panelizer\Access\PanelizerDefaultsDisplayAccess::isNotDefaultDisplay'
+
+panelizer.default.select:
+  path: '/admin/structure/panelizer/set_default/{machine_name}'
+  defaults:
+    _form: '\Drupal\panelizer\Form\PanelizerDefaultSelect'
+    _title: 'Set as default'
+  requirements:
+    _panelizer_field_ui_view_mode_access: 'TRUE'
+    _custom_access: '\Drupal\panelizer\Access\PanelizerDefaultsDisplayAccess::isNotDefaultDisplay'
+
+# Contexts
+panelizer.wizard.step.context.add:
+  path: '/admin/panelizer/wizard/{machine_name}/contexts/add/{context_id}'
+  defaults:
+    _form: '\Drupal\panelizer\Form\PanelizerWizardContextConfigure'
+    _title: 'Add custom context'
+    tempstore_id: 'panelizer.wizard'
+  requirements:
+    _permission: 'administer panelizer'
+
+panelizer.wizard.step.context.edit:
+  path: '/admin/panelizer/wizard/{machine_name}/contexts/edit/{context_id}'
+  defaults:
+    _form: '\Drupal\panelizer\Form\PanelizerWizardContextConfigure'
+    _title: 'Edit context'
+    tempstore_id: 'panelizer.wizard'
+  requirements:
+    _permission: 'administer panelizer'
+
+panelizer.wizard.step.context.delete:
+  path: '/admin/panelizer/wizard/{machine_name}/context/delete/{context_id}'
+  defaults:
+    _form: '\Drupal\panelizer\Form\PanelizerWizardContextDeleteForm'
+    _title: 'Delete static context'
+    tempstore_id: 'panelizer.wizard'
+  requirements:
+    _permission: 'administer panelizer'
diff --git a/panelizer.services.yml b/panelizer.services.yml
index 20c1b5d..d36b86c 100644
--- a/panelizer.services.yml
+++ b/panelizer.services.yml
@@ -1,7 +1,17 @@
 services:
+  access_check.panelizer.view_mode:
+    class: Drupal\panelizer\Access\ViewModeAccessCheck
+    arguments: ['@access_check.field_ui.view_mode']
+    tags:
+      - { name: access_check, applies_to: _panelizer_field_ui_view_mode_access }
   plugin.manager.panelizer_entity:
     class: Drupal\panelizer\Plugin\PanelizerEntityManager
     parent: default_plugin_manager
   panelizer:
     class: Drupal\panelizer\Panelizer
-    arguments: ['@entity_type.manager', '@entity_type.bundle.info', '@entity_field.manager', '@plugin.manager.field.field_type', '@module_handler', '@current_user', '@plugin.manager.panelizer_entity', '@panels.display_manager', '@string_translation']
+    arguments: ['@entity_type.manager', '@entity_type.bundle.info', '@entity_field.manager', '@plugin.manager.field.field_type', '@module_handler', '@current_user', '@plugin.manager.panelizer_entity', '@panels.display_manager', '@string_translation', '@ctools.context_mapper']
+  panelizer.default.access:
+    class: Drupal\panelizer\Access\DefaultAccess
+    arguments: ['@panelizer']
+    tags:
+      - { name: access_check, applies_to: _panelizer_default_access }
diff --git a/src/Access/DefaultAccess.php b/src/Access/DefaultAccess.php
new file mode 100644
index 0000000..54b56c5
--- /dev/null
+++ b/src/Access/DefaultAccess.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Access\DefaultAccess.
+ */
+
+namespace Drupal\panelizer\Access;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Routing\Access\AccessInterface;
+use Drupal\panelizer\PanelizerInterface;
+
+class DefaultAccess implements AccessInterface {
+
+  /**
+   * The Panelizer service.
+   *
+   * @var \Drupal\panelizer\PanelizerInterface
+   */
+  protected $panelizer;
+
+  /**
+   * DefaultAccess constructor.
+   *
+   * @param \Drupal\panelizer\PanelizerInterface $panelizer
+   *   The Panelizer service.
+   */
+  public function __construct(PanelizerInterface $panelizer) {
+    $this->panelizer = $panelizer;
+  }
+
+  /**
+   * Determines access to a default Panelizer layout.
+   *
+   * @param string $entity_type_id
+   *   The panelized entity type ID.
+   * @param string $bundle
+   *   The panelized bundle ID.
+   * @param string $view_mode_name
+   *   The panelized view mode ID.
+   *
+   * @return \Drupal\Core\Access\AccessResult
+   */
+  public function access($entity_type_id, $bundle, $view_mode_name) {
+    $settings = $this->panelizer->getPanelizerSettings($entity_type_id, $bundle, $view_mode_name);
+    return $settings['enable'] ? AccessResult::allowed() : AccessResult::forbidden();
+  }
+
+}
diff --git a/src/Access/PanelizerDefaultsDisplayAccess.php b/src/Access/PanelizerDefaultsDisplayAccess.php
new file mode 100644
index 0000000..f29b193
--- /dev/null
+++ b/src/Access/PanelizerDefaultsDisplayAccess.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Controller\PanelizerDefaultsDisplayAccess.
+ */
+
+namespace Drupal\panelizer\Access;
+
+use Drupal\Core\Access\AccessResult;
+
+/**
+ * Provides a custom access checking mechanism for default displays.
+ */
+class PanelizerDefaultsDisplayAccess {
+
+  /**
+   * Custom access checker for determining the default display of the bundle.
+   *
+   * @param string $machine_name
+   *   The machine name of the default display.
+   *
+   * @return \Drupal\Core\Access\AccessResult
+   */
+  public function isNotDefaultDisplay($machine_name) {
+    list($entity_type, $bundle, $view_mode, $default) = explode('__', $machine_name);
+    /** @var \Drupal\panelizer\Panelizer $panelizer */
+    $panelizer = \Drupal::service('panelizer');
+    $settings = $panelizer->getPanelizerSettings($entity_type, $bundle, $view_mode);
+    if ($settings['default'] != $default) {
+      $access = AccessResult::allowed();
+    }
+    else {
+      $access = AccessResult::forbidden();
+    }
+    return $access->addCacheTags(["panelizer_default:$entity_type:$bundle:$view_mode", "panelizer_default:$entity_type:$bundle:$view_mode:$default"]);
+  }
+
+}
diff --git a/src/Access/PanelizerUIAccess.php b/src/Access/PanelizerUIAccess.php
new file mode 100644
index 0000000..e3cb1e7
--- /dev/null
+++ b/src/Access/PanelizerUIAccess.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Access\PanelizerUIAccess.
+ */
+
+namespace Drupal\panelizer\Access;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\ctools\Access\AccessInterface;
+
+class PanelizerUIAccess implements AccessInterface {
+
+  public function access(AccountInterface $account) {
+    return $account->hasPermission('administer panelizer') ? AccessResult::allowed() : AccessResult::forbidden();
+  }
+
+}
diff --git a/src/Access/ViewModeAccessCheck.php b/src/Access/ViewModeAccessCheck.php
new file mode 100644
index 0000000..db0ff21
--- /dev/null
+++ b/src/Access/ViewModeAccessCheck.php
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Access\ViewModeAccessCheck.
+ */
+
+namespace Drupal\panelizer\Access;
+
+use Drupal\Core\Routing\Access\AccessInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\field_ui\Access\ViewModeAccessCheck as FieldUIViewModeAccessCheck;
+use Symfony\Component\Routing\Route;
+
+/**
+ * An access check as an adapter around field_ui's custom access check.
+ */
+class ViewModeAccessCheck implements AccessInterface {
+
+  /**
+   * The decorated ViewModeAccessCheck from field_ui.
+   *
+   * @var \Drupal\field_ui\Access\ViewModeAccessCheck
+   */
+  protected $accessCheck;
+
+  /**
+   * ViewModeAccessCheck constructor.
+   *
+   * @param \Drupal\field_ui\Access\ViewModeAccessCheck $access_check
+   */
+  public function __construct(FieldUIViewModeAccessCheck $access_check) {
+    $this->accessCheck = $access_check;
+  }
+
+  /**
+   * Adapt the panelizer defaults access check to correspond to field ui.
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *   The original route definition.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route matched.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The current user's account.
+   * @param string $machine_name
+   *   The machine name of the panelizer default.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   * @throws \Exception
+   */
+  public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account, $machine_name) {
+    $parts = explode('__', $machine_name);
+    if (count($parts) != 4) {
+      throw new \Exception('The provided machine_name is not well formed.');
+    }
+    list($entity_type_id, $bundle, $view_mode) = $parts;
+    $defaults = [
+      'entity_type_id' => $entity_type_id,
+    ] + $route->getDefaults();
+    $route->setDefaults($defaults);
+    $route->setRequirement('_field_ui_view_mode_access', 'administer ' . $entity_type_id . ' display');
+    return $this->accessCheck->access($route, $route_match, $account, $view_mode, $bundle);
+  }
+
+}
diff --git a/src/Controller/PanelizerPanelsIPEController.php b/src/Controller/PanelizerPanelsIPEController.php
index d33ed64..7cebed9 100644
--- a/src/Controller/PanelizerPanelsIPEController.php
+++ b/src/Controller/PanelizerPanelsIPEController.php
@@ -57,16 +57,26 @@ class PanelizerPanelsIPEController extends ControllerBase {
    *   The entity.
    * @param string $view_mode
    *   The view mode.
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request.
    *
    * @return \Symfony\Component\HttpFoundation\Response
    *   An empty response.
    *
    * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
    */
-  public function revertToDefault(FieldableEntityInterface $entity, $view_mode, Request $request) {
-    $default = $request->get('default');
+  public function revertToDefault(FieldableEntityInterface $entity, $view_mode) {
+    // Get the bundle specific default display as a fallback.
+    $settings = $this->panelizer->getPanelizerSettings($entity->getEntityTypeId(), $entity->bundle(), $view_mode);
+    $default = $settings['default'];
+    // Check the entity for a documented default to which we should revert.
+    if ($entity->hasField('panelizer') && $entity->panelizer->first()) {
+      foreach ($entity->panelizer as $item) {
+        if ($item->view_mode == $view_mode && !empty($item->default)) {
+          $default = $item->default;
+          break;
+        }
+      }
+    }
+    // If we somehow ended up not having a default, throw an exception.
     if (empty($default)) {
       throw new BadRequestHttpException("Default name to revert to must be passed!");
     }
diff --git a/src/Form/PanelizerDefaultDelete.php b/src/Form/PanelizerDefaultDelete.php
new file mode 100644
index 0000000..9fbedf4
--- /dev/null
+++ b/src/Form/PanelizerDefaultDelete.php
@@ -0,0 +1,172 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerDefaultDelete.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\panelizer\PanelizerInterface;
+use Drupal\panels\PanelsDisplayManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class PanelizerDefaultDelete extends ConfirmFormBase {
+
+  /**
+   * The type of entity.
+   *
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
+   * The entity bundle.
+   *
+   * @var string
+   */
+  protected $bundle;
+
+  /**
+   * The view mode.
+   *
+   * @var string
+   */
+  protected $viewMode;
+
+  /**
+   * The panelizer default display ID.
+   *
+   * @var string
+   */
+  protected $displayId;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The Panelizer service.
+   *
+   * @var \Drupal\panelizer\PanelizerInterface
+   */
+  protected $panelizer;
+
+  /**
+   * The Panels display manager.
+   *
+   * @var \Drupal\panels\PanelsDisplayManagerInterface
+   */
+  protected $panelsDisplayManager;
+
+  /**
+   * The cache tag invalidator.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
+   */
+  protected $invalidator;
+
+  /**
+   * PanelizerDefaultDelete constructor.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\panelizer\PanelizerInterface $panelizer
+   *   The Panelizer service.
+   * @param \Drupal\panels\PanelsDisplayManagerInterface $panels_display_manager
+   *   The Panels display manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, PanelizerInterface $panelizer, PanelsDisplayManagerInterface $panels_display_manager, CacheTagsInvalidatorInterface $invalidator) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->panelizer = $panelizer;
+    $this->panelsDisplayManager = $panels_display_manager;
+    $this->invalidator = $invalidator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('panelizer'),
+      $container->get('panels.display_manager'),
+      $container->get('cache_tags.invalidator')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return 'Are you certain you want to delete this panelizer default?.';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    $bundle_entity_type = $this->entityTypeManager->getDefinition($this->entityTypeId)->getBundleEntityType();
+    if ($this->viewMode == 'default') {
+      $route = "entity.entity_view_display.{$this->entityTypeId}.default";
+      $arguments = [
+        $bundle_entity_type => $this->bundle,
+      ];
+    }
+    else {
+      $route = "entity.entity_view_display.{$this->entityTypeId}.view_mode";
+      $arguments = [
+        $bundle_entity_type => $this->bundle,
+        'view_mode_name' => $this->viewMode,
+      ];
+    }
+    return new Url($route, $arguments);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panelizer_default_delete';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $machine_name = NULL) {
+    list (
+      $this->entityTypeId,
+      $this->bundle,
+      $this->viewMode,
+      $this->displayId
+    ) = explode('__', $machine_name);
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $display = $this->panelizer->getEntityViewDisplay($this->entityTypeId, $this->bundle, $this->viewMode);
+    $displays = $this->panelizer->getDefaultPanelsDisplays($this->entityTypeId, $this->bundle, $this->viewMode, $display);
+    unset($displays[$this->displayId]);
+    foreach ($displays as $key => $value) {
+      $displays[$key] = $this->panelsDisplayManager->exportDisplay($value);
+    }
+    $display->setThirdPartySetting('panelizer', 'displays', $displays);
+    $display->save();
+    $form_state->setRedirectUrl($this->getCancelUrl());
+    $tag = "panelizer_default:{$this->entityTypeId}:{$this->bundle}:{$this->viewMode}:{$this->displayId}";
+    $this->invalidator->invalidateTags([$tag]);
+  }
+
+}
diff --git a/src/Form/PanelizerDefaultSelect.php b/src/Form/PanelizerDefaultSelect.php
new file mode 100644
index 0000000..2558c90
--- /dev/null
+++ b/src/Form/PanelizerDefaultSelect.php
@@ -0,0 +1,144 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerDefaultSelect.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\panelizer\PanelizerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class PanelizerDefaultSelect extends ConfirmFormBase {
+
+  /**
+   * The type of entity.
+   *
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
+   * The entity bundle.
+   *
+   * @var string
+   */
+  protected $bundle;
+
+  /**
+   * The view mode.
+   *
+   * @var string
+   */
+  protected $viewMode;
+
+  /**
+   * The panelizer default display ID.
+   *
+   * @var string
+   */
+  protected $displayId;
+
+  /**
+   * The Panelizer service.
+   *
+   * @var \Drupal\panelizer\PanelizerInterface
+   */
+  protected $panelizer;
+
+  /**
+   * The cache tag invalidator.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
+   */
+  protected $invalidator;
+
+  /**
+   * PanelizerDefaultSelect constructor.
+   *
+   * @param \Drupal\panelizer\PanelizerInterface $panelizer
+   *   The Panelizer service.
+   */
+  public function __construct(PanelizerInterface $panelizer, CacheTagsInvalidatorInterface $invalidator) {
+    $this->panelizer = $panelizer;
+    $this->invalidator = $invalidator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('panelizer'),
+      $container->get('cache_tags.invalidator')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return 'Are you certain you want to set this panelizer default as the default for this bundle?.';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    $bundle_entity_type = \Drupal::entityTypeManager()->getDefinition($this->entityTypeId)->getBundleEntityType();
+    if ($this->viewMode == 'default') {
+      $route = "entity.entity_view_display.{$this->entityTypeId}.default";
+      $arguments = [
+        $bundle_entity_type => $this->bundle,
+      ];
+    }
+    else {
+      $route = "entity.entity_view_display.{$this->entityTypeId}.view_mode";
+      $arguments = [
+        $bundle_entity_type => $this->bundle,
+        'view_mode_name' => $this->viewMode,
+      ];
+    }
+    return new Url($route, $arguments);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panelizer_default_delete';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $machine_name = NULL) {
+    list (
+      $this->entityTypeId,
+      $this->bundle,
+      $this->viewMode,
+      $this->displayId
+      ) = explode('__', $machine_name);
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $display = $this->panelizer->getEntityViewDisplay($this->entityTypeId, $this->bundle, $this->viewMode);
+    $settings = $this->panelizer->getPanelizerSettings($this->entityTypeId, $this->bundle, $this->viewMode, $display);
+    $settings['default'] = $this->displayId;
+    $this->panelizer->setPanelizerSettings($this->entityTypeId, $this->bundle, $this->viewMode, $settings, $display);
+    $form_state->setRedirectUrl($this->getCancelUrl());
+    $tag = "panelizer_default:{$this->entityTypeId}:{$this->bundle}:{$this->viewMode}";
+    $this->invalidator->invalidateTags([$tag]);
+  }
+
+}
diff --git a/src/Form/PanelizerWizardContextConfigure.php b/src/Form/PanelizerWizardContextConfigure.php
new file mode 100644
index 0000000..7dea5d6
--- /dev/null
+++ b/src/Form/PanelizerWizardContextConfigure.php
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardContextConfigure.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Plugin\Context\ContextInterface;
+use Drupal\ctools\ContextMapperInterface;
+use Drupal\ctools\Form\ContextConfigure;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class PanelizerWizardContextConfigure extends ContextConfigure {
+
+  /**
+   * The context mapper.
+   *
+   * @var \Drupal\ctools\ContextMapperInterface
+   */
+  protected $contextMapper;
+
+  /**
+   * PanelizerWizardContextConfigure constructor.
+   *
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore
+   *   The shared tempstore factory.
+   * @param \Drupal\ctools\ContextMapperInterface $context_mapper
+   *   The context mapper.
+   */
+  public function __construct(SharedTempStoreFactory $tempstore, ContextMapperInterface $context_mapper) {
+    parent::__construct($tempstore);
+    $this->contextMapper = $context_mapper;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.shared_tempstore'),
+      $container->get('ctools.context_mapper')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getParentRouteInfo($cached_values) {
+    return ['panelizer.wizard.add.step', [
+      'machine_name' => $cached_values['id'],
+      'step' => 'contexts',
+    ]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    $static_contexts = isset($cached_values['contexts']) ? $cached_values['contexts'] : [];
+    $static_contexts = $this->contextMapper->getContextValues($static_contexts);
+    return $static_contexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function addContext($cached_values, $context_id, ContextInterface $context) {
+    $cached_values['contexts'][$context_id] = [
+      'label' => $context->getContextDefinition()->getLabel(),
+      'type' => $context->getContextDefinition()->getDataType(),
+      'description' => $context->getContextDefinition()->getDescription(),
+      'value' => strpos($context->getContextDefinition()->getDataType(), 'entity:') === 0 ? $context->getContextValue()->uuid() : $context->getContextValue(),
+    ];
+    return $cached_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function contextExists($value, $element, $form_state) {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function disableMachineName($cached_values, $machine_name) {
+    return !empty($cached_values['contexts'][$machine_name]);
+  }
+
+}
diff --git a/src/Form/PanelizerWizardContextDeleteForm.php b/src/Form/PanelizerWizardContextDeleteForm.php
new file mode 100644
index 0000000..84c6404
--- /dev/null
+++ b/src/Form/PanelizerWizardContextDeleteForm.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardContextDeleteForm.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\ctools\Form\ContextDelete;
+
+/**
+ * Provides a form for deleting a context.
+ */
+class PanelizerWizardContextDeleteForm extends ContextDelete {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panelizer_wizard_context_delete_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    $cached_values = $this->getTempstore();
+    $context = $cached_values['contexts'][$this->context_id];
+    return $this->t('Are you sure you want to delete the context @label?', ['@label' => $context['label']]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    $cached_values = $this->getTempstore();
+
+    return new Url('panelizer.wizard.add.step', [
+      'machine_name' => $cached_values['id'],
+      'step' => 'contexts',
+    ]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $this->getTempstore();
+    $context = $cached_values['contexts'][$this->context_id];
+    drupal_set_message($this->t('The static context %label has been removed.', ['%label' => $context['label']]));
+    unset($cached_values['contexts'][$this->context_id]);
+    $this->setTempstore($cached_values);
+    parent::submitForm($form, $form_state);
+  }
+
+}
diff --git a/src/Form/PanelizerWizardContextForm.php b/src/Form/PanelizerWizardContextForm.php
new file mode 100644
index 0000000..91acf4c
--- /dev/null
+++ b/src/Form/PanelizerWizardContextForm.php
@@ -0,0 +1,155 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardContextForm.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\OpenModalDialogCommand;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\TypedData\TypedDataManagerInterface;
+use Drupal\ctools\Form\ManageContext;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Simple wizard step form.
+ */
+class PanelizerWizardContextForm extends ManageContext {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $relationships = FALSE;
+
+  /**
+   * The shared temp store factory.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstoreFactory;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('typed_data_manager'),
+      $container->get('form_builder'),
+      $container->get('user.shared_tempstore')
+    );
+  }
+
+  /**
+   * ManageContext constructor.
+   *
+   * @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
+   *   The typed data manager.
+   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
+   *   The form builder.
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore_factory
+   *   Shared user tempstore factory.
+   */
+  public function __construct(TypedDataManagerInterface $typed_data_manager, FormBuilderInterface $form_builder, SharedTempStoreFactory $tempstore_factory) {
+    parent::__construct($typed_data_manager, $form_builder);
+    $this->tempstoreFactory = $tempstore_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panelizer_wizard_context_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContextClass($cached_values) {
+    return PanelizerWizardContextConfigure::class;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRelationshipClass($cached_values) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContextAddRoute($cached_values) {
+    return 'panelizer.wizard.step.context.add';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRelationshipAddRoute($cached_values) {
+    return 'panelizer.wizard.step.context.add';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    return $cached_values['plugin']->getPattern()->getDefaultContexts($this->tempstoreFactory, $this->getTempstoreId(), $this->machine_name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getTempstoreId() {
+    return 'panelizer.wizard';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContextOperationsRouteInfo($cached_values, $machine_name, $row) {
+    return ['panelizer.wizard.step.context', [
+      'machine_name' => $machine_name,
+      'context_id' => $row,
+    ]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRelationshipOperationsRouteInfo($cached_values, $machine_name, $row) {
+    return ['panelizer.wizard.step.context', [
+      'machine_name' => $machine_name,
+      'context_id' => $row,
+    ]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function isEditableContext($cached_values, $row) {
+    if (!isset($cached_values['contexts'][$row])) {
+      return FALSE;
+    }
+    $context = $cached_values['contexts'][$row];
+    return !empty($context['value']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addContext(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $context = $form_state->getValue('context');
+    $content = $this->formBuilder->getForm($this->getContextClass($cached_values), $context, $this->getTempstoreId(), $this->machine_name);
+    $content['#attached']['library'][] = 'core/drupal.dialog.ajax';
+    list(, $route_parameters) = $this->getContextOperationsRouteInfo($cached_values, $this->machine_name, $context);
+    $content['submit']['#attached']['drupalSettings']['ajax'][$content['submit']['#id']]['url'] = $this->url($this->getContextAddRoute($cached_values), $route_parameters, ['query' => [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]]);
+    $response = new AjaxResponse();
+    $response->addCommand(new OpenModalDialogCommand($this->t('Add new context'), $content, array('width' => '700')));
+    return $response;
+  }
+
+}
diff --git a/src/Form/PanelizerWizardGeneralForm.php b/src/Form/PanelizerWizardGeneralForm.php
new file mode 100644
index 0000000..a3a5862
--- /dev/null
+++ b/src/Form/PanelizerWizardGeneralForm.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardGeneralForm.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * General settings for a panelized bundle.
+ */
+class PanelizerWizardGeneralForm extends FormBase {
+
+  /**
+   * The SharedTempStore key for our current wizard values.
+   *
+   * @var string|NULL
+   */
+  protected $machine_name;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panelizer_wizard_general_form';
+  }
+
+  /**
+   * @param $machine_name
+   * @param $element
+   */
+  public static function validateMachineName($machine_name, $element) {
+    // Attempt to load via the machine name and entity type.
+    if (isset($element['#machine_name']['prefix'])) {
+      $panelizer = \Drupal::service('panelizer');
+      // Load the panels display variant.
+      $full_machine_name = $element['#machine_name']['prefix'] . '__' . $machine_name;
+      return $panelizer->getDefaultPanelsDisplayByMachineName($full_machine_name);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $machine_name = NULL) {
+    $this->machine_name = $machine_name;
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $plugin */
+    $plugin = $cached_values['plugin'];
+    $form['variant_settings'] = $plugin->buildConfigurationForm([], (new FormState())->setValues($form_state->getValue('variant_settings', [])));
+    $form['variant_settings']['#tree'] = TRUE;
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    if ($form_state->hasValue('id') && !isset($this->machine_name) && $form_state->has('machine_name_prefix')) {
+      $form_state->setValue('id', "{$form_state->get('machine_name_prefix')}__{$form_state->getValue('id')}");
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $plugin */
+    $plugin = $cached_values['plugin'];
+    $plugin->submitConfigurationForm($form['variant_settings'], (new FormState())->setValues($form_state->getValue('variant_settings', [])));
+    $configuration = $plugin->getConfiguration();
+    $cached_values['plugin']->setConfiguration($configuration);
+  }
+
+}
diff --git a/src/Menu/AddDefaultLocalAction.php b/src/Menu/AddDefaultLocalAction.php
new file mode 100644
index 0000000..932ed28
--- /dev/null
+++ b/src/Menu/AddDefaultLocalAction.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Menu\AddDefaultLocalAction.
+ */
+
+namespace Drupal\panelizer\Menu;
+
+use Drupal\Core\Menu\LocalActionDefault;
+use Drupal\Core\Routing\RouteMatchInterface;
+
+class AddDefaultLocalAction extends LocalActionDefault {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteParameters(RouteMatchInterface $route_match) {
+    $this->pluginDefinition['route_parameters']['entity_type_id'] = $route_match->getCurrentRouteMatch()->getParameter('entity_type_id');
+    $this->pluginDefinition['route_parameters']['bundle'] = $route_match->getCurrentRouteMatch()->getParameter('bundle');
+    $this->pluginDefinition['route_parameters']['view_mode_name'] = $route_match->getCurrentRouteMatch()->getParameter('view_mode_name');
+    return parent::getRouteParameters($route_match);
+  }
+
+}
diff --git a/src/Panelizer.php b/src/Panelizer.php
index 864191d..bfe929b 100644
--- a/src/Panelizer.php
+++ b/src/Panelizer.php
@@ -20,6 +20,7 @@ use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Session\AccountProxyInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\ctools\ContextMapperInterface;
 use Drupal\panelizer\Exception\PanelizerException;
 use Drupal\panelizer\Plugin\PanelizerEntityManager;
 use Drupal\panels\PanelsDisplayManagerInterface;
@@ -89,6 +90,13 @@ class Panelizer implements PanelizerInterface {
   protected $panelsManager;
 
   /**
+   * The context mapper.
+   *
+   * @var \Drupal\ctools\ContextMapperInterface
+   */
+  protected $contextMapper;
+
+  /**
    * Constructs a Panelizer.
    *
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
@@ -109,8 +117,10 @@ class Panelizer implements PanelizerInterface {
    *   The Panels display manager.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
    *   The string translation service.
+   * @param \Drupal\ctools\ContextMapperInterface $context_mapper
+   *   The context mapper service.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, ModuleHandlerInterface $module_handler, AccountProxyInterface $current_user, PanelizerEntityManager $panelizer_entity_manager, PanelsDisplayManagerInterface $panels_manager, TranslationInterface $string_translation) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, ModuleHandlerInterface $module_handler, AccountProxyInterface $current_user, PanelizerEntityManager $panelizer_entity_manager, PanelsDisplayManagerInterface $panels_manager, TranslationInterface $string_translation, ContextMapperInterface $context_mapper) {
     $this->entityTypeManager = $entity_type_manager;
     $this->entityTypeBundleInfo = $entity_type_bundle_info;
     $this->entityFieldManager = $entity_field_manager;
@@ -120,6 +130,7 @@ class Panelizer implements PanelizerInterface {
     $this->panelizerEntityManager = $panelizer_entity_manager;
     $this->panelsManager = $panels_manager;
     $this->stringTranslation = $string_translation;
+    $this->contextMapper = $context_mapper;
   }
 
   /**
@@ -135,19 +146,23 @@ class Panelizer implements PanelizerInterface {
   }
 
   /**
-   * Gets the entity view display for the entity type, bundle and view mode.
-   *
-   * @param $entity_type_id
-   *   The entity type id.
-   * @param $bundle
-   *   The bundle.
-   * @param $view_mode
-   *   The view mode.
+   * Load a Panels Display via an ID (Machine Name).
    *
-   * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface|NULL
-   *   The entity view display if one exists; NULL otherwise.
+   * @return \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant|NULL
+   *   The default Panels display with the given name if it exists; otherwise
+   *   NULL.
+   */
+  public function getDefaultPanelsDisplayByMachineName($full_machine_name) {
+    list($entity_type, $bundle, $view_mode, $machine_name) = explode('__', $full_machine_name);
+    /** @var \Drupal\panelizer\Panelizer $panelizer */
+    // @todo this $display_id looks all wrong to me since it's the name and view_mode.
+    return $this->getDefaultPanelsDisplay($machine_name, $entity_type, $bundle, $view_mode);
+  }
+
+  /**
+   * {@inheritdoc}
    */
-  protected function getEntityViewDisplay($entity_type_id, $bundle, $view_mode) {
+  public function getEntityViewDisplay($entity_type_id, $bundle, $view_mode) {
     // Check the existence and status of:
     // - the display for the view mode,
     // - the 'default' display.
@@ -200,7 +215,7 @@ class Panelizer implements PanelizerInterface {
    */
   public function getPanelsDisplay(FieldableEntityInterface $entity, $view_mode, EntityViewDisplayInterface $display = NULL) {
     $settings = $this->getPanelizerSettings($entity->getEntityTypeId(), $entity->bundle(), $view_mode, $display);
-    if ($settings['custom'] && isset($entity->panelizer)) {
+    if (($settings['custom'] || $settings['allow']) && isset($entity->panelizer) && $entity->panelizer->first()) {
       /** @var \Drupal\Core\Field\FieldItemInterface[] $values */
       $values = [];
       foreach ($entity->panelizer as $item) {
@@ -208,16 +223,68 @@ class Panelizer implements PanelizerInterface {
       }
       if (isset($values[$view_mode])) {
         $panelizer_item = $values[$view_mode];
+        // Check for a customized display first and use that if present.
+        if (!empty($panelizer_item->panels_display)) {
+          // @todo: validate schema after https://www.drupal.org/node/2392057 is fixed.
+          return $this->panelsManager->importDisplay($panelizer_item->panels_display, FALSE);
+        }
+        // If not customized, use the specified default.
         if (!empty($panelizer_item->default)) {
-          return $this->getDefaultPanelsDisplay($panelizer_item->default, $entity->getEntityTypeId(), $entity->bundle(), $view_mode, $display);
+          // If we're using this magic key use the settings default.
+          if ($panelizer_item->default == '__bundle_default__') {
+            $default = $settings['default'];
+          }
+          else {
+            $default = $panelizer_item->default;
+            // Ensure the default still exists and if not fallback sanely.
+            $displays = $this->getDefaultPanelsDisplays($entity->getEntityTypeId(), $entity->bundle(), $view_mode);
+            if (!isset($displays[$default])) {
+              $default = $settings['default'];
+            }
+          }
+          $panels_display = $this->getDefaultPanelsDisplay($default, $entity->getEntityTypeId(), $entity->bundle(), $view_mode, $display);
+          $this->setCacheTags($panels_display, $entity->getEntityTypeId(), $entity->bundle(), $view_mode, $display, $default, $settings);
+          return $panels_display;
         }
-
-        // @todo: validate schema after https://www.drupal.org/node/2392057 is fixed.
-        return $this->panelsManager->importDisplay($panelizer_item->panels_display, FALSE);
       }
     }
+    // If the field has no input to give us, use the settings default.
+    $panels_display = $this->getDefaultPanelsDisplay($settings['default'], $entity->getEntityTypeId(), $entity->bundle(), $view_mode, $display);
+    $this->setCacheTags($panels_display, $entity->getEntityTypeId(), $entity->bundle(), $view_mode, $display, $settings['default'], $settings);
+    return $panels_display;
+  }
 
-    return $this->getDefaultPanelsDisplay('default', $entity->getEntityTypeId(), $entity->bundle(), $view_mode, $display);
+  /**
+   * Properly determine the cache tags for a display and set them.
+   *
+   * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display
+   *   The panels display variant.
+   * @param string $entity_type_id
+   *   The entity type id.
+   * @param string $bundle
+   *   The bundle.
+   * @param string $view_mode
+   *   The view mode.
+   * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface|NULL $display
+   *   If the caller already has the correct display, it can optionally be
+   *   passed in here so the Panelizer service doesn't have to look it up;
+   *   otherwise, this argument can be omitted.
+   * @param $default
+   *   The name of the panels display we are about to render.
+   * @param array $settings
+   *   The default panelizer settings for this EntityViewDisplay.
+   */
+  protected function setCacheTags(PanelsDisplayVariant $panels_display, $entity_type_id, $bundle, $view_mode, EntityViewDisplayInterface $display = NULL, $default, array $settings) {
+    if (!$display) {
+      $display = $this->getEntityViewDisplay($entity_type_id, $bundle, $view_mode);
+    }
+    $display_mode = $display ? $display->getMode() : '';
+
+    if ($default == $settings['default']) {
+      $tags = ["{$panels_display->getStorageType()}:{$entity_type_id}:{$bundle}:{$display_mode}"];
+    }
+    $tags[] = "{$panels_display->getStorageType()}:{$entity_type_id}:{$bundle}:{$display_mode}:$default";
+    $panels_display->addCacheTags($tags);
   }
 
   /**
@@ -225,7 +292,7 @@ class Panelizer implements PanelizerInterface {
    */
   public function setPanelsDisplay(FieldableEntityInterface $entity, $view_mode, $default, PanelsDisplayVariant $panels_display = NULL) {
     $settings = $this->getPanelizerSettings($entity->getEntityTypeId(), $entity->bundle(), $view_mode);
-    if ($settings['custom'] && isset($entity->panelizer)) {
+    if (($settings['custom'] || $settings['allow']) && isset($entity->panelizer)) {
       $panelizer_item = NULL;
       /** @var \Drupal\Core\Field\FieldItemInterface $item */
       foreach ($entity->panelizer as $item) {
@@ -303,6 +370,8 @@ class Panelizer implements PanelizerInterface {
 
     $config = $display->getThirdPartySetting('panelizer', 'displays', []);
     if (!empty($config[$name])) {
+      // Set a default just in case.
+      $config[$name]['builder'] = empty($config[$name]['builder']) ? 'standard' : $config[$name]['builder'];
       // @todo: validate schema after https://www.drupal.org/node/2392057 is fixed.
       $panels_display = $this->panelsManager->importDisplay($config[$name], FALSE);
     }
@@ -342,6 +411,43 @@ class Panelizer implements PanelizerInterface {
   /**
    * {@inheritdoc}
    */
+  public function getDisplayStaticContexts($name, $entity_type_id, $bundle, $view_mode, EntityViewDisplayInterface $display = NULL) {
+    if (!$display) {
+      $display = $this->getEntityViewDisplay($entity_type_id, $bundle, $view_mode);
+      // If we still don't find a display, then we won't find a Panelizer
+      // default for sure.
+      if (!$display) {
+        return NULL;
+      }
+    }
+
+    $config = $display->getThirdPartySetting('panelizer', 'displays', []);
+    if (!empty($config[$name]) && !empty($config[$name]['static_context'])) {
+      return $this->contextMapper->getContextValues($config[$name]['static_context']);
+    }
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDisplayStaticContexts($name, $entity_type_id, $bundle, $view_mode, $contexts) {
+    $display = $this->getEntityViewDisplay($entity_type_id, $bundle, $view_mode);
+    if (!$display) {
+      throw new PanelizerException("Unable to find display for given entity type, bundle and view mode");
+    }
+
+    // Set this Panels display's static contexts.
+    $panels_displays = $display->getThirdPartySetting('panelizer', 'displays', []);
+    $panels_displays[$name]['static_context'] = $contexts;
+    $display->setThirdPartySetting('panelizer', 'displays', $panels_displays);
+
+    $display->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function isPanelized($entity_type_id, $bundle, $view_mode, EntityViewDisplayInterface $display = NULL) {
     if (!$this->getEntityPlugin($entity_type_id)) {
       return FALSE;
@@ -365,6 +471,8 @@ class Panelizer implements PanelizerInterface {
     $settings = [
       'enable' => $this->isPanelized($entity_type_id, $bundle, $view_mode, $display),
       'custom' => $display->getThirdPartySetting('panelizer', 'custom', FALSE),
+      'allow' => $display->getThirdPartySetting('panelizer', 'allow', FALSE),
+      'default' => $display->getThirdPartySetting('panelizer', 'default', 'default'),
     ];
 
     // Make sure that the Panelizer field actually exists.
@@ -386,6 +494,8 @@ class Panelizer implements PanelizerInterface {
 
     $display->setThirdPartySetting('panelizer', 'enable', !empty($settings['enable']));
     $display->setThirdPartySetting('panelizer', 'custom', !empty($settings['enable']) && !empty($settings['custom']));
+    $display->setThirdPartySetting('panelizer', 'allow', !empty($settings['enable']) && !empty($settings['allow']));
+    $display->setThirdPartySetting('panelizer', 'default', $settings['default']);
 
     if (!empty($settings['enable'])) {
       // Set the default display.
@@ -394,11 +504,12 @@ class Panelizer implements PanelizerInterface {
         /** @var \Drupal\panelizer\Plugin\PanelizerEntityInterface $panelizer_entity_plugin */
         $panelizer_entity_plugin = $this->panelizerEntityManager->createInstance($display->getTargetEntityTypeId(), []);
         $displays['default'] = $this->panelsManager->exportDisplay($panelizer_entity_plugin->getDefaultDisplay($display, $display->getTargetBundle(), $display->getMode()));
+        $settings['default'] = "{$display->getTargetEntityTypeId()}__{$display->getTargetBundle()}__{$view_mode}__default";
         $display->setThirdPartySetting('panelizer', 'displays', $displays);
       }
 
       // Make sure the field exists.
-      if (!empty($settings['custom'])) {
+      if (($settings['custom'] || $settings['allow'])) {
         $field_storage = $this->entityTypeManager->getStorage('field_storage_config')->load($entity_type_id . '.panelizer');
         if (!$field_storage) {
           $field_storage = $this->entityTypeManager->getStorage('field_storage_config')->create([
@@ -564,4 +675,4 @@ class Panelizer implements PanelizerInterface {
     return $this->hasOperationPermission($op, $entity_type_id, $bundle, $account);
   }
 
-}
\ No newline at end of file
+}
diff --git a/src/PanelizerEntityViewBuilder.php b/src/PanelizerEntityViewBuilder.php
index 4786e0f..d3a7a01 100644
--- a/src/PanelizerEntityViewBuilder.php
+++ b/src/PanelizerEntityViewBuilder.php
@@ -352,6 +352,8 @@ class PanelizerEntityViewBuilder implements EntityViewBuilderInterface, EntityHa
 
     foreach ($entities as $id => $entity) {
       $panels_display = $this->panelizer->getPanelsDisplay($entity, $view_mode, $displays[$entity->bundle()]);
+      $settings = $this->panelizer->getPanelizerSettings($entity->getEntityTypeId(), $entity->bundle(), $view_mode, $displays[$entity->bundle()]);
+      $panels_display->setContexts($this->panelizer->getDisplayStaticContexts($settings['default'], $entity->getEntityTypeId(), $entity->bundle(), $view_mode, $displays[$entity->bundle()]));
       $build[$id] = $this->buildPanelized($entity, $panels_display, $view_mode, $langcode);
     }
 
@@ -393,8 +395,8 @@ class PanelizerEntityViewBuilder implements EntityViewBuilderInterface, EntityHa
     }
 
     // @todo: I'm sure more is necessary to get the cache contexts right...
-    CacheableMetadata::createFromObject($entity)
-      ->applyTo($build);
+    $entity_metadata = CacheableMetadata::createFromObject($entity);
+    CacheableMetadata::createFromObject($panels_display)->merge($entity_metadata)->applyTo($build);
 
     $this->getPanelizerPlugin()->alterBuild($build, $entity, $panels_display, $view_mode);
 
diff --git a/src/PanelizerInterface.php b/src/PanelizerInterface.php
index 0372277..a793984 100644
--- a/src/PanelizerInterface.php
+++ b/src/PanelizerInterface.php
@@ -20,6 +20,21 @@ use Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant;
 interface PanelizerInterface {
 
   /**
+   * Gets the entity view display for the entity type, bundle and view mode.
+   *
+   * @param $entity_type_id
+   *   The entity type id.
+   * @param $bundle
+   *   The bundle.
+   * @param $view_mode
+   *   The view mode.
+   *
+   * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface|NULL
+   *   The entity view display if one exists; NULL otherwise.
+   */
+  public function getEntityViewDisplay($entity_type_id, $bundle, $view_mode);
+
+  /**
    * Gets the Panels display for a given entity and view mode.
    *
    * @param \Drupal\Core\Entity\EntityInterface $entity
@@ -115,6 +130,12 @@ interface PanelizerInterface {
    */
   public function setDefaultPanelsDisplay($name, $entity_type_id, $bundle, $view_mode, PanelsDisplayVariant $panels_display);
 
+  public function getDisplayStaticContexts($name, $entity_type_id, $bundle, $view_mode, EntityViewDisplayInterface $display = NULL);
+
+  public function setDisplayStaticContexts($name, $entity_type_id, $bundle, $view_mode, $contexts);
+
+
+
   /**
    * Checks if the given entity type, bundle and view mode are panelized.
    *
diff --git a/src/Plugin/AddDefaultLinkDeriver.php b/src/Plugin/AddDefaultLinkDeriver.php
new file mode 100644
index 0000000..cbd6f31
--- /dev/null
+++ b/src/Plugin/AddDefaultLinkDeriver.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Plugin\AddDefaultLinkDeriver.
+ */
+
+namespace Drupal\panelizer\Plugin;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class AddDefaultLinkDeriver extends DeriverBase implements ContainerDeriverInterface {
+
+  /**
+   * The Panelizer entity manager.
+   *
+   * @var \Drupal\panelizer\Plugin\PanelizerEntityManagerInterface
+   */
+  protected $panelizerEntityManager;
+
+  /**
+   * AddDefaultLinkDeriver constructor.
+   *
+   * @param \Drupal\panelizer\Plugin\PanelizerEntityManagerInterface $panelizer_entity_manager
+   *   The panelizer entity manager.
+   */
+  public function __construct(PanelizerEntityManagerInterface $panelizer_entity_manager) {
+    $this->panelizerEntityManager = $panelizer_entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('plugin.manager.panelizer_entity')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    foreach ($this->panelizerEntityManager->getDefinitions() as $plugin_id => $definition) {
+      $this->derivatives["$plugin_id"] = $base_plugin_definition;
+      $this->derivatives["$plugin_id"]['appears_on'] = [
+        "entity.entity_view_display.$plugin_id.default",
+        "entity.entity_view_display.$plugin_id.view_mode"
+      ];
+    }
+    return parent::getDerivativeDefinitions($base_plugin_definition);
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/PanelizerFieldType.php b/src/Plugin/Field/FieldType/PanelizerFieldType.php
index a1627bc..7ad1032 100644
--- a/src/Plugin/Field/FieldType/PanelizerFieldType.php
+++ b/src/Plugin/Field/FieldType/PanelizerFieldType.php
@@ -43,7 +43,7 @@ class PanelizerFieldType extends FieldItemBase {
       ->setRequired(FALSE);
     $properties['panels_display'] = MapDataDefinition::create('map')
       ->setLabel(new TranslatableMarkup('Panels display'))
-      ->setRequired(TRUE);
+      ->setRequired(FALSE);
 
     return $properties;
   }
@@ -86,6 +86,8 @@ class PanelizerFieldType extends FieldItemBase {
   }
 
   /**
+   * Returns the Panels display plugin manager.
+   *
    * @return \Drupal\panels\PanelsDisplayManagerInterface
    */
   protected static function getPanelsDisplayManager() {
@@ -120,9 +122,12 @@ class PanelizerFieldType extends FieldItemBase {
   public function postSave($update) {
     $panels_manager = $this->getPanelsDisplayManager();
     $panels_display_config = $this->get('panels_display')->getValue();
-    if (!empty($panels_display_config)) {
-      $panels_display = $panels_manager->importDisplay($panels_display_config, FALSE);
 
+    // If our field has custom panelizer display config data.
+    if (!empty($panels_display_config) && is_array($panels_display_config)) {
+      $panels_display = $panels_manager->importDisplay($panels_display_config, FALSE);
+    }
+    if (!empty($panels_display)) {
       // Set the storage id to include the current revision id.
       $entity = $this->getEntity();
       $storage_id_parts = [
diff --git a/src/Plugin/Field/FieldWidget/PanelizerWidget.php b/src/Plugin/Field/FieldWidget/PanelizerWidget.php
index 6a3601f..e9b8845 100644
--- a/src/Plugin/Field/FieldWidget/PanelizerWidget.php
+++ b/src/Plugin/Field/FieldWidget/PanelizerWidget.php
@@ -8,12 +8,9 @@
 namespace Drupal\panelizer\Plugin\Field\FieldWidget;
 
 use Drupal\Core\Entity\Entity\EntityViewDisplay;
-use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Plugin implementation of the 'panelizer' widget.
@@ -30,6 +27,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 class PanelizerWidget extends WidgetBase {
 
   /**
+   * Returns the Panels display plugin manager.
+   *
    * @return \Drupal\panels\PanelsDisplayManagerInterface
    */
   public function getPanelsManager() {
@@ -38,6 +37,8 @@ class PanelizerWidget extends WidgetBase {
   }
 
   /**
+   * Returns the Panelizer entity plugin manager.
+   *
    * @return \Drupal\panelizer\Plugin\PanelizerEntityManager
    */
   public function getPanelizerManager() {
@@ -46,50 +47,21 @@ class PanelizerWidget extends WidgetBase {
   }
 
   /**
-   * @return \Drupal\Core\Entity\EntityDisplayRepositoryInterface
+   * Returns the Panelizer service.
+   *
+   * @return \Drupal\panelizer\PanelizerInterface
    */
-  public function getEntityDisplayRepository() {
+  public function getPanelizer() {
     // @todo: is it possible to inject this?
-    return \Drupal::service('entity_display.repository');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function defaultSettings() {
-    return array(
-      'allow_panel_choice' => FALSE,
-    ) + parent::defaultSettings();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function settingsForm(array $form, FormStateInterface $form_state) {
-    $elements = [];
-
-    /*
-    $elements['allow_panel_choice'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Allow panel choice'),
-      '#default_value' => $this->getSetting('allow_panel_choice'),
-    );
-    */
-
-    return $elements;
+    return \Drupal::service('panelizer');
   }
 
   /**
-   * {@inheritdoc}
+   * @return \Drupal\Core\Entity\EntityDisplayRepositoryInterface
    */
-  public function settingsSummary() {
-    $summary = [];
-
-    if (!empty($this->getSetting('allow_panel_choice'))) {
-      $summary[] = t('Allow panel choice');
-    }
-
-    return $summary;
+  public function getEntityDisplayRepository() {
+    // @todo: is it possible to inject this?
+    return \Drupal::service('entity_display.repository');
   }
 
   /**
@@ -110,19 +82,18 @@ class PanelizerWidget extends WidgetBase {
       ];
     }
 
-    /** @var \Drupal\panelizer\Plugin\PanelizerEntityInterface $panelizer_plugin */
-    $panelizer_plugin = $this->getPanelizerManager()->createInstance($entity_type_id, []);
-
     // If any view modes are missing, then set the default.
+    $displays = [];
     foreach ($entity_view_modes as $view_mode => $view_mode_info) {
-      if (!isset($values[$view_mode])) {
-        $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
+      $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
+      $displays[$view_mode] = $display->getThirdPartySetting('panelizer', 'displays', []);
+      // If we don't have a value, or the default is __bundle_default__ and our
+      // panels_display is empty, set the default to __bundle_default__.
+      if (!isset($values[$view_mode]) || ($values[$view_mode]['default'] == '__bundle_default__' && empty($values[$view_mode]['panels_display']))) {
         if ($display->getThirdPartySetting('panelizer', 'enable', FALSE)) {
-          $panels_display = $panelizer_plugin->getDefaultDisplay($display, $entity->bundle(), $view_mode);
-
           $values[$view_mode] = [
-            'default' => 'default',
-            'panels_display' => $this->getPanelsManager()->exportDisplay($panels_display),
+            'default' => '__bundle_default__',
+            'panels_display' => [],
           ];
         }
       }
@@ -136,14 +107,28 @@ class PanelizerWidget extends WidgetBase {
         '#value' => $view_mode,
       ];
 
-      if (!empty($this->getSetting('allow_panel_choice'))) {
+      $settings = $this->getPanelizer()->getPanelizerSettings($entity_type_id, $entity->bundle(), $view_mode);
+      if (!empty($settings['allow'])) {
+        // We default to this option when the user hasn't previous interacted
+        // with the field.
+        $options = [
+          '__bundle_default__' => $this->t('Current default display'),
+        ];
+        foreach ($displays[$view_mode] as $machine_name => $panels_display) {
+          $options[$machine_name] = $panels_display['label'];
+        }
         $element[$delta]['default'] = [
+          '#title' => $entity_view_modes[$view_mode]['label'],
           '#type' => 'select',
-          // @todo: list the view mode in the title
-          // @todo: get the list of defaults
-          '#options' => [],
+          '#options' => $options,
           '#default_value' => $value['default'],
         ];
+        // If we have a value in panels_display, prevent the user from
+        // interacting with the widget for the view modes that are overridden.
+        if (!empty($value['panels_display'])) {
+          $element[$delta]['default']['#disabled'] = TRUE;
+          $element[$delta]['default']['#options'][$value['default']] = $this->t('Custom Override');
+        }
       }
       else {
         $element[$delta]['default'] = [
diff --git a/src/Plugin/PanelizerEntityBase.php b/src/Plugin/PanelizerEntityBase.php
index d7ea1d6..1f0cca1 100644
--- a/src/Plugin/PanelizerEntityBase.php
+++ b/src/Plugin/PanelizerEntityBase.php
@@ -12,6 +12,7 @@ use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Panels\PanelsDisplayManager;
 use Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -20,6 +21,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  * Base class for Panelizer entity plugins.
  */
 abstract class PanelizerEntityBase extends PluginBase implements PanelizerEntityInterface, ContainerFactoryPluginInterface {
+  use StringTranslationTrait;
 
   /**
    * @var \Drupal\Panels\PanelsDisplayManager
@@ -68,9 +70,11 @@ abstract class PanelizerEntityBase extends PluginBase implements PanelizerEntity
   public function getDefaultDisplay(EntityViewDisplayInterface $display, $bundle, $view_mode) {
     $panels_display = $this->panelsManager->createDisplay();
 
+    $panels_display->setConfiguration(['label' => $this->t('Default')] + $panels_display->getConfiguration());
     $panels_display->setLayout('onecol');
     // @todo: For now we always use the IPE, but we should support not using the ipe.
     $panels_display->setBuilder('ipe');
+    $panels_display->setPattern('panelizer');
 
     // Add all the visible fields to the Panel.
     $entity_type_id = $this->getPluginId();
diff --git a/src/Plugin/PanelsPattern/PanelizerPattern.php b/src/Plugin/PanelsPattern/PanelizerPattern.php
new file mode 100644
index 0000000..6e0eef5
--- /dev/null
+++ b/src/Plugin/PanelsPattern/PanelizerPattern.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Plugin\PanelsPattern\PanelizerPattern.
+ */
+
+namespace Drupal\panelizer\Plugin\PanelsPattern;
+
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\panels\Plugin\PanelsPattern\DefaultPattern;
+use Drupal\user\SharedTempStoreFactory;
+
+/**
+ * @PanelsPattern("panelizer")
+ */
+class PanelizerPattern extends DefaultPattern {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultContexts(SharedTempStoreFactory $tempstore, $tempstore_id, $machine_name) {
+    $contexts = [];
+    list($entity_type, $bundle) = explode('__', $machine_name);
+    $entity_definition = new ContextDefinition("entity:$entity_type", $this->t('Entity being panelized'));
+    $contexts['@panelizer.entity_context:entity'] = new Context($entity_definition);
+    $user_definition = new ContextDefinition("entity:user", $this->t('Current user'));
+    $contexts['current_user'] = new Context($user_definition);
+    return $contexts + parent::getDefaultContexts($tempstore, $tempstore_id, $machine_name);
+  }
+
+}
diff --git a/src/Plugin/PanelsStorage/PanelizerDefaultPanelsStorage.php b/src/Plugin/PanelsStorage/PanelizerDefaultPanelsStorage.php
index 72009c9..f2c3964 100644
--- a/src/Plugin/PanelsStorage/PanelizerDefaultPanelsStorage.php
+++ b/src/Plugin/PanelsStorage/PanelizerDefaultPanelsStorage.php
@@ -122,13 +122,20 @@ class PanelizerDefaultPanelsStorage extends PanelsStorageBase implements Contain
    * @param \Drupal\Core\Entity\EntityInterface|NULL $entity
    *   The entity.
    *
-   * @return \Drupal\Core\Plugin\Context\Context
-   *   The context.
+   * @return \Drupal\Core\Plugin\Context\Context[]
+   *   The available contexts.
    */
   protected function getEntityContext($entity_type_id, EntityInterface $entity = NULL) {
-    return new Context(new ContextDefinition('entity:' . $entity_type_id, NULL, TRUE), $entity);
+    $contexts = [];
+    // Set a placeholder context so that the calling code knows that we need
+    // an entity context. If we have the value available, then we actually set
+    // the context value.
+    $contexts['@panelizer.entity_context:entity'] = new Context(new ContextDefinition('entity:' . $entity_type_id, NULL, TRUE), $entity);
+    return $contexts;
   }
 
+
+
   /**
    * {@inheritdoc}
    */
@@ -136,12 +143,8 @@ class PanelizerDefaultPanelsStorage extends PanelsStorageBase implements Contain
     try {
       list ($entity_type_id, $bundle, $view_mode, $name, $entity) = $this->parseId($id);
       if ($panels_display = $this->panelizer->getDefaultPanelsDisplay($name, $entity_type_id, $bundle, $view_mode)) {
-        // Set a placeholder context so that the calling code knows that we need
-        // an entity context. If we have the value available, then we actually set
-        // the context value.
-        $contexts = [
-          '@panelizer.entity_context:entity' => $this->getEntityContext($entity_type_id, $entity),
-        ];
+        $contexts = $this->getEntityContext($entity_type_id, $entity);
+        $contexts = $contexts + $this->panelizer->getDisplayStaticContexts($name, $entity_type_id, $bundle, $view_mode);
         $panels_display->setContexts($contexts);
         return $panels_display;
       }
diff --git a/src/Plugin/PanelsStorage/PanelizerFieldPanelsStorage.php b/src/Plugin/PanelsStorage/PanelizerFieldPanelsStorage.php
index 39f5da4..4c6711b 100644
--- a/src/Plugin/PanelsStorage/PanelizerFieldPanelsStorage.php
+++ b/src/Plugin/PanelsStorage/PanelizerFieldPanelsStorage.php
@@ -134,9 +134,21 @@ class PanelizerFieldPanelsStorage extends PanelsStorageBase implements Container
     $id = $panels_display->getStorageId();
     if ($entity = $this->loadEntity($id)) {
       list (,, $view_mode) = explode(':', $id);
+      // If we're dealing with an entity that has a documented default, we
+      // don't want to lose that information when we save our customizations.
+      // This enables us to revert to the correct default at a later date.
       if ($entity instanceof FieldableEntityInterface) {
+        $default = NULL;
+        if ($entity->hasField('panelizer') && $entity->panelizer->first()) {
+          foreach ($entity->panelizer as $item) {
+            if ($item->view_mode == $view_mode) {
+              $default = $item->default;
+              break;
+            }
+          }
+        }
         try {
-          $this->panelizer->setPanelsDisplay($entity, $view_mode, NULL, $panels_display);
+          $this->panelizer->setPanelsDisplay($entity, $view_mode, $default, $panels_display);
         }
         catch (PanelizerException $e) {
           // Translate to expected exception type.
diff --git a/src/Tests/PanelizerAddDefaultLinkTest.php b/src/Tests/PanelizerAddDefaultLinkTest.php
new file mode 100644
index 0000000..897a0ce
--- /dev/null
+++ b/src/Tests/PanelizerAddDefaultLinkTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\panelizer\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\user\Entity\User;
+
+/**
+ * @group panelizer
+ */
+class PanelizerAddDefaultLinkTest extends WebTestBase {
+
+  use PanelizerTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $profile = 'standard';
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'block',
+    'ctools',
+    'layout_plugin',
+    'node',
+    'panelizer',
+    'panels',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $account = User::load(1);
+    $account->setPassword('foo')->save();
+    $account->pass_raw = 'foo';
+    $this->drupalLogin($account);
+  }
+
+  public function test() {
+    $this->panelize('page');
+    $this->assertLink('Add panelizer default');
+    $this->unpanelize('page');
+    $this->assertNoLink('Add panelizer default');
+  }
+
+}
diff --git a/src/Tests/PanelizerDefaultsTest.php b/src/Tests/PanelizerDefaultsTest.php
new file mode 100644
index 0000000..a2090d7
--- /dev/null
+++ b/src/Tests/PanelizerDefaultsTest.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Drupal\panelizer\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\user\Entity\User;
+
+/**
+ * @group panelizer
+ */
+class PanelizerDefaultsTest extends WebTestBase {
+
+  use PanelizerTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $profile = 'standard';
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'block',
+    'ctools',
+    'layout_plugin',
+    'node',
+    'panelizer',
+    'panels',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $account = User::load(1);
+    $account->setPassword('foo')->save();
+    $account->pass_raw = 'foo';
+    $this->drupalLogin($account);
+  }
+
+  public function test() {
+    $this->panelize('page');
+
+    // Create an additional default layout so we can assert that it's available
+    // as an option when choosing the layout on the node form.
+    $default_id = $this->addPanelizerDefault('page');
+    $this->assertDefaultExists('page', 'default', $default_id);
+
+    // The user should only be able to choose the layout if specifically allowed
+    // to (the panelizer[allow] checkbox in the view display configuration). By
+    // default, they aren't.
+    $this->drupalGet('node/add/page');
+    $this->assertNoFieldByName('panelizer[0][default]');
+
+    // Enable layout selection and assert that all the expected fields show up.
+    $this->panelize('page', NULL, ['panelizer[allow]' => TRUE]);
+    $this->drupalGet('node/add/page');
+    $view_modes = \Drupal::service('entity_display.repository')->getViewModes('node');
+    $view_modes = array_filter($view_modes, function (array $view_mode) {
+      // View modes that are inheriting the default display (i.e., status is
+      // FALSE) will not show up unless they, too, are panelized. But in this
+      // test, we only panelized the default display.
+      return $view_mode['status'] == FALSE;
+    });
+    for ($i = 0; $i < count($view_modes); $i++) {
+      $this->assertFieldByName("panelizer[{$i}][default]");
+      $this->assertOption("edit-panelizer-{$i}-default", 'default');
+      $this->assertOption("edit-panelizer-{$i}-default", $default_id);
+    }
+
+    $this->deletePanelizerDefault('page', 'default', $default_id);
+    $this->assertDefaultNotExists('page', 'default', $default_id);
+  }
+
+}
diff --git a/src/Tests/PanelizerNodeFunctionalTest.php b/src/Tests/PanelizerNodeFunctionalTest.php
index 2d82abe..51646b0 100644
--- a/src/Tests/PanelizerNodeFunctionalTest.php
+++ b/src/Tests/PanelizerNodeFunctionalTest.php
@@ -48,21 +48,102 @@ class PanelizerNodeFunctionalTest extends WebTestBase {
       'administer nodes',
       'administer content types',
       'create page content',
+      'create article content',
       'administer panelizer',
       'access panels in-place editing',
+      'view the administration theme',
     ]);
     $this->drupalLogin($user);
+  }
 
-    $this->drupalPostForm('admin/structure/types/manage/page/display', [
-      'panelizer[enable]' => TRUE,
-      'panelizer[custom]' => TRUE,
-    ], 'Save');
+  /**
+   * Tests the admin interface to set a default layout for a bundle.
+   */
+  public function testWizardUI() {
+    $this->drupalGet('admin/structure/types/manage/article/display');
+    $this->clickLink('Panelize this view mode');
+    // General settings step.
+    $edit = [
+      'enable' => TRUE,
+      'custom' => TRUE,
+    ];
+
+    // Contexts step.
+    $this->drupalPostForm(NULL, $edit, 'Next');
+    $this->assertText('panelizer_context_entity', 'The current entity context is present.');
+
+    // Layout selection step.
+    $this->drupalPostForm(NULL, [], 'Next');
+
+    // Content step. Add the Node block to the top region.
+    $this->drupalPostForm(NULL, [], 'Next');
+    $this->clickLink('Add new block');
+    $this->clickLink('Title');
+    $edit = [
+      'region' => 'middle',
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Add block');
+
+    // Finish the wizard.
+    $this->drupalPostForm(NULL, [], 'Finish');
+
+    // Check that the general setting was saved.
+    $this->assertFieldChecked('edit-custom');
+
+    // Now change the setting and then cancel changes.
+    $this->drupalPostForm(NULL, ['custom' => FALSE], 'Update');
+    $this->assertNoFieldChecked('edit-custom');
+    $this->drupalPostForm(NULL, [], 'Cancel');
+    $this->assertFieldChecked('edit-custom');
+
+    // Now change and save the general setting.
+    $this->drupalPostForm(NULL, ['custom' => FALSE], 'Update and save');
+    $this->assertNoFieldChecked('edit-custom');
+    $this->drupalPostForm(NULL, [], 'Cancel');
+    $this->assertNoFieldChecked('edit-custom');
+
+    // Add another block at the Content step and then save changes.
+    $this->clickLink('Content');
+    $this->clickLink('Add new block');
+    $this->clickLink('Body');
+    $edit = [
+      'region' => 'middle',
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Add block');
+    $this->assertText('entity_field:node:body', 'The body block was added successfully.');
+    $this->drupalPostForm(NULL, [], 'Save');
+    $this->clickLink('Content');
+    $this->assertText('entity_field:node:body', 'The body block was saved successfully.');
+
+    // Check that the Manage Display tab changed now that Panelizer is set up.
+    // Also, the field display table should be hidden.
+    $this->drupalGet('admin/structure/types/manage/article/display');
+    $this->assertLink('This display mode is managed by Panelizer. Click here to go to its settings.');
+    $this->assertNoRaw('<div id="field-display-overview-wrapper">');
+
+    // Disable Panelizer for the default display mode.
+    // This should bring back the field overview table at Manage Display
+    // and make the "Panelize this view mode" link to point to the Edit
+    // Wizard UI.
+    $this->clickLink('This display mode is managed by Panelizer. Click here to go to its settings.');
+    $edit = [
+      'enable' => FALSE,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->drupalGet('admin/structure/types/manage/article/display');
+    $this->assertLink('Panelize this view mode');
+    $this->assertLinkByHref('admin/structure/panelizer/edit/node__article__default');
+    $this->assertRaw('<div id="field-display-overview-wrapper">');
   }
 
   /**
    * Tests rendering a node with Panelizer default.
    */
   public function testPanelizerDefault() {
+    $this->drupalPostForm('admin/structure/types/manage/page/display', [
+      'panelizer[enable]' => TRUE,
+      'panelizer[custom]' => TRUE,
+    ], 'Save');
     /** @var \Drupal\panelizer\PanelizerInterface $panelizer */
     $panelizer = \Drupal::service('panelizer');
     $displays = $panelizer->getDefaultPanelsDisplays('node', 'page', 'default');
diff --git a/src/Tests/PanelizerTestTrait.php b/src/Tests/PanelizerTestTrait.php
new file mode 100644
index 0000000..3170a4b
--- /dev/null
+++ b/src/Tests/PanelizerTestTrait.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Drupal\panelizer\Tests;
+
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\Core\Entity\Entity\EntityViewDisplay;
+
+/**
+ * Contains helper methods for writing functional tests of Panelizer.
+ */
+trait PanelizerTestTrait {
+
+  /**
+   * Panelizes a node type's default view display.
+   *
+   * @param string $node_type
+   *   The node type ID.
+   * @param string $display
+   *   (optional) The view display to panelize.
+   * @param array $values
+   *   (optional) Additional form values.
+   */
+  protected function panelize($node_type, $display = NULL, array $values = []) {
+    $this->drupalGet("admin/structure/types/manage/{$node_type}/display/{$display}");
+
+    $values['panelizer[enable]'] = TRUE;
+    $this->drupalPostForm(NULL, $values, 'Save');
+
+    EntityFormDisplay::load('node.' . $node_type . '.default')
+      ->setComponent('panelizer', [
+        'type' => 'panelizer',
+      ])
+      ->save();
+  }
+
+  /**
+   * Unpanelizes a node type's default view display.
+   *
+   * Panelizer is disabled for the display, but its configuration is retained.
+   *
+   * @param string $node_type
+   *   The node type ID.
+   * @param string $display
+   *   (optional) The view display to unpanelize.
+   * @param array $values
+   *   (optional) Additional form values.
+   */
+  protected function unpanelize($node_type, $display = NULL, array $values = []) {
+    $this->drupalGet("admin/structure/types/manage/{$node_type}/display/{$display}");
+
+    $values['panelizer[enable]'] = FALSE;
+    $this->drupalPostForm(NULL, $values, 'Save');
+
+    EntityFormDisplay::load('node.' . $node_type . '.default')
+      ->removeComponent('panelizer')
+      ->save();
+  }
+
+  protected function addPanelizerDefault($node_type, $display = 'default') {
+    $label = $this->getRandomGenerator()->word(16);
+    $id = strtolower($label);
+    $default_id = "node__{$node_type}__{$display}__{$id}";
+    $options = array(
+      'query' => array(
+        'js' => 'nojs',
+      ),
+    );
+
+    $this->drupalGet("admin/structure/types/manage/{$node_type}/display");
+    $this->clickLink('Add panelizer default');
+
+    // Step 1: Enter the default's label and ID.
+    $this->drupalPostForm(NULL, [
+      'id' => $id,
+      'label' => $label,
+    ], 'Next');
+
+    // Step 2: Define contexts.
+    $this->assertResponse(200);
+    $this->assertUrl("admin/structure/panelizer/add/{$default_id}/contexts", $options);
+    $this->drupalPostForm(NULL, [], 'Next');
+
+    // Step 3: Select layout.
+    $this->assertResponse(200);
+    $this->assertUrl("admin/structure/panelizer/add/{$default_id}/layout", $options);
+    $this->drupalPostForm(NULL, [], 'Next');
+
+    // Step 4: Select content.
+    $this->assertResponse(200);
+    $this->assertUrl("admin/structure/panelizer/add/{$default_id}/content", $options);
+    $this->drupalPostForm(NULL, [], 'Finish');
+
+    return $id;
+  }
+
+  /**
+   * Deletes a Panelizer default.
+   *
+   * @param string $node_type
+   *   The node type ID.
+   * @param string $display
+   *   (optional) The view display ID.
+   * @param string $id
+   *   (optional) The default ID.
+   */
+  protected function deletePanelizerDefault($node_type, $display = 'default', $id = 'default') {
+    $this->drupalGet("admin/structure/panelizer/delete/node__{$node_type}__{$display}__{$id}");
+    $this->drupalPostForm(NULL, [], 'Confirm');
+  }
+
+  /**
+   * Asserts that a Panelizer default exists.
+   *
+   * @param string $node_type
+   *   The node type ID.
+   * @param string $display
+   *   (optional) The view display ID.
+   * @param string $id
+   *   (optional) The default ID.
+   */
+  protected function assertDefaultExists($node_type, $display = 'default', $id = 'default') {
+    $settings = EntityViewDisplay::load("node.{$node_type}.{$display}")
+      ->getThirdPartySettings('panelizer');
+
+    $display_exists = isset($settings['displays'][$id]);
+    $this->assertTrue($display_exists);
+  }
+
+  /**
+   * Asserts that a Panelizer default does not exist.
+   *
+   * @param string $node_type
+   *   The node type ID.
+   * @param string $display
+   *   The view display ID.
+   * @param string $id
+   *   The default ID.
+   */
+  protected function assertDefaultNotExists($node_type, $display = 'default', $id = 'default') {
+    $settings = EntityViewDisplay::load("node.{$node_type}.{$display}")
+      ->getThirdPartySettings('panelizer');
+
+    $display_exists = isset($settings['displays'][$id]);
+    $this->assertFalse($display_exists);
+  }
+
+}
diff --git a/src/Wizard/PanelizerAddWizard.php b/src/Wizard/PanelizerAddWizard.php
new file mode 100644
index 0000000..85513e8
--- /dev/null
+++ b/src/Wizard/PanelizerAddWizard.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Drupal\panelizer\Wizard;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\panelizer\Form\PanelizerWizardContentForm;
+use Drupal\panelizer\Form\PanelizerWizardContextForm;
+use Drupal\panelizer\Form\PanelizerWizardGeneralForm;
+
+class PanelizerAddWizard extends PanelizerWizardBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteName() {
+    return 'panelizer.wizard.add.step';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL, $bundle = NULL, $view_mode_name = NULL) {
+    if ($entity_type_id && $bundle && $view_mode_name) {
+      $form_state->set('machine_name_prefix', "{$entity_type_id}__{$bundle}__{$view_mode_name}");
+    }
+    $form = parent::buildForm($form, $form_state);
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $cached_values['id'] = $this->getMachineName();
+    // Some variants like PanelsDisplayVariant need this. Set it to empty.
+    $cached_values['access'] = [];
+    $form_state->setTemporaryValue('wizard', $cached_values);
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $operations = array_map('strval', [
+      $this->getNextOp(),
+      $this->t('Update'),
+      $this->t('Update and save'),
+      $this->t('Save'),
+    ]);
+
+    if (in_array($form_state->getValue('op'), $operations)) {
+      $cached_values = $form_state->getTemporaryValue('wizard');
+      if ($form_state->hasValue('label')) {
+        $config = $cached_values['plugin']->getConfiguration();
+        $config['label'] = $form_state->getValue('label');
+        $cached_values['plugin']->setConfiguration($config);
+      }
+      if ($form_state->hasValue('id')) {
+        $cached_values['id'] = $form_state->getValue('id');
+        /** @var \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $plugin */
+        $plugin = $cached_values['plugin'];
+        $plugin->setStorage($plugin->getStorageType(), $cached_values['id']);
+      }
+    }
+    parent::submitForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function finish(array &$form, FormStateInterface $form_state) {
+    parent::finish($form, $form_state);
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $form_state->setRedirect('panelizer.wizard.edit', ['machine_name' => $cached_values['id'], 'step' => 'content']);
+  }
+
+}
diff --git a/src/Wizard/PanelizerEditWizard.php b/src/Wizard/PanelizerEditWizard.php
new file mode 100644
index 0000000..fd68561
--- /dev/null
+++ b/src/Wizard/PanelizerEditWizard.php
@@ -0,0 +1,225 @@
+<?php
+
+namespace Drupal\panelizer\Wizard;
+
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+class PanelizerEditWizard extends PanelizerWizardBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteName() {
+    return 'panelizer.wizard.edit';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function initValues() {
+    $cached_values = parent::initValues();
+    // Load data in to values to be cached and managed by the
+    // wizard until the user clicks on Save or Cancel.
+    $cached_values['id'] = $this->getMachineName();
+    list($entity_type, $bundle, $view_mode, $display_id) = explode('__', $this->getMachineName());
+    $panelizer = \Drupal::service('panelizer');
+    // Load the panels display variant.
+    /** @var \Drupal\panelizer\Panelizer $panelizer */
+    // @todo this $display_id looks all wrong to me since it's the name and view_mode.
+    $variant_plugin = $panelizer->getDefaultPanelsDisplay($display_id, $entity_type, $bundle, $view_mode);
+    $cached_values['plugin'] = $variant_plugin;
+    $cached_values['label'] = $cached_values['plugin']->getConfiguration()['label'];
+
+    $display = $panelizer->getEntityViewDisplay($entity_type, $bundle, $view_mode);
+    $config = $display->getThirdPartySetting('panelizer', 'displays', []);
+    if (!empty($config[$display_id]['static_context'])) {
+      $cached_values['contexts'] = $config[$display_id]['static_context'];
+    }
+    return $cached_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function customizeForm(array $form, FormStateInterface $form_state) {
+    // The page actions.
+    $form['wizard_actions'] = [
+      '#theme' => 'links',
+      '#links' => [],
+      '#attributes' => [
+        'class' => ['inline'],
+      ]
+    ];
+
+    // The tree of wizard steps.
+    $form['wizard_tree'] = [
+      '#theme' => ['panelizer_wizard_tree'],
+      '#wizard' => $this,
+      '#cached_values' => $form_state->getTemporaryValue('wizard'),
+    ];
+
+    $form['#theme'] = 'panelizer_wizard_form';
+    $form['#attached']['library'][] = 'panelizer/wizard_admin';
+    $form = parent::customizeForm($form, $form_state);
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function actions(FormInterface $form_object, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $operation = $this->getOperation($cached_values);
+
+    $actions = [];
+
+    $actions['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Update'),
+      '#validate' => [
+        '::populateCachedValues',
+        [$form_object, 'validateForm'],
+      ],
+      '#submit' => [
+        [$form_object, 'submitForm'],
+      ],
+    ];
+
+    $actions['update_and_save'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Update and save'),
+      '#button_type' => 'primary',
+      '#validate' => [
+        '::populateCachedValues',
+        [$form_object, 'validateForm'],
+      ],
+      '#submit' => [
+        [$form_object, 'submitForm'],
+      ],
+    ];
+
+    $actions['finish'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Save'),
+      '#validate' => [
+        '::populateCachedValues',
+        [$form_object, 'validateForm'],
+      ],
+      '#submit' => [
+        [$form_object, 'submitForm'],
+      ],
+    ];
+
+    $actions['cancel'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Cancel'),
+      '#submit' => [
+        '::clearTempstore'
+      ],
+    ];
+
+    // Add any submit or validate functions for the step and the global ones.
+    foreach (['submit', 'update_and_save', 'finish'] as $button) {
+      if (isset($operation['validate'])) {
+        $actions[$button]['#validate'] = array_merge($actions[$button]['#validate'], $operation['validate']);
+      }
+      $actions[$button]['#validate'][] = '::validateForm';
+      if (isset($operation['submit'])) {
+        $actions[$button]['#submit'] = array_merge($actions[$button]['#submit'], $operation['submit']);
+      }
+      $actions[$button]['#submit'][] = '::submitForm';
+    }
+    $actions['update_and_save']['#submit'][] = '::finish';
+    $actions['finish']['#submit'][] = '::finish';
+
+    if ($form_state->get('ajax')) {
+      $cached_values = $form_state->getTemporaryValue('wizard');
+      $ajax_parameters = $this->getNextParameters($cached_values);
+      $ajax_parameters['step'] = $this->getStep($cached_values);
+      $ajax_url = Url::fromRoute($this->getRouteName(), $ajax_parameters);
+      $ajax_options = [
+        'query' => $this->getRequest()->query->all() + [
+            FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
+          ],
+      ];
+      $actions['submit']['#ajax'] = [
+        'callback' => '::ajaxSubmit',
+        'url' => $ajax_url,
+        'options' => $ajax_options,
+      ];
+      $actions['update_and_save']['#ajax'] = [
+        'callback' => '::ajaxFinish',
+        'url' => $ajax_url,
+        'options' => $ajax_options,
+      ];
+      $actions['finish']['#ajax'] = [
+        'callback' => '::ajaxFinish',
+        'url' => $ajax_url,
+        'options' => $ajax_options,
+      ];
+    }
+
+    return $actions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $operations = array_map('strval', [
+      $this->getNextOp(),
+      $this->t('Update'),
+      $this->t('Update and save'),
+      $this->t('Save'),
+    ]);
+
+    if (in_array($form_state->getValue('op'), $operations)) {
+      $cached_values = $form_state->getTemporaryValue('wizard');
+      if ($form_state->hasValue('label')) {
+        $config = $cached_values['plugin']->getConfiguration();
+        $config['label'] = $form_state->getValue('label');
+        $cached_values['plugin']->setConfiguration($config);
+      }
+      if ($form_state->hasValue('id')) {
+        $cached_values['id'] = $form_state->getValue('id');
+      }
+      if (is_null($this->machine_name) && !empty($cached_values['id'])) {
+        $this->machine_name = $cached_values['id'];
+      }
+      $this->getTempstore()->set($this->getMachineName(), $cached_values);
+      if (!$form_state->get('ajax')) {
+        $form_state->setRedirect($this->getRouteName(), $this->getNextParameters($cached_values));
+      }
+    }
+  }
+
+  /**
+   * Clears the temporary store.
+   *
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   */
+  public function clearTempstore(array &$form, FormStateInterface $form_state) {
+    $this->getTempstore()->delete($this->getMachineName());
+    list($entity_type_id, $bundle, $view_mode) = explode('__', $this->getMachineName());
+    $bundle_entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id)->getBundleEntityType();
+    if ($view_mode == 'default') {
+      $route = "entity.entity_view_display.{$entity_type_id}.default";
+      $arguments = [
+        $bundle_entity_type => $bundle,
+      ];
+    }
+    else {
+      $route = "entity.entity_view_display.{$entity_type_id}.view_mode";
+      $arguments = [
+        $bundle_entity_type => $bundle,
+        'view_mode_name' => $view_mode,
+      ];
+    }
+    $form_state->setRedirect($route, $arguments);
+  }
+
+}
diff --git a/src/Wizard/PanelizerWizardBase.php b/src/Wizard/PanelizerWizardBase.php
new file mode 100644
index 0000000..7d28b14
--- /dev/null
+++ b/src/Wizard/PanelizerWizardBase.php
@@ -0,0 +1,170 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Wizard\PanelizerWizardBase.
+ */
+
+namespace Drupal\panelizer\Wizard;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Wizard\FormWizardBase;
+use Drupal\panelizer\Access\PanelizerUIAccess;
+use Drupal\panelizer\Form\PanelizerWizardContentForm;
+use Drupal\panelizer\Form\PanelizerWizardContextForm;
+use Drupal\panelizer\Form\PanelizerWizardGeneralForm;
+
+abstract class PanelizerWizardBase extends FormWizardBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function customizeForm(array $form, FormStateInterface $form_state) {
+    $form = parent::customizeForm($form, $form_state);
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    // Get the current form operation.
+    $operation = $this->getOperation($cached_values);
+    $operations = $this->getOperations($cached_values);
+    $default_operation = reset($operations);
+
+    // Get the machine name. There are two ways we can get this data.
+    $storage = $form_state->getStorage();
+    $prefix = isset($storage['machine_name_prefix']) ? $storage['machine_name_prefix'] : $form_state->getTemporaryValue('wizard')['id'];
+
+    if ($operation['form'] == $default_operation['form']) {
+      // Create id and label form elements.
+      $form['name'] = array(
+        '#type' => 'fieldset',
+        '#attributes' => array('class' => array('fieldset-no-legend')),
+        '#title' => $this->getWizardLabel(),
+      );
+      $form['name']['label'] = array(
+        '#type' => 'textfield',
+        '#title' => $this->getMachineLabel(),
+        '#required' => TRUE,
+        '#size' => 32,
+        '#default_value' => !empty($cached_values['label']) ? $cached_values['label'] : '',
+        '#maxlength' => 255,
+        '#disabled' => !empty($cached_values['label']),
+      );
+      $form['name']['id'] = array(
+        '#type' => 'machine_name',
+        '#maxlength' => 128,
+        '#machine_name' => array(
+          'source' => array('name', 'label'),
+          'exists' => $this->exists(),
+          'prefix' => $prefix,
+        ),
+        '#description' => $this->t('A unique machine-readable name for this display. It must only contain lowercase letters, numbers, and underscores.'),
+        '#default_value' => !empty($cached_values['id']) ? $cached_values['id'] : '',
+        '#disabled' => !empty($cached_values['id']),
+      );
+    }
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getWizardLabel() {
+    return $this->t('Wizard Information');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMachineLabel() {
+    return $this->t('Wizard name');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function exists() {
+    return '\Drupal\panelizer\Form\PanelizerWizardGeneralForm::validateMachineName';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations($cached_values) {
+    $operations = [
+      'general' => [
+        'form' => PanelizerWizardGeneralForm::class,
+        'title' => $this->t('General settings'),
+      ],
+      'contexts' => [
+        'form' => PanelizerWizardContextForm::class,
+        'title' => $this->t('Contexts'),
+      ],
+    ];
+
+    // Add any wizard operations from the plugin itself.
+    foreach ($cached_values['plugin']->getWizardOperations($cached_values) as $name => $operation) {
+      $operations[$name] = $operation;
+    }
+
+    // Change the class that manages the Content step.
+    if (isset($operations['content'])) {
+      //$operations['content']['form'] = PanelizerWizardContentForm::class;
+    }
+
+    return $operations;
+  }
+
+  public function initValues() {
+    $cached_values = parent::initValues();
+    $cached_values['access'] = new PanelizerUIAccess();
+    if (empty($cached_values['plugin'])) {
+      /** @var \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $plugin */
+      $plugin = \Drupal::service('plugin.manager.display_variant')->createInstance('panels_variant');
+      $plugin->setPattern('panelizer');
+      $plugin->setBuilder('ipe');
+      $plugin->setStorage('panelizer_default', 'TEMPORARY_STORAGE_ID');
+      $cached_values['plugin'] = $plugin;
+    }
+    if (empty($cached_values['contexts'])) {
+      $cached_values['contexts'] = [];
+    }
+    return $cached_values;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function finish(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+
+    // Save the panels display mode and its custom settings as third party
+    // data of the display mode for this entity+bundle+display.
+    /** @var \Drupal\panelizer\Panelizer $panelizer */
+    $panelizer = \Drupal::service('panelizer');
+    /** @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface $invalidator */
+    $invalidator = \Drupal::service('cache_tags.invalidator');
+    list($entity_type, $bundle, $view_mode, $display_id) = explode('__', $cached_values['id']);
+    $panelizer->setDefaultPanelsDisplay($display_id, $entity_type, $bundle, $view_mode, $cached_values['plugin']);
+    $panelizer->setDisplayStaticContexts($display_id, $entity_type, $bundle, $view_mode, $cached_values['contexts']);
+
+    parent::finish($form, $form_state);
+    $form_state->setRedirect('panelizer.wizard.edit', ['machine_name' => $cached_values['id']]);
+    $invalidator->invalidateTags(["panelizer_default:$entity_type:$bundle:$view_mode:$display_id"]);
+  }
+
+  /**
+   * Wraps the context mapper.
+   *
+   * @return \Drupal\ctools\ContextMapperInterface
+   */
+  protected function getContextMapper() {
+    return \Drupal::service('ctools.context_mapper');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    return $this->getContextMapper()->getContextValues($cached_values['contexts']);
+  }
+
+}
diff --git a/templates/panelizer-wizard-form.html.twig b/templates/panelizer-wizard-form.html.twig
new file mode 100644
index 0000000..a691162
--- /dev/null
+++ b/templates/panelizer-wizard-form.html.twig
@@ -0,0 +1,31 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a 'form' element.
+ *
+ * Available variables
+ * - attributes: A list of HTML attributes for the wrapper element.
+ * - children: The child elements of the form.
+ *
+ * @see template_preprocess_form()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="panelizer-wizard">
+  <div class="panelizer-wizard-actions">
+    {{ form.wizard_actions }}
+  </div>
+  <div class="panelizer-wizard-main clearfix">
+    <div class="panelizer-wizard-tree">
+      {{ form.wizard_tree }}
+    </div>
+    <div class="panelizer-wizard-form">
+      {{ form|without('wizard_actions', 'wizard_tree', 'actions') }}
+    </div>
+  </div>
+
+  <div class="panelizer-wizard-form-actions">
+    {{ form.actions }}
+  </div>
+</div>
diff --git a/templates/panelizer-wizard-tree.html.twig b/templates/panelizer-wizard-tree.html.twig
new file mode 100644
index 0000000..7d48fcf
--- /dev/null
+++ b/templates/panelizer-wizard-tree.html.twig
@@ -0,0 +1,47 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display wizard tree.
+ *
+ * Available variables:
+ * - step: The current step name.
+ * - tree: A nested list of menu items. Each menu item contains:
+ *   - title: The menu link title.
+ *   - url: The menu link url, instance of \Drupal\Core\Url
+ *   - children: The menu item child items.
+ *   - step: The name of the step.
+ *
+ * @ingroup themeable
+ */
+#}
+{% import _self as panelizer %}
+
+{#
+  We call a macro which calls itself to render the full tree.
+  @see http://twig.sensiolabs.org/doc/tags/macro.html
+#}
+{{ panelizer.wizard_tree(tree, step, 0) }}
+
+{% macro wizard_tree(items, step, menu_level) %}
+  {% import _self as panelizer %}
+  {% if items %}
+    <ul>
+    {% for item in items %}
+      <li>
+        {% if item.url %}
+          {% if step is same as(item.step) %}
+            <strong>{{ link(item.title, item.url) }}</strong>
+          {% else %}
+            {{ link(item.title, item.url) }}
+          {% endif %}
+        {% else %}
+          {{ item.title }}
+        {% endif %}
+        {% if item.children %}
+          {{ panelizer.wizard_tree(item.children, step, menu_level + 1) }}
+        {% endif %}
+      </li>
+    {% endfor %}
+    </ul>
+  {% endif %}
+{% endmacro %}
