diff --git a/.travis.yml b/.travis.yml
index 294823f742ccf1a95bcf32a4ad6081cb7ddb754f..26918036c9a7cf2d98c1cda4a211ef32b4c9767d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,6 +15,10 @@ env:
 
 matrix:
   allow_failures:
+    # There are weird segmentation faults in the UI with PHP 5.6.5 (which travis
+    # uses). As everything works with other versions and 5.6.18 we allow 5.6 to
+    # fail for now. See https://www.drupal.org/node/2659028.
+    - php: 5.6
     # We cannot use hhvm-nightly since that does not work in Travis CI's old
     # Ubuntu 12.04.
     #- php: hhvm
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 93f5e611f63d5e5db60834a372e3762b12de9666..a99a187f760b30d4a6e5a2b358509bca839a4686 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -3,9 +3,6 @@
   <description>Default PHP CodeSniffer configuration for Rules.</description>
   <file>.</file>
 
-  <!-- Exclude generated code files. -->
-  <exclude-pattern>src/ProxyClass/ParamConverter/RulesTempConverter.php</exclude-pattern>
-
   <arg name="extensions" value="inc,install,module,php,profile,test,theme,yml"/>
 
   <rule ref="Drupal.NamingConventions.ValidVariableName.LowerCamelName">
diff --git a/rules.routing.yml b/rules.routing.yml
index 069a5e1664d5069707bae67cec76302098912f46..5fd854c09447ceacd76ad0943b295d7602cd7c4f 100755
--- a/rules.routing.yml
+++ b/rules.routing.yml
@@ -25,6 +25,8 @@ entity.rules_reaction_rule.add_form:
   defaults:
     _entity_form: 'rules_reaction_rule.add'
     _title: 'Add reaction rule'
+  options:
+    _rules_ui: rules.reaction_rules
   requirements:
     _permission: 'administer rules+administer rules reactions'
 
@@ -38,6 +40,7 @@ entity.rules_reaction_rule.edit_form:
       rules_reaction_rule:
         tempstore: TRUE
         type: entity:rules_reaction_rule
+    _rules_ui: rules.reaction_rules
   requirements:
     _permission: 'administer rules+administer rules reactions'
 
@@ -45,14 +48,8 @@ entity.rules_reaction_rule.delete_form:
   path: '/admin/config/workflow/rules/reactions/delete/{rules_reaction_rule}'
   defaults:
     _entity_form: 'rules_reaction_rule.delete'
-  requirements:
-    _permission: 'administer rules+administer rules reactions'
-
-entity.rules_reaction_rule.break_lock_form:
-  path: '/admin/config/workflow/rules/reactions/break-lock/{rules_reaction_rule}'
-  defaults:
-    _entity_form: 'rules_reaction_rule.break_lock'
-    _title: 'Break lock'
+  options:
+    _rules_ui: rules.reaction_rules
   requirements:
     _permission: 'administer rules+administer rules reactions'
 
diff --git a/rules.rules_ui.yml b/rules.rules_ui.yml
index eb16c4a9f46ad227adcac98286ac83617d173d55..ffcdfb509e88f01a7e7709289b48fb2b690e1bc3 100644
--- a/rules.rules_ui.yml
+++ b/rules.rules_ui.yml
@@ -1,3 +1,6 @@
 rules.reaction_rules:
   label: 'Manage reaction rules.'
   base_route: entity.rules_reaction_rule.edit_form
+  component_type_label: rule
+  settings:
+    config_parameter: rules_reaction_rule
diff --git a/rules.services.yml b/rules.services.yml
index a57b4b3b9697e02ad5ae8b848ede07ec51f46f17..053638c9034c783986d1e2dcb6ef6b6e20a2a17d 100644
--- a/rules.services.yml
+++ b/rules.services.yml
@@ -34,8 +34,13 @@ services:
     class: Drupal\rules\EventSubscriber\RedirectEventSubscriber
     tags:
       - { name: 'event_subscriber' }
+  rules.ui_route_enhancer:
+    class: Drupal\rules\Routing\RulesUiRouteEnhancer
+    arguments: ['@plugin.manager.rules_ui']
+    tags:
+      - { name: route_enhancer }
   rules.ui_route_subscriber:
-    class: Drupal\rules\Routing\UiRouteSubscriber
+    class: Drupal\rules\Routing\RulesUiRouteSubscriber
     arguments: ['@plugin.manager.rules_ui']
     tags:
       - { name: 'event_subscriber' }
@@ -44,9 +49,3 @@ services:
   typed_data.placeholder_resolver:
     class: Drupal\rules\TypedData\PlaceholderResolver
     arguments: ['@typed_data.data_fetcher', '@plugin.manager.typed_data_filter']
-  paramconverter.rules:
-    class: Drupal\rules\ParamConverter\RulesTempConverter
-    arguments: ['@entity.manager', '@user.shared_tempstore']
-    tags:
-      - { name: paramconverter, priority: 10 }
-    lazy: true
diff --git a/src/Core/RulesUiConfigHandler.php b/src/Core/RulesUiConfigHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d50f2661892d712a24cb9ac9471eb97c4e215b4
--- /dev/null
+++ b/src/Core/RulesUiConfigHandler.php
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\rules\Core\RulesUiDefaultHandler.
+ */
+
+namespace Drupal\rules\Core;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
+use Drupal\rules\Engine\RulesComponent;
+use Drupal\rules\Form\TempStoreTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * The default handler for RulesUi plugins that store to config.
+ *
+ * It follows a list of supported settings. Note that settings that are not
+ * marked as optional are required.
+ * - config_parameter: The name of the routing parameter holding the config.
+ *
+ * @see RulesUiDefinition::settings
+ */
+class RulesUiConfigHandler extends PluginBase implements RulesUiHandlerInterface, ContainerFactoryPluginInterface {
+
+  use TempStoreTrait;
+
+  /**
+   * The rules UI (plugin) definition.
+   *
+   * @var \Drupal\rules\Core\RulesUiDefinition
+   */
+  protected $pluginDefinition;
+
+  /**
+   * The current route match.
+   *
+   * @var \Drupal\Core\Routing\RouteMatchInterface
+   */
+  protected $currentRouteMatch;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static($configuration, $plugin_id, $plugin_definition, $container->get('current_route_match'));
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The current route match.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->currentRouteMatch = $route_match;
+  }
+
+  /**
+   * Gets the edited config object.
+   *
+   * @return \Drupal\Core\Config\Entity\ConfigEntityBase|\Drupal\Core\Config\Config
+   *   The config entity or config object.
+   */
+  public function getConfig() {
+    $config = $this->fetchFromTempStore();
+    if (!$config) {
+      $config = $this->currentRouteMatch->getParameter($this->pluginDefinition->settings['config_parameter']);
+    }
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getComponentLabel() {
+    if (isset($this->pluginDefinition->component_label)) {
+      return $this->pluginDefinition->component_label;
+    }
+    elseif ($this->getConfig() instanceof EntityInterface) {
+      return $this->getConfig()->label();
+    }
+    else {
+      return $this->pluginDefinition->component_type_label;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getComponent() {
+    return $this->getConfig()->getComponent();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function updateComponent(RulesComponent $component) {
+    $config = $this->getConfig();
+    $config->updateFromComponent($component);
+    $this->storeToTempStore($config);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBaseRouteUrl() {
+    // See Url::fromRouteMatch()
+    return Url::fromRoute($this->pluginDefinition->base_route, $this->currentRouteMatch->getRawParameters()->all());
+  }
+
+}
diff --git a/src/Core/RulesUiDefaultHandler.php b/src/Core/RulesUiDefaultHandler.php
deleted file mode 100644
index 5b170ff36b6894173177bcbe3b7ca322d0aa3be5..0000000000000000000000000000000000000000
--- a/src/Core/RulesUiDefaultHandler.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\rules\Core\RulesUiDefaultHandler.
- */
-
-namespace Drupal\rules\Core;
-
-use Drupal\Core\Plugin\PluginBase;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * The default handler for RulesUi plugins.
- *
- * @todo: Complete implementation.
- */
-class RulesUiDefaultHandler extends PluginBase implements RulesUiHandlerInterface {
-
-  /**
-   * The rules UI (plugin) definition.
-   *
-   * @var \Drupal\rules\Core\RulesUiDefinition
-   */
-  protected $pluginDefinition;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function registerRoutes(RouteCollection $collection) {
-    $base_route = $collection->get($this->pluginDefinition->base_route);
-
-    $options = [
-      'parameters' => ($base_route->getOption('parameters') ?: []),
-      '_admin_route' => $base_route->getOption('_admin_route') ?: FALSE,
-    ];
-    $requirements = [
-      '_permission' => $this->pluginDefinition->permissions ?: $base_route->getRequirement('_permission'),
-    ];
-
-    $route = (new Route($base_route->getPath() . '/add/{expression_id}'))
-      ->addDefaults([
-        '_form' => '\Drupal\rules\Form\AddExpressionForm',
-        '_title_callback' => '\Drupal\rules\Form\AddExpressionForm::getTitle',
-      ])
-      ->addOptions($options)
-      ->addRequirements($requirements);
-    $collection->add($this->pluginDefinition->base_route . '.expression.add', $route);
-
-    $route = (new Route($base_route->getPath() . '/edit/{uuid}'))
-      ->addDefaults([
-        '_form' => '\Drupal\rules\Form\EditExpressionForm',
-        '_title_callback' => '\Drupal\rules\Form\EditExpressionForm::getTitle',
-      ])
-      ->addOptions($options)
-      ->addRequirements($requirements);
-    $collection->add($this->pluginDefinition->base_route . '.expression.edit', $route);
-
-    $route = (new Route($base_route->getPath() . '/delete/{uuid}'))
-      ->addDefaults([
-        '_form' => '\Drupal\rules\Form\DeleteExpressionForm',
-        '_title' => 'Delete expression',
-      ])
-      ->addOptions($options)
-      ->addRequirements($requirements);
-    $collection->add($this->pluginDefinition->base_route . '.expression.delete', $route);
-  }
-
-}
diff --git a/src/Core/RulesUiDefinition.php b/src/Core/RulesUiDefinition.php
index 90b1aa5a148e80c33813744bfd5c8549ce4a62a2..8c339b6e9ebcf79ca5c90a0cf6ab4c605bea78ab 100644
--- a/src/Core/RulesUiDefinition.php
+++ b/src/Core/RulesUiDefinition.php
@@ -54,7 +54,16 @@ class RulesUiDefinition implements PluginDefinitionInterface {
    *
    * @var string
    */
-  public $class = RulesUiDefaultHandler::class;
+  public $class = RulesUiConfigHandler::class;
+
+  /**
+   * Array of handler-specific settings.
+   *
+   * Check the documentation of the ui handler for further details.
+   *
+   * @var array
+   */
+  public $settings = [];
 
   /**
    * The plugin provider; e.g., the module.
@@ -81,6 +90,28 @@ class RulesUiDefinition implements PluginDefinitionInterface {
    */
   public $permissions;
 
+  /**
+   * The label used for referring to the component (optional).
+   *
+   * If omitted, a handler-specific fallback logic is applied. For example,
+   * the RulesUiConfigHandler assumes a config entity and uses its label()
+   * method.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var string|null
+   */
+  public $component_label;
+
+  /**
+   * The label used for referring to the component type.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var string|null
+   */
+  public $component_type_label = 'component';
+
   /**
    * {@inheritdoc}
    */
diff --git a/src/Core/RulesUiHandlerInterface.php b/src/Core/RulesUiHandlerInterface.php
index 1b04e096071977f3560f8a4a7fb25d240d44422a..37f44d6aaffab4f166df473621754b61b4471238 100644
--- a/src/Core/RulesUiHandlerInterface.php
+++ b/src/Core/RulesUiHandlerInterface.php
@@ -7,24 +7,109 @@
 
 namespace Drupal\rules\Core;
 
-use Symfony\Component\Routing\RouteCollection;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\rules\Engine\RulesComponent;
 
 /**
  * Interface for Rules UI handlers.
  *
  * Rules UI handlers define where RulesUI instances are embedded and are
  * responsible for generating the appropriate routes.
- *
- * @todo: Implement.
  */
-interface RulesUiHandlerInterface {
+interface RulesUiHandlerInterface extends PluginInspectionInterface {
+
+  /**
+   * {@inheritdoc}
+   *
+   * @return \Drupal\rules\Core\RulesUiDefinition
+   *   The rules_ui plugin definition.
+   */
+  public function getPluginDefinition();
+
+  /**
+   * Gets the human-readable label of the component.
+   *
+   * The human-readable label used when referring to the whole component. This
+   * can be a fixed string, or the label of a config entity.
+   *
+   * @return string
+   *   The label.
+   */
+  public function getComponentLabel();
+
+  /**
+   * Gets the currently edited component.
+   *
+   * @return \Drupal\rules\Engine\RulesComponent
+   *   The edited component.
+   */
+  public function getComponent();
+
+  /**
+   * Updates the edited component.
+   *
+   * @param \Drupal\rules\Engine\RulesComponent $component
+   *   The updated, edited component.
+   */
+  public function updateComponent(RulesComponent $component);
+
+  /**
+   * Clears any temporary storage.
+   *
+   * Note that after clearing the temporary storage any unsaved changes are
+   * lost.
+   */
+  public function clearTemporaryStorage();
+
+  /**
+   * Returns the URL of the base route, based on the current URL.
+   *
+   * @return \Drupal\Core\Url
+   *   The url of the base route.
+   */
+  public function getBaseRouteUrl();
+
+  /**
+   * Determines if the component is locked for the current user.
+   *
+   * @return bool
+   *   TRUE if locked, FALSE otherwise.
+   */
+  public function isLocked();
+
+  /**
+   * Checks if the rule has been modified and is present in the storage.
+   *
+   * @return bool
+   *   TRUE if the rule has been modified, FALSE otherwise.
+   */
+  public function isEdited();
+
+  /**
+   * Provides information which user at which time locked the rule for editing.
+   *
+   * @return object
+   *   StdClass object as provided by \Drupal\user\SharedTempStore.
+   */
+  public function getLockMetaData();
+
+  /**
+   * Renders a message if the rule component is locked/modified.
+   *
+   * @return array
+   *   The render array, showing the message when applicable.
+   */
+  public function addLockInformation();
 
   /**
-   * Registers the routes as need for the UI.
+   * Validation callback that prevents editing locked rule components.
    *
-   * @param \Symfony\Component\Routing\RouteCollection $collection
-   *   The route collection to which to add the routes.
+   * @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 registerRoutes(RouteCollection $collection);
+  public function validateLock(array &$form, FormStateInterface $form_state);
 
 }
diff --git a/src/Core/RulesUiManagerInterface.php b/src/Core/RulesUiManagerInterface.php
index ad3b12c96887db58b10e532e9dcedf14d41370ec..53e590c27544828a728fa2002df0b8870425bda7 100644
--- a/src/Core/RulesUiManagerInterface.php
+++ b/src/Core/RulesUiManagerInterface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains Drupal\rules\Core\RulesUiManagerInterface.
+ * Contains \Drupal\rules\Core\RulesUiManagerInterface.
  */
 
 namespace Drupal\rules\Core;
diff --git a/src/Engine/ActionExpressionContainer.php b/src/Engine/ActionExpressionContainer.php
index 08493b3adec8e14fc4b04f0f5d10e4f74bdf26db..c2677d9005b2c81c591d15232675873cbb52fbc3 100644
--- a/src/Engine/ActionExpressionContainer.php
+++ b/src/Engine/ActionExpressionContainer.php
@@ -114,6 +114,16 @@ abstract class ActionExpressionContainer extends ExpressionBase implements Actio
     return new \ArrayIterator($this->actions);
   }
 
+  /**
+   * PHP magic __clone function.
+   */
+  public function __clone() {
+    // Implement a deep clone.
+    foreach ($this->actions as &$action) {
+      $action = clone $action;
+    }
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/src/Engine/ConditionExpressionContainer.php b/src/Engine/ConditionExpressionContainer.php
index e53a0fe4dfe6b53d8b625f449220e8a109df27fb..104521873868d23862de06ef94f8d527fda969ce 100644
--- a/src/Engine/ConditionExpressionContainer.php
+++ b/src/Engine/ConditionExpressionContainer.php
@@ -142,6 +142,16 @@ abstract class ConditionExpressionContainer extends ExpressionBase implements Co
     return new \ArrayIterator($this->conditions);
   }
 
+  /**
+   * PHP magic __clone function.
+   */
+  public function __clone() {
+    // Implement a deep clone.
+    foreach ($this->conditions as &$condition) {
+      $condition = clone $condition;
+    }
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/src/Engine/ExpressionBase.php b/src/Engine/ExpressionBase.php
index bac649c66c35209c99c089961075cb4de44b404c..a12cc10ac30c85be5c7cd58763551e97011e3a34 100644
--- a/src/Engine/ExpressionBase.php
+++ b/src/Engine/ExpressionBase.php
@@ -119,6 +119,7 @@ abstract class ExpressionBase extends PluginBase implements ExpressionInterface
    */
   public function getRoot() {
     if (isset($this->root)) {
+      // @todo: This seems to be the parent, not root.
       return $this->root->getRoot();
     }
     return $this;
diff --git a/src/Engine/ExpressionInterface.php b/src/Engine/ExpressionInterface.php
index ebc32b97ebdae5aa2de30de0f1772f65ec6cdc3b..98cde6bd777b7da728091df9ace3dc95a8b89c4c 100644
--- a/src/Engine/ExpressionInterface.php
+++ b/src/Engine/ExpressionInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\rules\Engine;
 
 use Drupal\Component\Plugin\ConfigurablePluginInterface;
+use Drupal\Component\Plugin\PluginInspectionInterface;
 use Drupal\Core\Executable\ExecutableInterface;
 
 /**
@@ -15,7 +16,7 @@ use Drupal\Core\Executable\ExecutableInterface;
  *
  * @see \Drupal\rules\Engine\ExpressionManager
  */
-interface ExpressionInterface extends ExecutableInterface, ConfigurablePluginInterface {
+interface ExpressionInterface extends ExecutableInterface, ConfigurablePluginInterface, PluginInspectionInterface {
 
   /**
    * Execute the expression with a given Rules state.
diff --git a/src/Engine/ExpressionManagerInterface.php b/src/Engine/ExpressionManagerInterface.php
index 7b7680bba83df171dc091d6669845ddaf708854e..cc7fb7ae320fa4d0715d68b34e06da75936f193d 100644
--- a/src/Engine/ExpressionManagerInterface.php
+++ b/src/Engine/ExpressionManagerInterface.php
@@ -14,6 +14,14 @@ use Drupal\Component\Plugin\PluginManagerInterface;
  */
 interface ExpressionManagerInterface extends PluginManagerInterface {
 
+  /**
+   * {@inheritdoc}
+   *
+   * @return \Drupal\rules\Engine\ExpressionInterface
+   *   A fully configured plugin instance.
+   */
+  public function createInstance($plugin_id, array $configuration = []);
+
   /**
    * Creates a new rule.
    *
diff --git a/src/Engine/RulesComponent.php b/src/Engine/RulesComponent.php
index 2788ab5990cc74819262edd9f3bed487c87954d2..b08bd0a94985f3aef3190c6c19cd964e02ff6839 100644
--- a/src/Engine/RulesComponent.php
+++ b/src/Engine/RulesComponent.php
@@ -7,9 +7,7 @@
 
 namespace Drupal\rules\Engine;
 
-use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\rules\Context\ContextDefinitionInterface;
-use Drupal\rules\Entity\ReactionRuleConfig;
 
 /**
  * Handles executable Rules components.
@@ -113,19 +111,16 @@ class RulesComponent {
   }
 
   /**
-   * Adds the configured context definitions from the config entity.
+   * Adds the available event context for the given events.
    *
-   * Example: for a reaction rule config all context definitions of the event
-   * will be added.
-   *
-   * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $rules_config
-   *   The config entity.
+   * @param string[] $event_names
+   *   The event names; e.g., as configured for a reaction rule.
    *
    * @return $this
    */
-  public function addContextDefinitionsFrom(ConfigEntityInterface $rules_config) {
-    if ($rules_config instanceof ReactionRuleConfig) {
-      $event_name = $rules_config->getEvent();
+  public function addContextDefinitionsForEvents(array $event_names) {
+    foreach ($event_names as $event_name) {
+      // @todo: Correctly handle multiple events to intersect available context.
       // @todo Use setter injection for the service.
       $event_definition = \Drupal::service('plugin.manager.rules_event')->getDefinition($event_name);
       foreach ($event_definition['context'] as $context_name => $context_definition) {
@@ -250,4 +245,13 @@ class RulesComponent {
     return ExecutionMetadataState::create($data_definitions);
   }
 
+  /**
+   * PHP magic __clone function.
+   */
+  public function __clone() {
+    // Implement a deep clone.
+    $this->state = clone $this->state;
+    $this->expression = clone $this->expression;
+  }
+
 }
diff --git a/src/Entity/ReactionRuleConfig.php b/src/Entity/ReactionRuleConfig.php
index d86c8a0a77b11b9793da4010185be1948cf0973a..b811ad79e480639caf61a71f16d2475d6ce86dc7 100644
--- a/src/Entity/ReactionRuleConfig.php
+++ b/src/Entity/ReactionRuleConfig.php
@@ -9,6 +9,7 @@ namespace Drupal\rules\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\rules\Engine\ExpressionInterface;
+use Drupal\rules\Engine\RulesComponent;
 
 /**
  * Reaction rule configuration entity to persistently store configuration.
@@ -22,8 +23,7 @@ use Drupal\rules\Engine\ExpressionInterface;
  *     "form" = {
  *        "add" = "\Drupal\rules\Form\ReactionRuleAddForm",
  *        "edit" = "\Drupal\rules\Form\ReactionRuleEditForm",
- *        "delete" = "\Drupal\Core\Entity\EntityDeleteForm",
- *        "break_lock" = "\Drupal\rules\Form\BreakLockForm"
+ *        "delete" = "\Drupal\Core\Entity\EntityDeleteForm"
  *      }
  *   },
  *   admin_permission = "administer rules",
@@ -48,7 +48,7 @@ use Drupal\rules\Engine\ExpressionInterface;
  *     "collection" = "/admin/config/workflow/rules",
  *     "edit-form" = "/admin/config/workflow/rules/reactions/edit/{rules_reaction_rule}",
  *     "delete-form" = "/admin/config/workflow/rules/reactions/delete/{rules_reaction_rule}",
- *     "break-lock-form" = "/admin/config/workflow/rules/reactions/break-lock/{rules_reaction_rule}"
+ *     "break-lock-form" = "/admin/config/workflow/rules/reactions/edit/break-lock/{rules_reaction_rule}"
  *   }
  * )
  */
@@ -142,7 +142,6 @@ class ReactionRuleConfig extends ConfigEntityBase {
     return $this;
   }
 
-
   /**
    * Gets a Rules expression instance for this Reaction rule.
    *
@@ -159,6 +158,36 @@ class ReactionRuleConfig extends ConfigEntityBase {
     return $this->expression;
   }
 
+  /**
+   * Gets the Rules component that is invoked when the events are dispatched.
+   *
+   * The returned component has the definitions of the available event context
+   * set.
+   *
+   * @return \Drupal\rules\Engine\RulesComponent
+   *   The Rules component.
+   */
+  public function getComponent() {
+    $component = RulesComponent::create($this->getExpression());
+    $component->addContextDefinitionsForEvents([$this->getEvent()]);
+    return $component;
+  }
+
+  /**
+   * Updates the configuration based upon the given component.
+   *
+   * @param \Drupal\rules\Engine\RulesComponent $component
+   *   The component containing the configuration to set.
+   *
+   * @return $this
+   */
+  public function updateFromComponent(RulesComponent $component) {
+    // Note that the available context definitions stem from the configured
+    // events, which are handled separately.
+    $this->setExpression($component->getExpression());
+    return $this;
+  }
+
   /**
    * Returns the Rules expression manager.
    *
diff --git a/src/Entity/RulesComponentConfig.php b/src/Entity/RulesComponentConfig.php
index 8057d12bc22685641305ed35c7fc988828a4dac0..f8a8255a069e28189769da3ca2d1f458d4225393 100644
--- a/src/Entity/RulesComponentConfig.php
+++ b/src/Entity/RulesComponentConfig.php
@@ -10,6 +10,7 @@ namespace Drupal\rules\Entity;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\rules\Context\ContextDefinition;
 use Drupal\rules\Engine\ExpressionInterface;
+use Drupal\rules\Engine\RulesComponent;
 
 /**
  * Rules component configuration entity to persistently store configuration.
@@ -170,7 +171,7 @@ class RulesComponentConfig extends ConfigEntityBase {
    *   The component.
    */
   public function getComponent() {
-    $component = \Drupal\rules\Engine\RulesComponent::create($this->getExpression());
+    $component = RulesComponent::create($this->getExpression());
     foreach ($this->context_definitions as $name => $definition) {
       $component->addContextDefinition($name, ContextDefinition::createFromArray($definition));
     }
@@ -188,7 +189,7 @@ class RulesComponentConfig extends ConfigEntityBase {
    *
    * @return $this
    */
-  public function setComponent(\Drupal\rules\Engine\RulesComponent $component) {
+  public function setComponent(RulesComponent $component) {
     $this->setExpression($component->getExpression());
     $this->setContextDefinitions($component->getContextDefinitions());
     return $this;
diff --git a/src/Form/AddExpressionForm.php b/src/Form/AddExpressionForm.php
index d6b43c6a21fb2beb22bccfa2f58d77f268cb4d38..112233c5c65c54c93c6d0b47052b74b6c4ad3f82 100644
--- a/src/Form/AddExpressionForm.php
+++ b/src/Form/AddExpressionForm.php
@@ -7,21 +7,17 @@
 
 namespace Drupal\rules\Form;
 
-use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\rules\Core\RulesUiHandlerInterface;
+use Drupal\rules\Engine\ExpressionContainerInterface;
 use Drupal\rules\Engine\ExpressionManagerInterface;
 use Drupal\rules\Engine\RulesComponent;
-use Drupal\rules\Entity\ReactionRuleConfig;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * UI form to add an expression like a condition or action to a rule.
  */
-class AddExpressionForm extends FormBase {
-
-  use TempStoreTrait {
-    validateForm as lockValidateForm;
-  }
+class AddExpressionForm extends EditExpressionForm {
 
   /**
    * The Rules expression manager to get expression plugins.
@@ -30,13 +26,6 @@ class AddExpressionForm extends FormBase {
    */
   protected $expressionManager;
 
-  /**
-   * The reaction rule config the expression is added to.
-   *
-   * @var \Drupal\rules\Entity\ReactionRuleConfig
-   */
-  protected $ruleConfig;
-
   /**
    * The expression ID that is added, example: 'rules_action'.
    *
@@ -61,44 +50,41 @@ class AddExpressionForm extends FormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, ReactionRuleConfig $rules_reaction_rule = NULL, $expression_id = NULL) {
-    $this->ruleConfig = $rules_reaction_rule;
+  public function buildForm(array $form, FormStateInterface $form_state, RulesUiHandlerInterface $rules_ui_handler = NULL, $uuid = NULL, $expression_id = NULL) {
     $this->expressionId = $expression_id;
+    $this->uuid = $uuid;
+
+    // When initially adding the expression, we have to initialize the object
+    // and add the expression.
+    if (!$this->uuid) {
+      // Before we add our edited expression to the component's expression,
+      // we clone it such that we do not change the source component until
+      // the form has been successfully submitted.
+      $component = clone $rules_ui_handler->getComponent();
+      $this->uuid = $this->getEditedExpression($component)->getUuid();
+      $form_state->set('component', $component);
+      $form_state->set('uuid', $this->uuid);
+    }
 
-    $expression = $this->expressionManager->createInstance($expression_id);
-    $form_handler = $expression->getFormHandler();
-    $form = $form_handler->form($form, $form_state);
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'rules_expression_add';
+    return parent::buildForm($form, $form_state, $rules_ui_handler, $this->uuid);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    $this->lockValidateForm($form, $form_state);
-
-    $expression = $this->expressionManager->createInstance($this->expressionId);
-    $form_handler = $expression->getFormHandler();
-    $form_handler->validateForm($form, $form_state);
-
-    $validation_config = clone $this->ruleConfig;
-    $rule_expression = $validation_config->getExpression();
-    $rule_expression->addExpressionObject($expression);
-
-    $all_violations = RulesComponent::create($rule_expression)
-      ->addContextDefinitionsFrom($validation_config)
-      ->checkIntegrity();
-    $local_violations = $all_violations->getFor($expression->getUuid());
-
-    foreach ($local_violations as $violation) {
-      $form_state->setError($form, $violation->getMessage());
+  protected function getEditedExpression(RulesComponent $component) {
+    $component_expression = $component->getExpression();
+    if (!$component_expression instanceof ExpressionContainerInterface) {
+      throw new \LogicException('Cannot add expression to expression of type ' . $component_expression->getPluginId());
+    }
+    if ($this->uuid && $expression = $component_expression->getExpression($this->uuid)) {
+      return $expression;
+    }
+    else {
+      $expression = $this->expressionManager->createInstance($this->expressionId);
+      $rule_expression = $component->getExpression();
+      $rule_expression->addExpressionObject($expression);
+      return $expression;
     }
   }
 
@@ -106,28 +92,16 @@ class AddExpressionForm extends FormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $expression = $this->expressionManager->createInstance($this->expressionId);
-    $form_handler = $expression->getFormHandler();
-    $form_handler->submitForm($form, $form_state);
-
-    $rule_expression = $this->ruleConfig->getExpression();
-    $rule_expression->addExpressionObject($expression);
-    // Set the expression again so that the config is copied over to the
-    // config entity.
-    $this->ruleConfig->setExpression($rule_expression);
-
-    $this->saveToTempStore();
-
-    $form_state->setRedirect('entity.rules_reaction_rule.edit_form', [
-      'rules_reaction_rule' => $this->ruleConfig->id(),
-    ]);
+    parent::submitForm($form, $form_state);
+    $form_state->setRedirect('entity.rules_reaction_rule.edit_form', $this->getRouteMatch()->getRawParameters()->all());
   }
 
   /**
    * Provides the page title on the form.
    */
-  public function getTitle(ReactionRuleConfig $rules_reaction_rule, $expression_id) {
-    $expression = $this->expressionManager->createInstance($expression_id);
+  public function getTitle(RulesUiHandlerInterface $rules_ui_handler, $expression_id) {
+    $this->expressionId = $expression_id;
+    $expression = $this->expressionManager->createInstance($this->expressionId);
     return $this->t('Add @expression', ['@expression' => $expression->getLabel()]);
   }
 
diff --git a/src/Form/BreakLockForm.php b/src/Form/BreakLockForm.php
index 79db983c6f92a48c03194e3c7cfe5986bd41ccf2..559ec0e9e4d0020ade5daf61bf25977d87e1abe2 100644
--- a/src/Form/BreakLockForm.php
+++ b/src/Form/BreakLockForm.php
@@ -7,17 +7,17 @@
 
 namespace Drupal\rules\Form;
 
-use Drupal\Core\Entity\EntityConfirmFormBase;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\RendererInterface;
-use Drupal\user\SharedTempStoreFactory;
+use Drupal\rules\Core\RulesUiHandlerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Builds the form to break the lock of an edited rule.
  */
-class BreakLockForm extends EntityConfirmFormBase {
+class BreakLockForm extends ConfirmFormBase {
 
   /**
    * The entity type manager.
@@ -27,25 +27,24 @@ class BreakLockForm extends EntityConfirmFormBase {
   protected $entityTypeManager;
 
   /**
-   * The temporary storage factory.
+   * The rendering service.
    *
-   * @var \Drupal\user\SharedTempStoreFactory
+   * @var \Drupal\Core\Render\RendererInterface
    */
-  protected $tempStoreFactory;
+  protected $renderer;
 
   /**
-   * The rendering service.
+   * The RulesUI handler of the currently active UI.
    *
-   * @var \Drupal\Core\Render\RendererInterface
+   * @var \Drupal\rules\Core\RulesUiHandlerInterface
    */
-  protected $renderer;
+  protected $rulesUiHandler;
 
   /**
    * Constructor.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, SharedTempStoreFactory $temp_store_factory, RendererInterface $renderer) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer) {
     $this->entityTypeManager = $entity_type_manager;
-    $this->tempStoreFactory = $temp_store_factory;
     $this->renderer = $renderer;
   }
 
@@ -55,7 +54,6 @@ class BreakLockForm extends EntityConfirmFormBase {
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('entity_type.manager'),
-      $container->get('user.shared_tempstore'),
       $container->get('renderer')
     );
   }
@@ -71,15 +69,14 @@ class BreakLockForm extends EntityConfirmFormBase {
    * {@inheritdoc}
    */
   public function getQuestion() {
-    return $this->t('Do you want to break the lock on rule %name?', ['%name' => $this->entity->id()]);
+    return $this->t('Do you want to break the lock on %label?', ['%label' => $this->rulesUiHandler->getComponentLabel()]);
   }
 
   /**
    * {@inheritdoc}
    */
   public function getDescription() {
-    $store = $this->tempStoreFactory->get($this->entity->getEntityTypeId());
-    $locked = $store->getMetadata($this->entity->id());
+    $locked = $this->rulesUiHandler->getLockMetaData();
     $account = $this->entityTypeManager->getStorage('user')->load($locked->owner);
     $username = [
       '#theme' => 'username',
@@ -94,7 +91,7 @@ class BreakLockForm extends EntityConfirmFormBase {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->entity->urlInfo('edit-form');
+    return $this->rulesUiHandler->getBaseRouteUrl();
   }
 
   /**
@@ -107,10 +104,10 @@ class BreakLockForm extends EntityConfirmFormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state) {
-    $store = $this->tempStoreFactory->get($this->entity->getEntityTypeId());
-    if (!$store->getMetadata($this->entity->id())) {
-      $form['message']['#markup'] = $this->t('There is no lock on rule %name to break.', ['%name' => $this->entity->id()]);
+  public function buildForm(array $form, FormStateInterface $form_state, RulesUiHandlerInterface $rules_ui_handler = NULL) {
+    $this->rulesUiHandler = $rules_ui_handler;
+    if (!$rules_ui_handler->isLocked()) {
+      $form['message']['#markup'] = $this->t('There is no lock on %label to break.', ['%label' => $rules_ui_handler->getComponentLabel()]);
       return $form;
     }
     return parent::buildForm($form, $form_state);
@@ -120,10 +117,11 @@ class BreakLockForm extends EntityConfirmFormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $store = $this->tempStoreFactory->get($this->entity->getEntityTypeId());
-    $store->delete($this->entity->id());
-    $form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
-    drupal_set_message($this->t('The lock has been broken and you may now edit this rule.'));
+    $this->rulesUiHandler->clearTemporaryStorage();
+    $form_state->setRedirectUrl($this->rulesUiHandler->getBaseRouteUrl());
+    drupal_set_message($this->t('The lock has been broken and you may now edit this @component_type.', [
+      '@component_type' => $this->rulesUiHandler->getPluginDefinition()->component_type_label,
+    ]));
   }
 
 }
diff --git a/src/Form/DeleteExpressionForm.php b/src/Form/DeleteExpressionForm.php
index 53766b724269ad4c3a1ecf53cd699d800a6baf3d..7b02c49fb178d147ba704d71007f4db551575630 100644
--- a/src/Form/DeleteExpressionForm.php
+++ b/src/Form/DeleteExpressionForm.php
@@ -9,7 +9,7 @@ namespace Drupal\rules\Form;
 
 use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\rules\Entity\ReactionRuleConfig;
+use Drupal\rules\Core\RulesUiHandlerInterface;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
@@ -17,21 +17,19 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  */
 class DeleteExpressionForm extends ConfirmFormBase {
 
-  use TempStoreTrait;
-
   /**
-   * The reaction rule config the expression is deleted from.
+   * The UUID of the expression in the rule.
    *
-   * @var \Drupal\rules\Entity\ReactionRuleConfig
+   * @var string
    */
-  protected $ruleConfig;
+  protected $uuid;
 
   /**
-   * The UUID of the expression in the rule.
+   * The RulesUI handler of the currently active UI.
    *
-   * @var string
+   * @var \Drupal\rules\Core\RulesUiHandlerInterface
    */
-  protected $uuid;
+  protected $rulesUiHandler;
 
   /**
    * {@inheritdoc}
@@ -43,8 +41,8 @@ class DeleteExpressionForm extends ConfirmFormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, ReactionRuleConfig $rules_reaction_rule = NULL, $uuid = NULL) {
-    $this->ruleConfig = $rules_reaction_rule;
+  public function buildForm(array $form, FormStateInterface $form_state, RulesUiHandlerInterface $rules_ui_handler = NULL, $uuid = NULL) {
+    $this->rulesUiHandler = $rules_ui_handler;
     $this->uuid = $uuid;
     return parent::buildForm($form, $form_state);
   }
@@ -60,7 +58,7 @@ class DeleteExpressionForm extends ConfirmFormBase {
    * {@inheritdoc}
    */
   public function getQuestion() {
-    $rule_expression = $this->ruleConfig->getExpression();
+    $rule_expression = $this->rulesUiHandler->getComponent()->getExpression();
     $expression_inside = $rule_expression->getExpression($this->uuid);
     if (!$expression_inside) {
       throw new NotFoundHttpException();
@@ -68,7 +66,7 @@ class DeleteExpressionForm extends ConfirmFormBase {
 
     return $this->t('Are you sure you want to delete %title from %rule?', [
       '%title' => $expression_inside->getLabel(),
-      '%rule' => $this->ruleConfig->label(),
+      '%rule' => $this->rulesUiHandler->getComponentLabel(),
     ]);
   }
 
@@ -76,21 +74,16 @@ class DeleteExpressionForm extends ConfirmFormBase {
    * {@inheritdoc}
    */
   public function getCancelUrl() {
-    return $this->ruleConfig->urlInfo();
+    return $this->rulesUiHandler->getBaseRouteUrl();
   }
 
   /**
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $expression = $this->ruleConfig->getExpression();
-    $expression->deleteExpression($this->uuid);
-    // Set the expression again so that the config is copied over to the
-    // config entity.
-    $this->ruleConfig->setExpression($expression);
-
-    $this->saveToTempStore();
-
+    $component = $this->rulesUiHandler->getComponent();
+    $component->getExpression()->deleteExpression($this->uuid);
+    $this->rulesUiHandler->updateComponent($component);
     $form_state->setRedirectUrl($this->getCancelUrl());
   }
 
diff --git a/src/Form/EditExpressionForm.php b/src/Form/EditExpressionForm.php
index 6786e06776698fb6c3b56aac4ff425403252a75f..5f45a19684647d28bd7e6fc9fd78bbe49421cebf 100644
--- a/src/Form/EditExpressionForm.php
+++ b/src/Form/EditExpressionForm.php
@@ -9,8 +9,8 @@ namespace Drupal\rules\Form;
 
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\rules\Core\RulesUiHandlerInterface;
 use Drupal\rules\Engine\RulesComponent;
-use Drupal\rules\Entity\ReactionRuleConfig;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
@@ -18,37 +18,60 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  */
 class EditExpressionForm extends FormBase {
 
-  use TempStoreTrait {
-    validateForm as lockValidateForm;
-  }
+  /**
+   * The edited component.
+   *
+   * @var \Drupal\rules\Engine\RulesComponent
+   */
+  protected $component;
 
   /**
-   * The reaction rule config the expression is edited on.
+   * The RulesUI handler of the currently active UI.
    *
-   * @var \Drupal\rules\Entity\ReactionRuleConfig
+   * @var \Drupal\rules\Core\RulesUiHandlerInterface
    */
-  protected $ruleConfig;
+  protected $rulesUiHandler;
 
   /**
-   * The UUID of the expression in the rule.
+   * The UUID of the edited expression in the rule.
    *
    * @var string
    */
   protected $uuid;
 
+  /**
+   * Gets the currently edited expression from the given component.
+   *
+   * @param \Drupal\rules\Engine\RulesComponent $component
+   *   The component from which to get the expression.
+   *
+   * @return \Drupal\rules\Engine\ExpressionInterface|null
+   *   The expression object.
+   */
+  protected function getEditedExpression(RulesComponent $component) {
+    $rule_expression = $component->getExpression();
+    return $rule_expression->getExpression($this->uuid);
+  }
+
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, ReactionRuleConfig $rules_reaction_rule = NULL, $uuid = NULL) {
-    $this->ruleConfig = $rules_reaction_rule;
-    $this->uuid = $uuid;
+  public function buildForm(array $form, FormStateInterface $form_state, RulesUiHandlerInterface $rules_ui_handler = NULL, $uuid = NULL) {
+    $this->rulesUiHandler = $rules_ui_handler;
+    $this->component = is_object($form_state->get('component')) ? $form_state->get('component') : $this->rulesUiHandler->getComponent();
+    $this->uuid = $form_state->get('uuid') ?: $uuid;
+
+    // During form rebuilds, keep track of changes using form state.
+    $form_state->set('rules_ui_handler', $this->rulesUiHandler);
+    $form_state->set('component', $this->component);
+    $form_state->set('uuid', $this->uuid);
+
+    $expression = $this->getEditedExpression($this->component, $form_state);
 
-    $rule_expression = $rules_reaction_rule->getExpression();
-    $expression_inside = $rule_expression->getExpression($uuid);
-    if (!$expression_inside) {
+    if (!$expression) {
       throw new NotFoundHttpException();
     }
-    $form_handler = $expression_inside->getFormHandler();
+    $form_handler = $expression->getFormHandler();
     $form = $form_handler->form($form, $form_state);
     return $form;
   }
@@ -61,26 +84,47 @@ class EditExpressionForm extends FormBase {
   }
 
   /**
-   * {@inheritdoc}
+   * Builds an updated component object based upon the submitted form values.
+   *
+   * @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 \Drupal\rules\Engine\RulesComponent
+   *   The updated component.
    */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    $this->lockValidateForm($form, $form_state);
-
-    // In order to validdate the whole rule we need to invoke the submission
-    // handler of the expression form. That way the expression is changed and we
-    // can validate the change for integrity afterwards.
-    $validation_config = clone $this->ruleConfig;
-    $rule_expression = $validation_config->getExpression();
-    $expression = $rule_expression->getExpression($this->uuid);
+  protected function buildComponent(array $form, FormStateInterface $form_state) {
+    $component = clone $this->component;
+
+    // In order to update the whole component we need to invoke the submission
+    // handler of the expression form. That way the expression gets changed
+    // accordingly.
+    $expression = $this->getEditedExpression($component);
     $form_handler = $expression->getFormHandler();
     $form_handler->submitForm($form, $form_state);
+    return $component;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    // Ensure the object properties are initialized, see
+    // https://www.drupal.org/node/2669032.
+    $this->rulesUiHandler = $form_state->get('rules_ui_handler');
+    $this->component = is_object($form_state->get('component')) ? $form_state->get('component') : $this->rulesUiHandler->getComponent();
+    $this->uuid = $form_state->get('uuid');
 
-    $all_violations = RulesComponent::create($rule_expression)
-      ->addContextDefinitionsFrom($validation_config)
-      ->checkIntegrity();
-    $local_violations = $all_violations->getFor($this->uuid);
+    $this->rulesUiHandler->validateLock($form, $form_state);
 
-    foreach ($local_violations as $violation) {
+    // @todo: This ignores ExpressionFormInterface::validateForm().
+
+    $component = $this->buildComponent($form, $form_state);
+    $violations = $component->checkIntegrity();
+
+    // Only show the violations caused by the edited expression.
+    foreach ($violations->getFor($this->uuid) as $violation) {
       $form_state->setError($form, $violation->getMessage());
     }
   }
@@ -89,29 +133,18 @@ class EditExpressionForm extends FormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $rule_expression = $this->ruleConfig->getExpression();
-    $expression = $rule_expression->getExpression($this->uuid);
-    $form_handler = $expression->getFormHandler();
-    $form_handler->submitForm($form, $form_state);
-
-    // Set the expression again so that the config is copied over to the
-    // config entity.
-    $this->ruleConfig->setExpression($rule_expression);
-
-    $this->saveToTempStore();
-
-    $form_state->setRedirect('entity.rules_reaction_rule.edit_form', [
-      'rules_reaction_rule' => $this->ruleConfig->id(),
-    ]);
+    $this->component = $this->buildComponent($form, $form_state);
+    $this->rulesUiHandler->updateComponent($this->component);
+    $form_state->setRedirectUrl($this->rulesUiHandler->getBaseRouteUrl());
   }
 
   /**
    * Provides the page title on the form.
    */
-  public function getTitle(ReactionRuleConfig $rules_reaction_rule, $uuid) {
-    $rule_expression = $rules_reaction_rule->getExpression();
-    $expression_inside = $rule_expression->getExpression($uuid);
-    return $this->t('Edit @expression', ['@expression' => $expression_inside->getLabel()]);
+  public function getTitle(RulesUiHandlerInterface $rules_ui_handler, $uuid) {
+    $this->uuid = $uuid;
+    $expression = $this->getEditedExpression($rules_ui_handler->getComponent());
+    return $this->t('Edit @expression', ['@expression' => $expression->getLabel()]);
   }
 
 }
diff --git a/src/Form/Expression/ActionForm.php b/src/Form/Expression/ActionForm.php
index ca37bb357af2370b769b883a8c503f723f2a5675..acc170c87bfa76264d7b0d3d1bd11754e8787177 100644
--- a/src/Form/Expression/ActionForm.php
+++ b/src/Form/Expression/ActionForm.php
@@ -47,14 +47,15 @@ class ActionForm implements ExpressionFormInterface {
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
-    $action_name = $form_state->get('action');
+    $action_id = $form_state->get('action_id');
     $configuration = $this->actionExpression->getConfiguration();
-    if (empty($action_name) && !empty($configuration['action_id'])) {
-      $action_name = $configuration['action_id'];
+    if (empty($action_id) && !empty($configuration['action_id'])) {
+      $action_id = $configuration['action_id'];
+      $form_state->set('action_id', $action_id);
     }
 
     // Step 1 of the multistep form.
-    if (!$action_name) {
+    if (!$action_id) {
       $action_definitions = $this->actionManager->getGroupedDefinitions();
       $options = [];
       foreach ($action_definitions as $group => $definitions) {
@@ -63,7 +64,7 @@ class ActionForm implements ExpressionFormInterface {
         }
       }
 
-      $form['action'] = [
+      $form['action_id'] = [
         '#type' => 'select',
         '#title' => $this->t('Action'),
         '#options' => $options,
@@ -74,7 +75,7 @@ class ActionForm implements ExpressionFormInterface {
         '#value' => $this->t('Continue'),
         '#name' => 'continue',
         // Only validate the selected action in the first step.
-        '#limit_validation_errors' => [['action']],
+        '#limit_validation_errors' => [['action_id']],
         '#submit' => [static::class . '::submitFirstStep'],
       ];
 
@@ -82,15 +83,11 @@ class ActionForm implements ExpressionFormInterface {
     }
 
     // Step 2 of the form.
-    $action = $this->actionManager->createInstance($action_name);
+    $action = $this->actionManager->createInstance($action_id);
 
     $form['summary'] = [
       '#markup' => $action->summary(),
     ];
-    $form['action'] = [
-      '#type' => 'value',
-      '#value' => $action_name,
-    ];
 
     $context_definitions = $action->getContextDefinitions();
 
@@ -113,7 +110,7 @@ class ActionForm implements ExpressionFormInterface {
    */
   public function validateForm(array $form, FormStateInterface $form_state) {
     // Only if there is an action selected already we can validate something.
-    if ($form_state->get('action')) {
+    if ($form_state->get('action_id')) {
       // Invoke the submission handler which will setup the expression being
       // edited in the form. That way the expression is ready for other
       // validation handlers.
@@ -125,7 +122,7 @@ class ActionForm implements ExpressionFormInterface {
    * Submit callback: save the selected action in the first step.
    */
   public static function submitFirstStep(array &$form, FormStateInterface $form_state) {
-    $form_state->set('action', $form_state->getValue('action'));
+    $form_state->set('action_id', $form_state->getValue('action_id'));
     $form_state->setRebuild();
   }
 
@@ -133,6 +130,10 @@ class ActionForm implements ExpressionFormInterface {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
+    // Nothing todo as long as the first step is not completed.
+    if (!$form_state->get('action_id')) {
+      return;
+    }
     $context_config = ContextConfig::create();
     foreach ($form_state->getValue('context') as $context_name => $value) {
       if ($form_state->get("context_$context_name") == 'selector') {
@@ -143,7 +144,7 @@ class ActionForm implements ExpressionFormInterface {
       }
     }
     $configuration = $context_config->toArray();
-    $configuration['action_id'] = $form_state->getValue('action');
+    $configuration['action_id'] = $form_state->get('action_id');
     $this->actionExpression->setConfiguration($configuration);
   }
 
diff --git a/src/Form/Expression/ConditionForm.php b/src/Form/Expression/ConditionForm.php
index da0d9508a91b6ae2b7560861f3158d925914d632..d15cb0cbe484d7b890e25a5e264130b7e08f395a 100644
--- a/src/Form/Expression/ConditionForm.php
+++ b/src/Form/Expression/ConditionForm.php
@@ -12,7 +12,6 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\rules\Core\ConditionManager;
 use Drupal\rules\Context\ContextConfig;
 use Drupal\rules\Engine\ConditionExpressionInterface;
-use Drupal\rules\Form\Expression\ExpressionFormInterface;
 
 /**
  * UI form for adding/editing a Rules condition.
@@ -48,14 +47,16 @@ class ConditionForm implements ExpressionFormInterface {
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
-    $condition_name = $form_state->get('condition');
+    $condition_id = $form_state->get('condition_id');
+
     $configuration = $this->conditionExpression->getConfiguration();
-    if (empty($condition_name) && !empty($configuration['condition_id'])) {
-      $condition_name = $configuration['condition_id'];
+    if (empty($condition_id) && !empty($configuration['condition_id'])) {
+      $condition_id = $configuration['condition_id'];
+      $form_state->set('condition_id', $condition_id);
     }
 
     // Step 1 of the multistep form.
-    if (!$condition_name) {
+    if (!$condition_id) {
       $condition_definitions = $this->conditionManager->getGroupedDefinitions();
       $options = [];
       foreach ($condition_definitions as $group => $definitions) {
@@ -64,7 +65,7 @@ class ConditionForm implements ExpressionFormInterface {
         }
       }
 
-      $form['condition'] = [
+      $form['condition_id'] = [
         '#type' => 'select',
         '#title' => $this->t('Condition'),
         '#options' => $options,
@@ -75,7 +76,7 @@ class ConditionForm implements ExpressionFormInterface {
         '#value' => $this->t('Continue'),
         '#name' => 'continue',
         // Only validate the selected condition in the first step.
-        '#limit_validation_errors' => [['condition']],
+        '#limit_validation_errors' => [['condition_id']],
         '#submit' => [static::class . '::submitFirstStep'],
       ];
 
@@ -84,15 +85,11 @@ class ConditionForm implements ExpressionFormInterface {
 
     // Step 2 of the form.
     /** @var \Drupal\rules\Core\RulesConditionInterface $condition */
-    $condition = $this->conditionManager->createInstance($condition_name);
+    $condition = $this->conditionManager->createInstance($condition_id);
 
     $form['summary'] = [
       '#markup' => $condition->summary(),
     ];
-    $form['condition'] = [
-      '#type' => 'value',
-      '#value' => $condition_name,
-    ];
 
     $context_definitions = $condition->getContextDefinitions();
 
@@ -114,8 +111,8 @@ class ConditionForm implements ExpressionFormInterface {
    * {@inheritdoc}
    */
   public function validateForm(array $form, FormStateInterface $form_state) {
-    // Only if there is a conditoon selected already we can validate something.
-    if ($form_state->get('condition')) {
+    // Only if there is a condition selected already we can validate something.
+    if ($form_state->get('condition_id')) {
       // Invoke the submission handler which will setup the expression being
       // edited in the form. That way the expression is ready for other
       // validation handlers.
@@ -123,12 +120,11 @@ class ConditionForm implements ExpressionFormInterface {
     }
   }
 
-
   /**
    * Submit callback: save the selected condition in the first step.
    */
   public static function submitFirstStep(array &$form, FormStateInterface $form_state) {
-    $form_state->set('condition', $form_state->getValue('condition'));
+    $form_state->set('condition_id', $form_state->getValue('condition_id'));
     $form_state->setRebuild();
   }
 
@@ -136,6 +132,10 @@ class ConditionForm implements ExpressionFormInterface {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
+    // Nothing todo as long as the first step is not completed.
+    if (!$form_state->get('condition_id')) {
+      return;
+    }
     $context_config = ContextConfig::create();
     foreach ($form_state->getValue('context') as $context_name => $value) {
       if ($form_state->get("context_$context_name") == 'selector') {
@@ -147,7 +147,7 @@ class ConditionForm implements ExpressionFormInterface {
     }
 
     $configuration = $context_config->toArray();
-    $configuration['condition_id'] = $form_state->getValue('condition');
+    $configuration['condition_id'] = $form_state->get('condition_id');
     $this->conditionExpression->setConfiguration($configuration);
   }
 
diff --git a/src/Form/ReactionRuleEditForm.php b/src/Form/ReactionRuleEditForm.php
index 8e47d49678e655b9faaf29f0393f6475fd9f2933..5c0f54ae1364d1d07268542da2865fbdd2f769a2 100644
--- a/src/Form/ReactionRuleEditForm.php
+++ b/src/Form/ReactionRuleEditForm.php
@@ -9,6 +9,7 @@ namespace Drupal\rules\Form;
 
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\rules\Core\RulesEventManager;
+use Drupal\rules\Core\RulesUiConfigHandler;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -16,8 +17,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  */
 class ReactionRuleEditForm extends RulesComponentFormBase {
 
-  use TempStoreTrait;
-
   /**
    * The event plugin manager.
    *
@@ -25,6 +24,13 @@ class ReactionRuleEditForm extends RulesComponentFormBase {
    */
   protected $eventManager;
 
+  /**
+   * The RulesUI handler of the currently active UI.
+   *
+   * @var \Drupal\rules\Core\RulesUiConfigHandler
+   */
+  protected $rulesUiHandler;
+
   /**
    * Constructs a new object of this class.
    *
@@ -42,11 +48,30 @@ class ReactionRuleEditForm extends RulesComponentFormBase {
     return new static($container->get('plugin.manager.rules_event'));
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, RulesUiConfigHandler $rules_ui_handler = NULL) {
+    // Overridden such we can receive further route parameters.
+    $this->rulesUiHandler = $rules_ui_handler;
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function prepareEntity() {
+    parent::prepareEntity();
+    // Replace the config entity with the latest entity from temp store, so any
+    // interim changes are picked up.
+    $this->entity = $this->rulesUiHandler->getConfig();
+  }
+
   /**
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
-    $this->addLockInformation($form);
+    $form['locked'] = $this->rulesUiHandler->addLockInformation();
 
     $event_name = $this->entity->getEvent();
     $event_definition = $this->eventManager->getDefinition($event_name);
@@ -54,11 +79,20 @@ class ReactionRuleEditForm extends RulesComponentFormBase {
       '@label' => $event_definition['label'],
       '@name' => $event_name,
     ]);
-    $form_handler = $this->entity->getExpression()->getFormHandler();
+    $form_handler = $this->rulesUiHandler->getComponent()
+      ->getExpression()->getFormHandler();
     $form = $form_handler->form($form, $form_state);
     return parent::form($form, $form_state);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
+    $this->rulesUiHandler->validateLock($form, $form_state);
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -78,8 +112,9 @@ class ReactionRuleEditForm extends RulesComponentFormBase {
    */
   public function save(array $form, FormStateInterface $form_state) {
     parent::save($form, $form_state);
+
     // Also remove the temporarily stored rule, it has been persisted now.
-    $this->deleteFromTempStore();
+    $this->rulesUiHandler->clearTemporaryStorage();
 
     drupal_set_message($this->t('Reaction rule %label has been updated.', ['%label' => $this->entity->label()]));
   }
@@ -88,7 +123,7 @@ class ReactionRuleEditForm extends RulesComponentFormBase {
    * Form submission handler for the 'cancel' action.
    */
   public function cancel(array $form, FormStateInterface $form_state) {
-    $this->deleteFromTempStore();
+    $this->rulesUiHandler->clearTemporaryStorage();
     drupal_set_message($this->t('Canceled.'));
     $form_state->setRedirect('entity.rules_reaction_rule.collection');
   }
@@ -100,13 +135,4 @@ class ReactionRuleEditForm extends RulesComponentFormBase {
     return $this->t('Edit reaction rule "@label"', ['@label' => $rules_reaction_rule->label()]);
   }
 
-  /**
-   * Returns the entity object, which is the rules config on this class.
-   *
-   * @see \Drupal\rules\Form\TempStoreTrait
-   */
-  protected function getRuleConfig() {
-    return $this->entity;
-  }
-
 }
diff --git a/src/Form/TempStoreTrait.php b/src/Form/TempStoreTrait.php
index 940022a3b021373c3aa4e2f1667b966cc44ae4ae..43e168cdeee220a0952c59188a871e4e7079f3b4 100644
--- a/src/Form/TempStoreTrait.php
+++ b/src/Form/TempStoreTrait.php
@@ -13,7 +13,13 @@ use Drupal\Core\Url;
 use Drupal\user\SharedTempStoreFactory;
 
 /**
- * Provides methods for modified rules configurations in temporary storage.
+ * Provides methods for modified rules components in temporary storage.
+ *
+ * Note that this implements the lock-related methods of
+ * \Drupal\rules\Core\RulesUiHandlerInterface.
+ *
+ * @see \Drupal\rules\Core\RulesUiHandlerInterface
+ * @see \Drupal\rules\Core\RulesUiConfigHandler
  */
 trait TempStoreTrait {
 
@@ -25,12 +31,19 @@ trait TempStoreTrait {
   protected $tempStoreFactory;
 
   /**
-   * The temporary store for the rules configuration.
+   * The temporary store for the rules component.
    *
    * @var \Drupal\user\SharedTempStore
    */
   protected $tempStore;
 
+  /**
+   * The currently active rules UI handler.
+   *
+   * @var \Drupal\rules\Core\RulesUiHandlerInterface
+   */
+  protected $rulesUiHandler;
+
   /**
    * The date formatter service.
    *
@@ -77,7 +90,7 @@ trait TempStoreTrait {
   /**
    * Setter injection for the date formatter service.
    *
-   * @param \Drupal\rules\Form\DateFormatterInterface $date_formatter
+   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
    *   The service.
    */
   public function setDateFormatter(DateFormatterInterface $date_formatter) {
@@ -98,36 +111,50 @@ trait TempStoreTrait {
   }
 
   /**
-   * Gets the temporary storage repository from the factory.
+   * Gets the currently active RulesUI's handler.
    *
-   * @return \Drupal\user\SharedTempStore
-   *   The shareds storage.
+   * @return \Drupal\rules\Core\RulesUiHandlerInterface
+   *   The RulesUI handler.
    */
-  protected function getTempStore() {
-    if (!isset($this->tempStore)) {
-      $this->tempStore = $this->getTempStoreFactory()->get($this->getRuleConfig()->getEntityTypeId());
-    }
-    return $this->tempStore;
+  protected function getRulesUiHandler() {
+    // Usually the trait is used on the RulesUI handler.
+    return $this;
   }
 
   /**
-   * Saves the rule configuration to the temporary storage.
+   * Fetches the stored data from the temporary storage.
+   *
+   * @return mixed|null
+   *   The stored data or NULL if the temp store is empty.
    */
-  protected function saveToTempStore() {
-    $this->getTempStore()->set($this->getRuleConfig()->id(), $this->getRuleConfig());
+  protected function fetchFromTempStore() {
+    return $this->getTempStore()->get($this->getTempStoreItemId());
   }
 
   /**
-   * Determines if the rule coniguration is locked for the current user.
+   * Stores some data in the temporary storage.
    *
-   * @return bool
-   *   TRUE if locked, FALSE otherwise.
+   * @param mixed $data
+   *   The data to store.
+   */
+  protected function storeToTempStore($data) {
+    $this->getTempStore()->set($this->getTempStoreItemId(), $data);
+  }
+
+  /**
+   * @see \Drupal\rules\Core\RulesUiHandlerInterface::clearTemporaryStorage()
    */
-  protected function isLocked() {
+  public function clearTemporaryStorage() {
+    $this->getTempStore()->delete($this->getTempStoreItemId());
+  }
+
+  /**
+   * @see \Drupal\rules\Core\RulesUiHandlerInterface::isLocked()
+   */
+  public function isLocked() {
     // If there is an object in the temporary storage from another user then
-    // this configuration is locked.
-    if ($this->getTempStore()->get($this->getRuleConfig()->id())
-      && !$this->getTempStore()->getIfOwner($this->getRuleConfig()->id())
+    // this component is locked.
+    if ($this->getTempStore()->get($this->getTempStoreItemId()) && !$this->getTempStore()->getIfOwner($this->getTempStoreItemId())
     ) {
       return TRUE;
     }
@@ -135,54 +162,53 @@ trait TempStoreTrait {
   }
 
   /**
-   * Provides information which user at which time locked the rule for editing.
+   * Generates the temp store item's ID to use for the edited component.
    *
-   * @return object
-   *   StdClass object as provided by \Drupal\user\SharedTempStore.
+   * @return string
+   *   The temp store ID.
    */
-  protected function getLockMetaData() {
-    return $this->getTempStore()->getMetadata($this->getRuleConfig()->id());
+  private function getTempStoreItemId() {
+    // The internal path is unique for the currently edited component.
+    return $this->getRulesUiHandler()->getBaseRouteUrl()->getInternalPath();
   }
 
   /**
-   * Checks if the rule has been modified and is present in the storage.
+   * Gets the temporary storage repository from the factory.
    *
-   * @return bool
-   *   TRUE if the rule has been modified, FALSE otherwise.
+   * @return \Drupal\user\SharedTempStore
+   *   The shareds storage.
    */
-  protected function isEdited() {
-    if ($this->getTempStore()->get($this->getRuleConfig()->id())) {
-      return TRUE;
+  private function getTempStore() {
+    if (!isset($this->tempStore)) {
+      $this->tempStore = $this->getTempStoreFactory()->get($this->getRulesUiHandler()->getPluginId());
     }
-    return FALSE;
+    return $this->tempStore;
   }
 
   /**
-   * Removed the current rule configuration from the temporary storage.
+   * @see \Drupal\rules\Core\RulesUiHandlerInterface::getLockMetaData()
    */
-  protected function deleteFromTempStore() {
-    $this->getTempStore()->delete($this->getRuleConfig()->id());
+  public function getLockMetaData() {
+    return $this->getTempStore()->getMetadata($this->getTempStoreItemId());
   }
 
   /**
-   * Provides the config entity object that is dealt with in the temp store.
-   *
-   * @return \Drupal\Core\Config\Entity\ConfigEntityInterface
-   *   The rules config entity.
+   * @see \Drupal\rules\Core\RulesUiHandlerInterface::isEdited()
    */
-  protected function getRuleConfig() {
-    return $this->ruleConfig;
+  public function isEdited() {
+    if ($this->getTempStore()->get($this->getTempStoreItemId())) {
+      return TRUE;
+    }
+    return FALSE;
   }
 
   /**
-   * Adds a message to the form if the rule configuration is locked/modified.
-   *
-   * @param array $form
-   *   The form render array.
+   * @see \Drupal\rules\Core\RulesUiHandlerInterface::addLockInformation()
    */
-  protected function addLockInformation(array &$form) {
+  public function addLockInformation() {
+    $build = [];
     if ($this->isLocked()) {
-      $form['locked'] = [
+      $build['locked'] = [
         '#type' => 'container',
         '#attributes' => [
           'class' => ['rules-locked', 'messages', 'messages--warning'],
@@ -192,7 +218,7 @@ trait TempStoreTrait {
       ];
     }
     else {
-      $form['changed'] = [
+      $build['changed'] = [
         '#type' => 'container',
         '#attributes' => [
           'class' => ['rules-changed', 'messages', 'messages--warning'],
@@ -201,15 +227,16 @@ trait TempStoreTrait {
         '#weight' => -10,
       ];
       if (!$this->isEdited()) {
-        $form['changed']['#attributes']['class'][] = 'js-hide';
+        $build['changed']['#attributes']['class'][] = 'js-hide';
       }
     }
+    return $build;
   }
 
   /**
-   * Validation callback that prevents editing locked rule configs.
+   * @see \Drupal\rules\Core\RulesUiHandlerInterface::validateLock()
    */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
+  public function validateLock(array &$form, FormStateInterface $form_state) {
     if ($this->isLocked()) {
       $form_state->setError($form, $this->lockInformationMessage());
     }
@@ -221,7 +248,7 @@ trait TempStoreTrait {
    * @return \Drupal\Core\StringTranslation\TranslatableMarkup
    *   The message suitable to be shown in the UI.
    */
-  protected function lockInformationMessage() {
+  private function lockInformationMessage() {
     $lock = $this->getLockMetaData();
     $username = [
       '#theme' => 'username',
@@ -230,11 +257,10 @@ trait TempStoreTrait {
     $lock_message_substitutions = [
       '@user' => drupal_render($username),
       '@age' => $this->getDateFormatter()->formatTimeDiffSince($lock->updated),
-      ':url' => Url::fromRoute('entity.rules_reaction_rule.break_lock_form', [
-        'rules_reaction_rule' => $this->getRuleConfig()->id(),
-      ])->toString(),
+      '@component_type' => $this->getRulesUiHandler()->getPluginDefinition()->component_type_label,
+      ':url' => Url::fromRoute($this->getRulesUiHandler()->getPluginDefinition()->base_route . '.break_lock', \Drupal::routeMatch()->getRawParameters()->all())->toString(),
     ];
-    return $this->t('This rule is being edited by user @user, and is therefore locked from editing by others. This lock is @age old. Click here to <a href=":url">break this lock</a>.', $lock_message_substitutions);
+    return $this->t('This @component_type is being edited by user @user, and is therefore locked from editing by others. This lock is @age old. Click here to <a href=":url">break this lock</a>.', $lock_message_substitutions);
   }
 
 }
diff --git a/src/ParamConverter/RulesTempConverter.php b/src/ParamConverter/RulesTempConverter.php
deleted file mode 100644
index 8b58a696de3e51e31d8bb8ed135f09b0cfe18f0e..0000000000000000000000000000000000000000
--- a/src/ParamConverter/RulesTempConverter.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\rules\ParamConverter\RulesTempConverter.
- */
-
-namespace Drupal\rules\ParamConverter;
-
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\ParamConverter\EntityConverter;
-use Symfony\Component\Routing\Route;
-use Drupal\user\SharedTempStoreFactory;
-
-/**
- * Provides upcasting for a rules entity to be used in the UI.
- *
- * Either loads the rule from the temporary storage if it is currently being
- * edited or from the canonical entity storage otherwise.
- *
- * Largely copied from \Drupal\views_ui\ParamConverter\ViewsUIConverter.
- */
-class RulesTempConverter extends EntityConverter {
-
-  /**
-   * Stores the tempstore factory.
-   *
-   * @var \Drupal\user\SharedTempStoreFactory
-   */
-  protected $tempStoreFactory;
-
-  /**
-   * Constructor.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   * @param \Drupal\user\SharedTempStoreFactory $temp_store_factory
-   *   The factory for the temp store object.
-   */
-  public function __construct(EntityManagerInterface $entity_manager, SharedTempStoreFactory $temp_store_factory) {
-    parent::__construct($entity_manager);
-
-    $this->tempStoreFactory = $temp_store_factory;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function convert($value, $definition, $name, array $defaults) {
-    // Standard upcasting: check if the config entity exists at all in the
-    // storage.
-    if (!$entity = parent::convert($value, $definition, $name, $defaults)) {
-      return;
-    }
-
-    // Now check if there is also a version being edited and return that.
-    $store = $this->tempStoreFactory->get($entity->getEntityTypeId());
-    $edited_entity = $store->get($value);
-    if ($edited_entity) {
-      return $edited_entity;
-    }
-
-    return $entity;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function applies($definition, $name, Route $route) {
-    if (parent::applies($definition, $name, $route)) {
-      return !empty($definition['tempstore']) && $definition['type'] === 'entity:rules_reaction_rule';
-    }
-    return FALSE;
-  }
-
-}
diff --git a/src/Plugin/RulesExpression/Rule.php b/src/Plugin/RulesExpression/Rule.php
index 07c66a94c168a21bbf38348a2d28ff0ed1ccb467..ec7b0759464c1ee84bdae898faef09f8dd4d38e0 100644
--- a/src/Plugin/RulesExpression/Rule.php
+++ b/src/Plugin/RulesExpression/Rule.php
@@ -28,11 +28,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  * nest several rules into one rule. This is the functionality of so called
  * "rule sets" in Drupal 7.
  *
- * @todo rename the form class to just RuleForm.
- *
  * @RulesExpression(
  *   id = "rules_rule",
- *   label = @Translation("A rule, executing actions when conditions are met."),
+ *   label = @Translation("Rule"),
  *   form_class = "\Drupal\rules\Form\Expression\ReactionRuleForm"
  * )
  */
@@ -239,4 +237,14 @@ class Rule extends ExpressionBase implements RuleInterface, ContainerFactoryPlug
     return TRUE;
   }
 
+  /**
+   * PHP magic __clone function.
+   */
+  public function __clone() {
+    $this->actions = clone $this->actions;
+    $this->actions->setRoot($this->getRoot());
+    $this->conditions = clone $this->conditions;
+    $this->conditions->setRoot($this->getRoot());
+  }
+
 }
diff --git a/src/ProxyClass/ParamConverter/RulesTempConverter.php b/src/ProxyClass/ParamConverter/RulesTempConverter.php
deleted file mode 100644
index 70b5a18f1d5fb1bc5789a3bf3925191dfe3191ba..0000000000000000000000000000000000000000
--- a/src/ProxyClass/ParamConverter/RulesTempConverter.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\rules\ProxyClass\ParamConverter\RulesTempConverter.
- */
-
-/**
- * This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\rules\ParamConverter\RulesTempConverter' "modules/rules/src".
- */
-
-namespace Drupal\rules\ProxyClass\ParamConverter {
-
-    /**
-     * Provides a proxy class for \Drupal\rules\ParamConverter\RulesTempConverter.
-     *
-     * @see \Drupal\Component\ProxyBuilder
-     */
-    class RulesTempConverter implements \Drupal\Core\ParamConverter\ParamConverterInterface
-    {
-
-        use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
-
-        /**
-         * The id of the original proxied service.
-         *
-         * @var string
-         */
-        protected $drupalProxyOriginalServiceId;
-
-        /**
-         * The real proxied service, after it was lazy loaded.
-         *
-         * @var \Drupal\rules\ParamConverter\RulesTempConverter
-         */
-        protected $service;
-
-        /**
-         * The service container.
-         *
-         * @var \Symfony\Component\DependencyInjection\ContainerInterface
-         */
-        protected $container;
-
-        /**
-         * Constructs a ProxyClass Drupal proxy object.
-         *
-         * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
-         *   The container.
-         * @param string $drupal_proxy_original_service_id
-         *   The service ID of the original service.
-         */
-        public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
-        {
-            $this->container = $container;
-            $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
-        }
-
-        /**
-         * Lazy loads the real service from the container.
-         *
-         * @return object
-         *   Returns the constructed real service.
-         */
-        protected function lazyLoadItself()
-        {
-            if (!isset($this->service)) {
-                $this->service = $this->container->get($this->drupalProxyOriginalServiceId);
-            }
-
-            return $this->service;
-        }
-
-        /**
-         * {@inheritdoc}
-         */
-        public function convert($value, $definition, $name, array $defaults)
-        {
-            return $this->lazyLoadItself()->convert($value, $definition, $name, $defaults);
-        }
-
-        /**
-         * {@inheritdoc}
-         */
-        public function applies($definition, $name, \Symfony\Component\Routing\Route $route)
-        {
-            return $this->lazyLoadItself()->applies($definition, $name, $route);
-        }
-
-    }
-
-}
diff --git a/src/Routing/RulesUiRouteEnhancer.php b/src/Routing/RulesUiRouteEnhancer.php
new file mode 100644
index 0000000000000000000000000000000000000000..43782bff4214c2ae80ab6e5687292a8a50d75d12
--- /dev/null
+++ b/src/Routing/RulesUiRouteEnhancer.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rules\Routing\RulesUiRouteEnhancer.
+ */
+
+namespace Drupal\rules\Routing;
+
+use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
+use Drupal\rules\Core\RulesUiManagerInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Enhances routes with the specified RulesUI.
+ *
+ * Routes have the plugin ID of the active RulesUI instance set on the _rules_ui
+ * option. Based upon that information, this enhances adds the following
+ * parameters to the routes:
+ * - rules_ui_handler: The RulesUI handler, as specified by the plugin.
+ * - rules_component: The rules component being edited, as provided by the
+ *   handler.
+ */
+class RulesUiRouteEnhancer implements RouteEnhancerInterface {
+
+  /**
+   * The rules_ui plugin manager.
+   *
+   * @var \Drupal\rules\Core\RulesUiManagerInterface
+   */
+  protected $rulesUiManager;
+
+  /**
+   * Constructs the object.
+   *
+   * @param \Drupal\rules\Core\RulesUiManagerInterface $rules_ui_manager
+   *   The rules_ui plugin manager.
+   */
+  public function __construct(RulesUiManagerInterface $rules_ui_manager) {
+    $this->rulesUiManager = $rules_ui_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function enhance(array $defaults, Request $request) {
+    // @var $route \Symfony\Component\Routing\Route
+    $route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
+    $plugin_id = $route->getOption('_rules_ui');
+    $defaults['rules_ui_handler'] = $this->rulesUiManager->createInstance($plugin_id);
+    return $defaults;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies(Route $route) {
+    return ($route->hasOption('_rules_ui'));
+  }
+
+}
diff --git a/src/Routing/RulesUiRouteSubscriber.php b/src/Routing/RulesUiRouteSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..ddfa86c4585c91213f4eb9682e2d83537e29719b
--- /dev/null
+++ b/src/Routing/RulesUiRouteSubscriber.php
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\rules\Routing\RulesUiRouteSubscriber.
+ */
+
+namespace Drupal\rules\Routing;
+
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Drupal\Core\Routing\RoutingEvents;
+use Drupal\rules\Core\RulesUiDefinition;
+use Drupal\rules\Core\RulesUiManagerInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Adds routes generated by the rules UI handlers.
+ */
+class RulesUiRouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * The rules UI manager.
+   *
+   * @var \Drupal\rules\Core\RulesUiManagerInterface
+   */
+  protected $rulesUiManager;
+
+  /**
+   * Constructs the object.
+   *
+   * @param \Drupal\rules\Core\RulesUiManagerInterface $rules_ui_manager
+   *   The rules UI manager.
+   */
+  public function __construct(RulesUiManagerInterface $rules_ui_manager) {
+    $this->rulesUiManager = $rules_ui_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection) {
+    foreach ($this->rulesUiManager->getDefinitions() as $name => $definition) {
+      $ui_definition = $this->rulesUiManager->getDefinition($name);
+      $this->registerRoutes($ui_definition, $collection);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events = parent::getSubscribedEvents();
+    // Should run after AdminRouteSubscriber so the routes can inherit admin
+    // status of the edit routes on entities. Therefore priority -210.
+    $events[RoutingEvents::ALTER] = ['onAlterRoutes', -210];
+    return $events;
+  }
+
+  /**
+   * Registers the routes as needed for the UI.
+   *
+   * @param \Drupal\rules\Core\RulesUiDefinition $ui_definition
+   *   The definition of the RulesUI for which to register the routes.
+   * @param \Symfony\Component\Routing\RouteCollection $collection
+   *   The route collection to which to add the routes.
+   */
+  protected function registerRoutes(RulesUiDefinition $ui_definition, RouteCollection $collection) {
+    $base_route = $collection->get($ui_definition->base_route);
+
+    $options = [
+      'parameters' => ($base_route->getOption('parameters') ?: []),
+      '_admin_route' => $base_route->getOption('_admin_route') ?: FALSE,
+      '_rules_ui' => $ui_definition->id,
+    ];
+    $requirements = [
+      '_permission' => $ui_definition->permissions ?: $base_route->getRequirement('_permission'),
+    ];
+
+    $route = (new Route($base_route->getPath() . '/add/{expression_id}'))
+      ->addDefaults([
+        '_form' => '\Drupal\rules\Form\AddExpressionForm',
+        '_title_callback' => '\Drupal\rules\Form\AddExpressionForm::getTitle',
+      ])
+      ->addOptions($options)
+      ->addRequirements($requirements);
+    $collection->add($ui_definition->base_route . '.expression.add', $route);
+
+    $route = (new Route($base_route->getPath() . '/edit/{uuid}'))
+      ->addDefaults([
+        '_form' => '\Drupal\rules\Form\EditExpressionForm',
+        '_title_callback' => '\Drupal\rules\Form\EditExpressionForm::getTitle',
+      ])
+      ->addOptions($options)
+      ->addRequirements($requirements);
+    $collection->add($ui_definition->base_route . '.expression.edit', $route);
+
+    $route = (new Route($base_route->getPath() . '/delete/{uuid}'))
+      ->addDefaults([
+        '_form' => '\Drupal\rules\Form\DeleteExpressionForm',
+        '_title' => 'Delete expression',
+      ])
+      ->addOptions($options)
+      ->addRequirements($requirements);
+    $collection->add($ui_definition->base_route . '.expression.delete', $route);
+
+    $route = (new Route($base_route->getPath() . '/break-lock'))
+      ->addDefaults([
+        '_form' => '\Drupal\rules\Form\\BreakLockForm',
+        '_title' => 'Break lock',
+      ])
+      ->addOptions($options)
+      ->addRequirements($requirements);
+    $collection->add($ui_definition->base_route . '.break_lock', $route);
+  }
+
+}
diff --git a/src/Routing/UiRouteSubscriber.php b/src/Routing/UiRouteSubscriber.php
deleted file mode 100644
index 087e80c19dc64affe430b40549c2a595013dd70b..0000000000000000000000000000000000000000
--- a/src/Routing/UiRouteSubscriber.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\rules\Routing\UiRouteSubscriber.
- */
-
-namespace Drupal\rules\Routing;
-
-use Drupal\Core\Routing\RouteSubscriberBase;
-use Drupal\Core\Routing\RoutingEvents;
-use Drupal\rules\Core\RulesUiManagerInterface;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Adds routes generated by the rules UI handlers.
- */
-class UiRouteSubscriber extends RouteSubscriberBase {
-
-  /**
-   * The rules UI manager.
-   *
-   * @var \Drupal\rules\Core\RulesUiManagerInterface
-   */
-  protected $rulesUiManager;
-
-  /**
-   * Constructs the object.
-   *
-   * @param \Drupal\rules\Core\RulesUiManagerInterface $rules_ui_manager
-   *   The rules UI manager.
-   */
-  public function __construct(RulesUiManagerInterface $rules_ui_manager) {
-    $this->rulesUiManager = $rules_ui_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function alterRoutes(RouteCollection $collection) {
-    foreach ($this->rulesUiManager->getDefinitions() as $name => $definition) {
-      $ui_handler = $this->rulesUiManager->createInstance($name);
-      $ui_handler->registerRoutes($collection);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getSubscribedEvents() {
-    $events = parent::getSubscribedEvents();
-    // Should run after AdminRouteSubscriber so the routes can inherit admin
-    // status of the edit routes on entities. Therefore priority -210.
-    $events[RoutingEvents::ALTER] = ['onAlterRoutes', -210];
-    return $events;
-  }
-
-}
diff --git a/tests/src/Kernel/RulesUiEmbedTest.php b/tests/src/Kernel/RulesUiEmbedTest.php
index 44374bffcf4bd2cacf25aaed21dcf01882203ff6..b0c1bce442ec6603179760499737a04e540e5261 100644
--- a/tests/src/Kernel/RulesUiEmbedTest.php
+++ b/tests/src/Kernel/RulesUiEmbedTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Tests\rules\Kernel;
 
-use Drupal\rules\Core\RulesUiDefaultHandler;
+use Drupal\rules\Core\RulesUiConfigHandler;
 use Drupal\rules\Core\RulesUiDefinition;
 
 /**
@@ -55,7 +55,7 @@ class RulesUiEmbedTest extends RulesDrupalTestBase {
     $this->assertTrue(isset($definition['rules_test_ui_embed.settings_conditions']));
     $this->assertInstanceOf(RulesUiDefinition::class, $definition['rules_test_ui_embed.settings_conditions']);
     $this->assertTrue(!empty($definition['rules_test_ui_embed.settings_conditions']->label));
-    $this->assertEquals(RulesUiDefaultHandler::class, $definition['rules_test_ui_embed.settings_conditions']->getClass());
+    $this->assertEquals(RulesUiConfigHandler::class, $definition['rules_test_ui_embed.settings_conditions']->getClass());
   }
 
 }