diff --git a/panelizer.module b/panelizer.module
index 2672b1b..2049b16 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().
@@ -162,9 +163,11 @@ function panelizer_form_entity_view_display_edit_form_alter(&$form, FormStateInt
       '#tree' => TRUE,
     ];
     $form['panelizer']['enable'] = [
-      '#type' => 'checkbox',
       '#title' => t('Panelize this view mode'),
-      '#default_value' => !empty($settings['enable']),
+      '#type' => 'link',
+      '#url' => Url::fromRoute('panelizer.wizard', [
+        'machine_name' => $display->getTargetEntityTypeId() . '__' . $display->getTargetBundle() . '__' . $display->getMode(),
+      ]),
     ];
     $form['panelizer']['custom'] = [
       '#type' => 'checkbox',
diff --git a/panelizer.routing.yml b/panelizer.routing.yml
index feac328..98bf6fa 100644
--- a/panelizer.routing.yml
+++ b/panelizer.routing.yml
@@ -12,3 +12,87 @@ panelizer.panels_ipe.revert_to_default:
     _method: 'POST'
     _permission: 'access panels in-place editing'
     _custom_access: '\Drupal\panelizer\Controller\PanelizerPanelsIPEController::accessRevertToDefault'
+
+# Wizard
+panelizer.wizard:
+  path: '/admin/panelizer/wizard/{machine_name}'
+  defaults:
+    _wizard: '\Drupal\panelizer\Wizard\PanelizerWizard'
+    _title: 'Panelizer Wizard'
+    tempstore_id: 'panelizer.wizard'
+  requirements:
+    _access: 'TRUE'
+
+panelizer.wizard.step:
+  path: '/admin/panelizer/wizard/{machine_name}/{step}'
+  defaults:
+    _wizard: '\Drupal\panelizer\Wizard\PanelizerWizard'
+    _title: 'Panelizer Wizard'
+    tempstore_id: 'panelizer.wizard'
+  requirements:
+    _access: 'TRUE'
+
+# 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 pages'
+
+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 pages'
+
+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 pages'
+
+# Content blocks
+panelizer.wizard.step.content.select_block:
+  path: '/admin/panelizer/wizard/block_display/{block_display}/select'
+  defaults:
+    _controller: '\Drupal\panelizer\Controller\PanelizerWizardController::selectBlock'
+    _title: 'Select block'
+    tempstore_id: 'panelizer.block_display'
+  requirements:
+    _permission: 'administer pages'
+
+panelizer.wizard.step.content.add_block:
+  path: '/admin/panelizer/wizard/block_display/{block_display}/add/{block_id}'
+  defaults:
+    _form: '\Drupal\panelizer\Form\PanelizerWizardAddBlockForm'
+    _title: 'Add block'
+    tempstore_id: 'panelizer.block_display'
+  requirements:
+    _permission: 'administer pages'
+
+panelizer.wizard.step.content.edit_block:
+  path: '/admin/panelizer/wizard/block_display/{block_display}/edit/{block_id}'
+  defaults:
+    _form: '\Drupal\panelizer\Form\PanelizerWizardEditBlockForm'
+    _title: 'Edit block'
+    tempstore_id: 'panelizer.block_display'
+  requirements:
+    _permission: 'administer pages'
+
+panelizer.wizard.step.content.delete_block:
+  path: '/admin/panelizer/wizard/block_display/{block_display}/delete/{block_id}'
+  defaults:
+    _form: '\Drupal\panelizer\Form\PanelizerWizardDeleteBlockForm'
+    _title: 'Delete block'
+    tempstore_id: 'panelizer.block_display'
+  requirements:
+    _permission: 'administer pages'
diff --git a/src/Controller/PanelizerWizardController.php b/src/Controller/PanelizerWizardController.php
new file mode 100644
index 0000000..3f6364d
--- /dev/null
+++ b/src/Controller/PanelizerWizardController.php
@@ -0,0 +1,167 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Controller\PanelizerWizardController.
+ */
+
+namespace Drupal\panelizer\Controller;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Block\BlockManagerInterface;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextHandlerInterface;
+use Drupal\Core\Url;
+use Drupal\ctools\Form\AjaxFormTrait;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides route controllers for the Panelizer wizard.
+ */
+class PanelizerWizardController extends ControllerBase {
+
+  use AjaxFormTrait;
+
+  /**
+   * The block manager.
+   *
+   * @var \Drupal\Core\Block\BlockManagerInterface
+   */
+  protected $blockManager;
+
+  /**
+   * The condition manager.
+   *
+   * @var \Drupal\Core\Plugin\Context\ContextAwarePluginManagerInterface
+   */
+  protected $conditionManager;
+
+  /**
+   * The variant manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $variantManager;
+
+  /**
+   * The context handler.
+   *
+   * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
+   */
+  protected $contextHandler;
+
+  /**
+   * Tempstore factory.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * Constructs a new VariantPluginEditForm.
+   *
+   * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
+   *   The block manager.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $condition_manager
+   *   The condition manager.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $variant_manager
+   *   The variant manager.
+   * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler
+   *   The context handler.
++   * @param \Drupal\user\SharedTempStoreFactory $tempstore
++   *   The tempstore factory.
+   */
+  public function __construct(BlockManagerInterface $block_manager, PluginManagerInterface $condition_manager, PluginManagerInterface $variant_manager, ContextHandlerInterface $context_handler, SharedTempStoreFactory $tempstore) {
+    $this->blockManager = $block_manager;
+    $this->conditionManager = $condition_manager;
+    $this->variantManager = $variant_manager;
+    $this->contextHandler = $context_handler;
+    $this->tempstore = $tempstore;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.block'),
+      $container->get('plugin.manager.condition'),
+      $container->get('plugin.manager.display_variant'),
+      $container->get('context.handler'),
+      $container->get('user.shared_tempstore')
+    );
+  }
+
+  /**
+   * Presents a list of blocks to add to the variant.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request.
+   * @param string $block_display
+   *   The identifier of the block display variant.
+   * @param string $tempstore_id
+   *   The identifier of the temporary store.
+   *
+   * @return array
+   *   The block selection page.
+   */
+  public function selectBlock(Request $request, $block_display, $tempstore_id) {
+    $cached_values = $this->tempstore->get($tempstore_id)->get($block_display);
+    /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */
+    $variant_plugin = $cached_values['plugin'];
+
+    // Rehydrate the contexts on this end.
+    $contexts = [];
+    /**
+     * @var string $context_name
+     * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context_definition
+     */
+    foreach ($cached_values['contexts'] as $context_name => $context_definition) {
+      $contexts[$context_name] = new Context($context_definition);
+    }
+    $variant_plugin->setContexts($contexts);
+
+    // Add a section containing the available blocks to be added to the variant.
+    $build = [
+      '#type' => 'container',
+      '#attached' => [
+        'library' => [
+          'core/drupal.ajax',
+        ],
+      ],
+    ];
+    $available_plugins = $this->blockManager->getDefinitionsForContexts($variant_plugin->getContexts());
+    // Order by category, and then by admin label.
+    $available_plugins = $this->blockManager->getSortedDefinitions($available_plugins);
+    foreach ($available_plugins as $plugin_id => $plugin_definition) {
+      // Make a section for each region.
+      $category = $plugin_definition['category'];
+      $category_key = 'category-' . $category;
+      if (!isset($build[$category_key])) {
+        $build[$category_key] = [
+          '#type' => 'fieldgroup',
+          '#title' => $category,
+          'content' => [
+            '#theme' => 'links',
+          ],
+        ];
+      }
+      // Add a link for each available block within each region.
+      $build[$category_key]['content']['#links'][$plugin_id] = [
+        'title' => $plugin_definition['admin_label'],
+        'url' => Url::fromRoute('panelizer.wizard.step.content.add_block', [
+          'block_display' => $block_display,
+          'block_id' => $plugin_id,
+          'region' => $request->query->get('region'),
+          'destination' => $request->query->get('destination'),
+        ]),
+        'attributes' => $this->getAjaxAttributes(),
+      ];
+    }
+    return $build;
+  }
+
+}
diff --git a/src/Form/PanelizerWizardAddBlockForm.php b/src/Form/PanelizerWizardAddBlockForm.php
new file mode 100644
index 0000000..1d173c7
--- /dev/null
+++ b/src/Form/PanelizerWizardAddBlockForm.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardAddBlockForm.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\page_manager\PageVariantInterface;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides a form for adding a block plugin to a variant.
+ */
+class PanelizerWizardAddBlockForm extends PanelizerWizardConfigureBlockFormBase   {
+
+  /**
+   * The block manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $blockManager;
+
+  /**
+   * Constructs a new VariantPluginFormBase.
+   *
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $block_manager
+   *   The block manager.
+   */
+  public function __construct(SharedTempStoreFactory $tempstore, PluginManagerInterface $block_manager) {
+    parent::__construct($tempstore);
+    $this->blockManager = $block_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.shared_tempstore'),
+      $container->get('plugin.manager.block')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panelizer_wizard_add_block_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function prepareBlock($plugin_id) {
+    $block = $this->blockManager->createInstance($plugin_id);
+    $block_id = $this->getVariantPlugin()->addBlock($block->getConfiguration());
+    return $this->getVariantPlugin()->getBlock($block_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, $block_display = NULL, $block_id = NULL) {
+    $form = parent::buildForm($form, $form_state, $block_display, $block_id);
+    $form['region']['#default_value'] = $request->query->get('region');
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function submitText() {
+    return $this->t('Add block');
+  }
+
+}
diff --git a/src/Form/PanelizerWizardConfigureBlockFormBase.php b/src/Form/PanelizerWizardConfigureBlockFormBase.php
new file mode 100644
index 0000000..d442127
--- /dev/null
+++ b/src/Form/PanelizerWizardConfigureBlockFormBase.php
@@ -0,0 +1,199 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardConfigureBlockFormBase.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+use Drupal\page_manager\PageVariantInterface;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a base form for configuring a block as part of a variant.
+ */
+abstract class PanelizerWizardConfigureBlockFormBase extends FormBase {
+
+  use ContextAwarePluginAssignmentTrait;
+
+  /**
+   * Tempstore factory.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * The variant plugin.
+   *
+   * @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant
+   */
+  protected $variantPlugin;
+
+  /**
+   * The plugin being configured.
+   *
+   * @var \Drupal\Core\Block\BlockPluginInterface
+   */
+  protected $block;
+
+  /**
+   * Constructs a new VariantPluginConfigureBlockFormBase.
+   *
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore
+   *   The tempstore factory.
+   */
+  public function __construct(SharedTempStoreFactory $tempstore) {
+    $this->tempstore = $tempstore;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.shared_tempstore')
+    );
+  }
+
+  /**
+   * Get the tempstore id.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'panelizer.block_display';
+  }
+
+  /**
+   * Get the tempstore.
+   *
+   * @return \Drupal\user\SharedTempStore
+   */
+  protected function getTempstore() {
+    return $this->tempstore->get($this->getTempstoreId());
+  }
+
+  /**
+   * Prepares the block plugin based on the block ID.
+   *
+   * @param string $block_id
+   *   Either a block ID, or the plugin ID used to create a new block.
+   *
+   * @return \Drupal\Core\Block\BlockPluginInterface
+   *   The block plugin.
+   */
+  abstract protected function prepareBlock($block_id);
+
+  /**
+   * Returns the text to use for the submit button.
+   *
+   * @return string
+   *   The submit button text.
+   */
+  abstract protected function submitText();
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $block_display = NULL, $block_id = NULL) {
+    $cached_values = $this->tempstore->get($this->getTempstoreId())->get($block_display);
+    /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */
+    $this->variantPlugin = $cached_values['plugin'];
+
+    // Rehydrate the contexts on this end.
+    $contexts = [];
+    /**
+     * @var string $context_name
+     * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context_definition
+     */
+    foreach ($cached_values['contexts'] as $context_name => $context_definition) {
+      $contexts[$context_name] = new Context($context_definition);
+    }
+    $this->variantPlugin->setContexts($contexts);
+
+    $this->block = $this->prepareBlock($block_id);
+    $form_state->set('variant_id', $this->getVariantPlugin()->id());
+    $form_state->set('block_id', $this->block->getConfiguration()['uuid']);
+
+    $form['#tree'] = TRUE;
+    $form['settings'] = $this->block->buildConfigurationForm([], $form_state);
+    $form['settings']['id'] = [
+      '#type' => 'value',
+      '#value' => $this->block->getPluginId(),
+    ];
+    $form['region'] = [
+      '#title' => $this->t('Region'),
+      '#type' => 'select',
+      '#options' => $this->getVariantPlugin()->getRegionNames(),
+      '#default_value' => $this->getVariantPlugin()->getRegionAssignment($this->block->getConfiguration()['uuid']),
+      '#required' => TRUE,
+    ];
+
+    if ($this->block instanceof ContextAwarePluginInterface) {
+      $form['context_mapping'] = $this->addContextAssignmentElement($this->block, $this->getVariantPlugin()->getContexts());
+    }
+
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->submitText(),
+      '#button_type' => 'primary',
+    ];
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    // The page might have been serialized, resulting in a new variant
+    // collection. Refresh the block object.
+    $this->block = $this->getVariantPlugin()->getBlock($form_state->get('block_id'));
+
+    $settings = (new FormState())->setValues($form_state->getValue('settings'));
+    // Call the plugin validate handler.
+    $this->block->validateConfigurationForm($form, $settings);
+    // Update the original form values.
+    $form_state->setValue('settings', $settings->getValues());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $settings = (new FormState())->setValues($form_state->getValue('settings'));
+
+    // Call the plugin submit handler.
+    $this->block->submitConfigurationForm($form, $settings);
+    // Update the original form values.
+    $form_state->setValue('settings', $settings->getValues());
+
+    if ($this->block instanceof ContextAwarePluginInterface) {
+      $this->block->setContextMapping($form_state->getValue('context_mapping', []));
+    }
+
+    $this->getVariantPlugin()->updateBlock($this->block->getConfiguration()['uuid'], ['region' => $form_state->getValue('region')]);
+
+    $cached_values = $this->getTempstore()->get($form_state->get('variant_id'));
+    $cached_values['plugin'] = $this->getVariantPlugin();
+    $this->getTempstore()->set($form_state->get('variant_id'), $cached_values);
+  }
+
+  /**
+   * Gets the variant plugin for this page variant entity.
+   *
+   * @return \Drupal\ctools\Plugin\BlockVariantInterface
+   */
+  protected function getVariantPlugin() {
+    return $this->variantPlugin;
+  }
+
+}
diff --git a/src/Form/PanelizerWizardContentForm.php b/src/Form/PanelizerWizardContentForm.php
new file mode 100644
index 0000000..e16c553
--- /dev/null
+++ b/src/Form/PanelizerWizardContentForm.php
@@ -0,0 +1,257 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\panelizer\Form\PanelizerWizardContentForm.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\ctools\Form\AjaxFormTrait;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a form for editing a variant's content.
+ */
+class PanelizerWizardContentForm extends FormBase {
+
+  use AjaxFormTrait;
+
+  /**
+   * Tempstore factory.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * Constructs a new VariantPluginContentForm.
+   *
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore
+   *   The tempstore factory.
+   */
+  public function __construct(SharedTempStoreFactory $tempstore) {
+    $this->tempstore = $tempstore;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.shared_tempstore')
+    );
+  }
+
+  /**
+   * Get the tempstore ID.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'panelizer.block_display';
+  }
+
+  /**
+   * Get the tempstore.
+   *
+   * @return \Drupal\user\SharedTempStore
+   */
+  protected function getTempstore() {
+    return $this->tempstore->get($this->getTempstoreId());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panelizer_wizard_block_page_content';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */
+    $variant_plugin = $cached_values['plugin'];
+
+    // Store the block display plugin so we can get it in our dialogs.
+    if (!empty($this->getTempstore()->get($variant_plugin->id())['plugin'])) {
+      $variant_plugin->setConfiguration($this->getTempstore()->get($variant_plugin->id())['plugin']->getConfiguration());
+      $form_state->setTemporaryValue('wizard', $cached_values);
+    }
+    $context_definitions = [];
+    foreach ($variant_plugin->getContexts() as $context_name => $context) {
+      $context_definitions[$context_name] = $context->getContextDefinition();
+    }
+    $this->getTempstore()->set($variant_plugin->id(), [
+      'plugin' => $variant_plugin,
+      'access' => $cached_values['access'],
+      'contexts' => $context_definitions,
+    ]);
+
+    // Set up the attributes used by a modal to prevent duplication later.
+    $attributes = $this->getAjaxAttributes();
+    $add_button_attributes = $this->getAjaxButtonAttributes();
+
+    if ($block_assignments = $variant_plugin->getRegionAssignments()) {
+      // Build a table of all blocks used by this variant.
+      $form['add'] = [
+        '#type' => 'link',
+        '#title' => $this->t('Add new block'),
+        '#url' => Url::fromRoute('panelizer.wizard.step.content.select_block', [
+          'block_display' => $variant_plugin->id(),
+          'destination' => $this->getRequest()->getRequestUri(),
+        ]),
+        '#attributes' => $add_button_attributes,
+        '#attached' => [
+          'library' => [
+            'core/drupal.ajax',
+          ],
+        ],
+      ];
+      $form['blocks'] = [
+        '#type' => 'table',
+        '#header' => [
+          $this->t('Label'),
+          $this->t('Plugin ID'),
+          $this->t('Region'),
+          $this->t('Weight'),
+          $this->t('Operations'),
+        ],
+        '#empty' => $this->t('There are no regions for blocks.'),
+      ];
+      // Loop through the blocks per region.
+      foreach ($block_assignments as $region => $blocks) {
+        // Add a section for each region and allow blocks to be dragged between
+        // them.
+        $form['blocks']['#tabledrag'][] = [
+          'action' => 'match',
+          'relationship' => 'sibling',
+          'group' => 'block-region-select',
+          'subgroup' => 'block-region-' . $region,
+          'hidden' => FALSE,
+        ];
+        $form['blocks']['#tabledrag'][] = [
+          'action' => 'order',
+          'relationship' => 'sibling',
+          'group' => 'block-weight',
+          'subgroup' => 'block-weight-' . $region,
+        ];
+        $form['blocks'][$region] = [
+          '#attributes' => [
+            'class' => ['region-title', 'region-title-' . $region],
+            'no_striping' => TRUE,
+          ],
+        ];
+        $form['blocks'][$region]['title'] = [
+          '#markup' => $variant_plugin->getRegionName($region),
+          '#wrapper_attributes' => [
+            'colspan' => 5,
+          ],
+        ];
+        $form['blocks'][$region . '-message'] = [
+          '#attributes' => [
+            'class' => [
+              'region-message',
+              'region-' . $region . '-message',
+              empty($blocks) ? 'region-empty' : 'region-populated',
+            ],
+          ],
+        ];
+        $form['blocks'][$region . '-message']['message'] = [
+          '#markup' => '<em>' . $this->t('No blocks in this region') . '</em>',
+          '#wrapper_attributes' => [
+            'colspan' => 5,
+          ],
+        ];
+
+        /** @var \Drupal\Core\Block\BlockPluginInterface[] $blocks */
+        foreach ($blocks as $block_id => $block) {
+          $row = [
+            '#attributes' => [
+              'class' => ['draggable'],
+            ],
+          ];
+          $row['label']['#markup'] = $block->label();
+          $row['id']['#markup'] = $block->getPluginId();
+          // Allow the region to be changed for each block.
+          $row['region'] = [
+            '#title' => $this->t('Region'),
+            '#title_display' => 'invisible',
+            '#type' => 'select',
+            '#options' => $variant_plugin->getRegionNames(),
+            '#default_value' => $variant_plugin->getRegionAssignment($block_id),
+            '#attributes' => [
+              'class' => ['block-region-select', 'block-region-' . $region],
+            ],
+          ];
+          // Allow the weight to be changed for each block.
+          $configuration = $block->getConfiguration();
+          $row['weight'] = [
+            '#type' => 'weight',
+            '#default_value' => isset($configuration['weight']) ? $configuration['weight'] : 0,
+            '#title' => $this->t('Weight for @block block', ['@block' => $block->label()]),
+            '#title_display' => 'invisible',
+            '#attributes' => [
+              'class' => ['block-weight', 'block-weight-' . $region],
+            ],
+          ];
+          // Add the operation links.
+          $operations = [];
+          $operations['edit'] = [
+            'title' => $this->t('Edit'),
+            'url' => Url::fromRoute('panelizer.wizard.step.content.edit_block', [
+              'block_display' => $variant_plugin->id(),
+              'block_id' => $block_id,
+              'destination' => $this->getRequest()->getRequestUri(),
+            ]),
+            'attributes' => $attributes,
+          ];
+          $operations['delete'] = [
+            'title' => $this->t('Delete'),
+            'url' => Url::fromRoute('panelizer.wizard.step.content.delete_block', [
+              'block_display' => $variant_plugin->id(),
+              'block_id' => $block_id,
+              'destination' => $this->getRequest()->getRequestUri(),
+            ]),
+            'attributes' => $attributes,
+          ];
+
+          $row['operations'] = [
+            '#type' => 'operations',
+            '#links' => $operations,
+          ];
+          $form['blocks'][$block_id] = $row;
+        }
+      }
+    }
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\Plugin\DisplayVariant\PageBlockDisplayVariant $variant_plugin */
+    $variant_plugin = $cached_values['plugin'];
+
+    // If the blocks were rearranged, update their values.
+    if (!$form_state->isValueEmpty('blocks')) {
+      foreach ($form_state->getValue('blocks') as $block_id => $block_values) {
+        $variant_plugin->updateBlock($block_id, $block_values);
+      }
+    }
+
+    // Remove from the tempstore so we refresh from the database the next time
+    // we come here.
+    $this->getTempstore()->delete($variant_plugin->id());
+  }
+
+}
diff --git a/src/Form/PanelizerWizardContextConfigure.php b/src/Form/PanelizerWizardContextConfigure.php
new file mode 100644
index 0000000..4dc2f28
--- /dev/null
+++ b/src/Form/PanelizerWizardContextConfigure.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardContextConfigure.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Plugin\Context\ContextInterface;
+use Drupal\ctools\Form\ContextConfigure;
+
+class PanelizerWizardContextConfigure extends ContextConfigure {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getParentRouteInfo($cached_values) {
+    return ['panelizer.wizard.step', [
+      'machine_name' => $cached_values['id'],
+      'step' => 'contexts',
+    ]];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    return $cached_values['contexts'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function addContext($cached_values, $context_id, ContextInterface $context) {
+    $context_config = [
+      'label' => $context->getContextDefinition()->getLabel(),
+      'type' => $context->getContextDefinition()->getDataType(),
+      'description' => $context->getContextDefinition()->getDescription(),
+      'value' => strpos($context->getContextDefinition()->getDataType(), 'entity:') === 0 ? $context->getContextValue()->uuid() : $context->getContextValue(),
+    ];
+    $cached_values['contexts'][$context_id] = $context_config;
+    return $cached_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function contextExists($value, $element, $form_state) {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function disableMachineName($cached_values, $machine_name) {
+    if ($machine_name) {
+      return !empty($cached_values['contexts'][$machine_name]);
+    }
+    return FALSE;
+  }
+
+}
diff --git a/src/Form/PanelizerWizardContextDeleteForm.php b/src/Form/PanelizerWizardContextDeleteForm.php
new file mode 100644
index 0000000..783eff9
--- /dev/null
+++ b/src/Form/PanelizerWizardContextDeleteForm.php
@@ -0,0 +1,57 @@
+<?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();
+    return $this->t('Are you sure you want to delete the context @label?', ['@label' => $cached_values['contexts'][$this->context_id]['label']]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    $cached_values = $this->getTempstore();
+
+    return new Url('panelizer.wizard.step', [
+      'machine_name' => $cached_values['id'],
+      'step' => 'contexts',
+    ]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $cached_values = $this->getTempstore();
+    drupal_set_message($this->t('The static context %label has been removed.', ['%label' => $cached_values['contexts'][$this->context_id]['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..dd3556a
--- /dev/null
+++ b/src/Form/PanelizerWizardContextForm.php
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardContextForm.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Form\ManageContext;
+
+/**
+ * Simple wizard step form.
+ */
+class PanelizerWizardContextForm extends ManageContext {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $relationships = FALSE;
+
+  /**
+   * Returns a unique string identifying the form.
+   *
+   * @return string
+   *   The unique string identifying the form.
+   */
+  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) {
+    if (!isset($cached_values['contexts'])) {
+      $cached_values['contexts'] = [];
+    }
+
+    return $this->getContextMapper()->getContextValues($cached_values['contexts']);
+  }
+
+  /**
+   * Wraps the context mapper.
+   *
+   * @return \Drupal\page_manager\ContextMapperInterface
+   */
+  protected function getContextMapper() {
+    return \Drupal::service('page_manager.context_mapper');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getTempstoreId() {
+    return 'panelizer.wizard.contexts';
+  }
+
+  /**
+   * {@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) {
+    return !empty($cached_values['contexts'][$row]);
+  }
+
+}
diff --git a/src/Form/PanelizerWizardDeleteBlockForm.php b/src/Form/PanelizerWizardDeleteBlockForm.php
new file mode 100644
index 0000000..e5bf70d
--- /dev/null
+++ b/src/Form/PanelizerWizardDeleteBlockForm.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardDeleteBlockForm.
+ */
+
+namespace Drupal\panelizer\Form;
+
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\page_manager\PageVariantInterface;
+
+/**
+ * Provides a form for deleting an access condition.
+ */
+class PanelizerWizardDeleteBlockForm extends ConfirmFormBase {
+
+  /**
+   * @var \Drupal\ctools\Plugin\BlockVariantInterface
+   */
+  protected $plugin;
+
+  /**
+   * The plugin being configured.
+   *
+   * @var \Drupal\Core\Block\BlockPluginInterface
+   */
+  protected $block;
+
+  /**
+   * Get the tempstore id.
+   *
+   * @return string
+   */
+  protected function getTempstoreId() {
+    return 'panelizer.block_display';
+  }
+
+  /**
+   * Get the tempstore.
+   *
+   * @return \Drupal\user\SharedTempStore
+   */
+  protected function getTempstore() {
+    return \Drupal::service('user.shared_tempstore')->get($this->getTempstoreId());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panelizer_wizard_delete_block_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to delete the block %label?', ['%label' => $this->block->label()]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return \Drupal::request()->attributes->get('destination');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $block_display = NULL, $block_id = NULL) {
+    $this->plugin = $this->getTempstore()->get($block_display)['plugin'];
+    $this->block = $this->plugin->getBlock($block_id);
+    $form['block_display'] = [
+      '#type' => 'value',
+      '#value' => $block_display
+    ];
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->plugin->removeBlock($this->block->getConfiguration()['uuid']);
+    $cached_values = $this->getTempstore()->get($form_state->getValue('block_display'));
+    $cached_values['plugin'] = $this->plugin;
+    $this->getTempstore()->set($form_state->getValue('block_display'), $cached_values);
+    drupal_set_message($this->t('The block %label has been removed.', ['%label' => $this->block->label()]));
+  }
+
+}
diff --git a/src/Form/PanelizerWizardEditBlockForm.php b/src/Form/PanelizerWizardEditBlockForm.php
new file mode 100644
index 0000000..ddf9366
--- /dev/null
+++ b/src/Form/PanelizerWizardEditBlockForm.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardEditBlockForm.
+ */
+
+namespace Drupal\panelizer\Form;
+
+/**
+ * Provides a form for editing a block plugin of a variant.
+ */
+class PanelizerWizardEditBlockForm extends PanelizerWizardConfigureBlockFormBase  {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'panelizer_wizard_edit_block_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function prepareBlock($block_id) {
+    return $this->getVariantPlugin()->getBlock($block_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function submitText() {
+    return $this->t('Update block');
+  }
+
+}
diff --git a/src/Form/PanelizerWizardGeneralForm.php b/src/Form/PanelizerWizardGeneralForm.php
new file mode 100644
index 0000000..0835246
--- /dev/null
+++ b/src/Form/PanelizerWizardGeneralForm.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Form\PanelizerWizardGeneralForm.
+ */
+
+namespace Drupal\panelizer\Form;
+
+
+use Drupal\Core\Form\FormBase;
+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;
+
+  /**
+   * Returns a unique string identifying the form.
+   *
+   * @return string
+   *   The unique string identifying the form.
+   */
+  public function getFormId() {
+    return 'panelizer_wizard_general_form';
+  }
+
+  /**
+   * Form constructor.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   The form structure.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $machine_name = NULL) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+
+    $form['custom'] = [
+      '#type' => 'checkbox',
+      '#title' => t('Allow custom overrides of each entity'),
+      '#default_value' => !empty($cached_values['one']) ? $cached_values['one'] : FALSE,
+    ];
+
+    return $form;
+  }
+
+  /**
+   * Form submission handler.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $keys = array(
+      'custom',
+    );
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    foreach ($keys as $key) {
+      $cached_values[$key] = $form_state->getValue($key);
+    }
+    $form_state->setTemporaryValue('wizard', $cached_values);
+  }
+
+}
diff --git a/src/Panelizer.php b/src/Panelizer.php
index 864191d..1a8c964 100644
--- a/src/Panelizer.php
+++ b/src/Panelizer.php
@@ -147,7 +147,7 @@ class Panelizer implements PanelizerInterface {
    * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface|NULL
    *   The entity view display if one exists; NULL otherwise.
    */
-  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.
@@ -564,4 +564,4 @@ class Panelizer implements PanelizerInterface {
     return $this->hasOperationPermission($op, $entity_type_id, $bundle, $account);
   }
 
-}
\ No newline at end of file
+}
diff --git a/src/Wizard/PanelizerWizard.php b/src/Wizard/PanelizerWizard.php
new file mode 100644
index 0000000..0c6cd54
--- /dev/null
+++ b/src/Wizard/PanelizerWizard.php
@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\panelizer\Wizard\PanelizerWizard.
+ */
+
+namespace Drupal\panelizer\Wizard;
+
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ctools\Wizard\FormWizardBase;
+use Drupal\panelizer\Form\PanelizerWizardContentForm;
+
+class PanelizerWizard extends FormWizardBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getWizardLabel() {
+    return $this->t('Wizard Information');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMachineLabel() {
+    return $this->t('Wizard Test Name');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations($cached_values) {
+    $operations = [
+      'general' => [
+        'form' => 'Drupal\panelizer\Form\PanelizerWizardGeneralForm',
+        'title' => $this->t('General settings'),
+      ],
+      'contexts' => [
+        'form' => 'Drupal\panelizer\Form\PanelizerWizardContextForm',
+        'title' => $this->t('Contexts'),
+      ],
+    ];
+
+    // Add any wizard operations from the plugin itself.
+    if (isset($cached_values['page_variant'])) {
+      $page_variant = $cached_values['page_variant'];
+      $variant_plugin = $page_variant->getVariantPlugin();
+      $variant_plugin->setContexts($this->getContexts($cached_values));
+      foreach ($variant_plugin->getWizardOperations($cached_values) as $name => $operation) {
+        $operation['values']['plugin'] = $variant_plugin;
+        $operation['submit'][] = '::submitVariantStep';
+        $operations[$name] = $operation;
+      }
+
+      // Change the class that manages the Content step.
+      if (isset($operations['content'])) {
+        $operations['content']['form'] = PanelizerWizardContentForm::class;
+      }
+    }
+
+    return $operations;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteName() {
+    return 'panelizer.wizard.step';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $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'] = [];
+    if (empty($cached_values['page_variant'])) {
+      $variant_plugin_id = 'panels_variant';
+      $panels_variant = \Drupal::entityManager()
+        ->getStorage('page_variant')
+        ->create([
+          'variant' => $variant_plugin_id,
+          //'page' => $page->id(),
+          'id' => $cached_values['id'] . '__variant',
+        ]);
+      $cached_values['page_variant'] = $panels_variant;
+    }
+    $form_state->setTemporaryValue('wizard', $cached_values);
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function finish(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+
+    // Save the wizard data into the Panelizer settings for this display mode.
+    $panelizer = \Drupal::service('panelizer');
+    $panelizer_entity_manager = \Drupal::service('plugin.manager.panelizer_entity');
+    $panels_display_manager = \Drupal::service('panels.display_manager');
+    list($entity_type, $bundle, $display_id) = explode('__', $cached_values['id']);
+    $display = $panelizer->getEntityViewDisplay($entity_type, $bundle, $display_id);
+    $settings = $panelizer->getPanelizerSettings($entity_type, $bundle, $display_id);
+    $settings['enable'] = TRUE;
+    /** @var \Drupal\panelizer\Plugin\PanelizerEntityInterface $panelizer_entity_plugin */
+    $panelizer_entity_plugin = $panelizer_entity_manager->createInstance($display->getTargetEntityTypeId(), []);
+    $displays = $display->getThirdPartySetting('panelizer', 'displays', []);
+    $displays[$display_id] = $panels_display_manager->exportDisplay($cached_values['plugin']);
+    $display->setThirdPartySetting('panelizer', 'displays', $displays);
+    $panelizer->setPanelizerSettings($entity_type, $bundle, $display_id, $settings, $display);
+
+    parent::finish($form, $form_state);
+  }
+
+  /**
+   * Wraps the context mapper.
+   *
+   * @return \Drupal\page_manager\ContextMapperInterface
+   */
+  protected function getContextMapper() {
+    return \Drupal::service('page_manager.context_mapper');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getContexts($cached_values) {
+    if (!isset($cached_values['contexts'])) {
+      $cached_values['contexts'] = [];
+    }
+
+    return $this->getContextMapper()->getContextValues($cached_values['contexts']);
+  }
+
+  /**
+   * Submission callback for the variant plugin steps.
+   */
+  public function submitVariantStep(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    /** @var \Drupal\page_manager\PageVariantInterface $page_variant */
+    $page_variant = $cached_values['page_variant'];
+    /** @var \Drupal\Core\Display\VariantInterface $plugin */
+    $plugin = $cached_values['plugin'];
+
+    // Make sure the variant plugin on the page variant gets the configuration
+    // from the 'plugin' which should have been setup by the variant's steps.
+    if (!empty($plugin) && !empty($page_variant)) {
+      $page_variant->getVariantPlugin()->setConfiguration($plugin->getConfiguration());
+    }
+  }
+
+}
