diff --git a/js/builder.js b/js/builder.js
index 8c792894abc849a868f39a152080e19731f3d548..534f198a71899ae6ccc769159330d0e8d1983f56 100644
--- a/js/builder.js
+++ b/js/builder.js
@@ -112,7 +112,10 @@
     } else {
       controls.classList.remove('is-fixed');
     }
-    if (controlsRect.bottom + offsetY > componentRect.bottom) {
+    if (
+      controls.classList.contains('is-fixed') &&
+      controlsRect.bottom + offsetY > componentRect.bottom
+    ) {
       controls.classList.add('is-scrolled-past');
     } else {
       controls.classList.remove('is-scrolled-past');
@@ -121,8 +124,9 @@
 
   /**
    * Removes focus data attributes from all components.
+   * @param {Event} originalEvent The original event that triggered selection.
    */
-  function unselectComponents() {
+  function unselectComponents(originalEvent) {
     selectAll('[data-active="true"]').forEach((element) => {
       element.removeAttribute('data-active');
     });
@@ -132,6 +136,13 @@
     selectAll('.is-navigating').forEach((element) =>
       element.classList.remove('is-navigating'),
     );
+    if (activeComponent && activeComponent.closest(`[${idAttr}]`)) {
+      dispatch(activeComponent, 'lpb-component:blur', {
+        componentUuid: activeComponent.getAttribute('data-uuid'),
+        layoutId: activeComponent.closest(`[${idAttr}]`).getAttribute(idAttr),
+        originalEvent,
+      });
+    }
     activeComponent = null;
     setUiElementVisibility();
   }
@@ -140,8 +151,9 @@
    * Adds focus data attributes to a component and all its parents.
    *
    * @param {HTMLElement} element The component to focus.
+   * @param {Event} originalEvent The original event that triggered selection.
    */
-  function selectComponent(element) {
+  function selectComponent(element, originalEvent = {}) {
     activeComponent = element;
     select('.lpb-hover-label')?.remove();
     // Add the data-active attribute to the element.
@@ -151,6 +163,7 @@
     dispatch(element, 'lpb-component:focus', {
       componentUuid: element.getAttribute('data-uuid'),
       layoutId: element.closest(`[${idAttr}]`).getAttribute(idAttr),
+      originalEvent,
     });
     setUiElementVisibility();
     setControlsPosition();
@@ -297,9 +310,7 @@
       submit: {
         components: JSON.stringify(order),
       },
-      error: () => {
-        // Fail silently to prevent console errors.
-      },
+      error: () => {},
     }).execute();
   };
   const reorderComponents = debounce(Drupal.layoutParagraphsUpdateOrder);
@@ -625,14 +636,14 @@
     ajax,
     response,
   ) => {
-    const { layoutId, componentUuid, eventName } = response;
+    const { layoutId, componentUuid, eventName, extra } = response;
     const element =
       document.querySelector(`[data-uuid="${componentUuid}"]`) ||
       document.querySelector(`[data-lpb-id="${layoutId}"]`);
-
     dispatch(element, `lpb-${eventName}`, {
       componentUuid,
       layoutId,
+      extra,
     });
   };
 
@@ -753,6 +764,9 @@
    * Select the component that was clicked.
    */
   listen(document, 'mouseup', (event) => {
+    if (event.target.ownerDocument !== document) {
+      return;
+    }
     let element = event.target.closest('.js-lpb-component');
     if (element) {
       while (
@@ -764,9 +778,9 @@
         element = element.parentNode.closest('.js-lpb-component');
       }
     }
-    unselectComponents();
+    unselectComponents(event);
     if (element) {
-      selectComponent(element);
+      selectComponent(element, event);
     }
   });
 
@@ -774,9 +788,12 @@
     const component = event.target.closest('.js-lpb-ui')
       ? event.target.closest('.js-lpb-component')
       : null;
+    if (component === activeComponent) {
+      return;
+    }
     if (component) {
-      unselectComponents();
-      selectComponent(component);
+      unselectComponents(event);
+      selectComponent(component, event);
     }
   });
 
@@ -813,11 +830,11 @@
    * Selects a newly inserted or updated component.
    */
   listen(
-    document,
+    document.body,
     ['lpb-component:insert', 'lpb-component:update'],
     (event) => {
-      unselectComponents();
-      selectComponent(event.target);
+      unselectComponents(event);
+      selectComponent(event.target, event);
     },
   );
 
@@ -826,20 +843,20 @@
    */
   listen(document, 'keydown', (event) => {
     if (event.key === 'Escape') {
-      return unselectComponents();
+      return unselectComponents(event);
     }
     if (
       event.key === 'Enter' &&
       event.target.matches('.js-lpb-component:not([data-active="true"])')
     ) {
-      unselectComponents();
-      return selectComponent(event.target);
+      unselectComponents(event);
+      return selectComponent(event.target, event);
     }
     if (
       event.key === 'Enter' &&
       event.target.matches('.js-lpb-component[data-active="true"]')
     ) {
-      return unselectComponents();
+      return unselectComponents(event);
     }
     const focused = select('.js-lpb-component[data-active="true"]');
     if (!focused) {
@@ -871,7 +888,7 @@
    * Add is-changed class when a layout is modified.
    */
   listen(
-    document,
+    document.body,
     [
       'lpb-component:insert',
       'lpb-component:update',
@@ -886,9 +903,13 @@
   /**
    * Reorder component on drop or move.
    */
-  listen(document, ['lpb-component:drop', 'lpb-component:move'], (event) => {
-    reorderComponents(event.target.closest(`[${idAttr}]`));
-  });
+  listen(
+    document.body,
+    ['lpb-component:drop', 'lpb-component:move'],
+    (event) => {
+      reorderComponents(event.target.closest(`[${idAttr}]`));
+    },
+  );
 
   /**
    * Click handler for various UI elements.
diff --git a/src/Ajax/LayoutParagraphsEventCommand.php b/src/Ajax/LayoutParagraphsEventCommand.php
index f4952f6870316ae561391f3624bc7cd79dea92af..a7e25171d8dff45336139de03aba0f66467f5038 100644
--- a/src/Ajax/LayoutParagraphsEventCommand.php
+++ b/src/Ajax/LayoutParagraphsEventCommand.php
@@ -30,13 +30,19 @@ class LayoutParagraphsEventCommand implements CommandInterface {
    */
   protected $eventName;
 
+  /**
+   * Extra data to pass to the event.
+   */
+  protected $extra;
+
   /**
    * Constructor.
    */
-  public function __construct($layout, $component_uuid, $event_name) {
+  public function __construct($layout, $component_uuid, $event_name, $extra = []) {
     $this->layout = $layout;
     $this->componentUuid = $component_uuid;
     $this->eventName = $event_name;
+    $this->extra = $extra;
   }
 
   /**
@@ -51,6 +57,7 @@ class LayoutParagraphsEventCommand implements CommandInterface {
       'layoutId' => $this->layout->id(),
       'componentUuid' => $this->componentUuid,
       'eventName' => $this->eventName,
+      'extra' => $this->extra,
     ];
   }
 
diff --git a/src/Form/ComponentFormBase.php b/src/Form/ComponentFormBase.php
index 0dbb7b2a7a61b711d0b9524b7912b3ce6b439c11..832b32f00e40c613c53c5bad6eb4e1b0f93352ea 100644
--- a/src/Form/ComponentFormBase.php
+++ b/src/Form/ComponentFormBase.php
@@ -523,10 +523,14 @@ abstract class ComponentFormBase extends FormBase implements ComponentFormInterf
    *   The paragraph render array.
    */
   protected function renderParagraph(string $uuid) {
+    $this->layoutParagraphsLayout->setComponent($this->paragraph);
     return [
       '#type' => 'layout_paragraphs_builder',
       '#layout_paragraphs_layout' => $this->layoutParagraphsLayout,
       '#uuid' => $uuid,
+      '#cache' => [
+        'max-age' => 0,
+      ],
     ];
   }