diff --git a/resources/js/services/events.ts b/resources/js/services/events.ts
index 7d72a9f1a..32c70f5a8 100644
--- a/resources/js/services/events.ts
+++ b/resources/js/services/events.ts
@@ -1,7 +1,7 @@
 import {HttpError} from "./http";
 
 export class EventManager {
-    protected listeners: Record<string, ((data: {}) => void)[]> = {};
+    protected listeners: Record<string, ((data: any) => void)[]> = {};
     protected stack: {name: string, data: {}}[] = [];
 
     /**
@@ -19,7 +19,7 @@ export class EventManager {
     /**
      * Listen to a custom event and run the given callback when that event occurs.
      */
-    listen(eventName: string, callback: (data: {}) => void): void {
+    listen<T>(eventName: string, callback: (data: T) => void): void {
         if (typeof this.listeners[eventName] === 'undefined') this.listeners[eventName] = [];
         this.listeners[eventName].push(callback);
     }
diff --git a/resources/js/wysiwyg/actions.ts b/resources/js/wysiwyg/actions.ts
index 3a32b82d8..0e2202525 100644
--- a/resources/js/wysiwyg/actions.ts
+++ b/resources/js/wysiwyg/actions.ts
@@ -1,13 +1,30 @@
-import {$createParagraphNode, $getRoot, $isTextNode, LexicalEditor} from "lexical";
+import {$getRoot, $getSelection, $isTextNode, LexicalEditor, LexicalNode, RootNode} from "lexical";
 import {$generateHtmlFromNodes, $generateNodesFromDOM} from "@lexical/html";
 import {$createCustomParagraphNode} from "./nodes/custom-paragraph";
 
+function htmlToDom(html: string): Document {
+    const parser = new DOMParser();
+    return parser.parseFromString(html, 'text/html');
+}
+
+function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
+    return nodes.map(node => {
+        if ($isTextNode(node)) {
+            const paragraph = $createCustomParagraphNode();
+            paragraph.append(node);
+            return paragraph;
+        }
+        return node;
+    });
+}
+
+function appendNodesToRoot(root: RootNode, nodes: LexicalNode[]) {
+    root.append(...wrapTextNodes(nodes));
+}
 
 export function setEditorContentFromHtml(editor: LexicalEditor, html: string) {
-    const parser = new DOMParser();
-    const dom = parser.parseFromString(html, 'text/html');
+    const dom = htmlToDom(html);
 
-    console.log(html);
     editor.update(() => {
         // Empty existing
         const root = $getRoot();
@@ -16,18 +33,52 @@ export function setEditorContentFromHtml(editor: LexicalEditor, html: string) {
         }
 
         const nodes = $generateNodesFromDOM(editor, dom);
+        root.append(...wrapTextNodes(nodes));
+    });
+}
 
-        // Wrap top-level text nodes
-        for (let i = 0; i < nodes.length; i++) {
-            const node = nodes[i];
-            if ($isTextNode(node)) {
-                const paragraph = $createCustomParagraphNode();
-                paragraph.append(node);
-                nodes[i] = paragraph;
+export function appendHtmlToEditor(editor: LexicalEditor, html: string) {
+    const dom = htmlToDom(html);
+
+    editor.update(() => {
+        const root = $getRoot();
+        const nodes = $generateNodesFromDOM(editor, dom);
+        root.append(...wrapTextNodes(nodes));
+    });
+}
+
+export function prependHtmlToEditor(editor: LexicalEditor, html: string) {
+    const dom = htmlToDom(html);
+
+    editor.update(() => {
+        const root = $getRoot();
+        const nodes = wrapTextNodes($generateNodesFromDOM(editor, dom));
+        let reference = root.getChildren()[0];
+        for (let i = nodes.length - 1; i >= 0; i--) {
+            if (reference) {
+                reference.insertBefore(nodes[i]);
+            } else {
+                root.append(nodes[i])
+            }
+            reference = nodes[i];
+        }
+    });
+}
+
+export function insertHtmlIntoEditor(editor: LexicalEditor, html: string) {
+    const dom = htmlToDom(html);
+    editor.update(() => {
+        const selection = $getSelection();
+        const nodes = wrapTextNodes($generateNodesFromDOM(editor, dom));
+
+        const reference = selection?.getNodes()[0];
+        const referencesParents = reference?.getParents() || [];
+        const topLevel = referencesParents[referencesParents.length - 1];
+        if (topLevel && reference) {
+            for (let i = nodes.length - 1; i >= 0; i--) {
+                reference.insertAfter(nodes[i]);
             }
         }
-
-        root.append(...nodes);
     });
 }
 
@@ -38,4 +89,8 @@ export function getEditorContentAsHtml(editor: LexicalEditor): Promise<string> {
             resolve(html);
         });
     });
+}
+
+export function focusEditor(editor: LexicalEditor) {
+    editor.focus(() => {}, {defaultSelection: "rootStart"});
 }
\ No newline at end of file
diff --git a/resources/js/wysiwyg/common-events.ts b/resources/js/wysiwyg/common-events.ts
new file mode 100644
index 000000000..7355d977b
--- /dev/null
+++ b/resources/js/wysiwyg/common-events.ts
@@ -0,0 +1,43 @@
+import {LexicalEditor} from "lexical";
+import {
+    appendHtmlToEditor,
+    focusEditor,
+    insertHtmlIntoEditor,
+    prependHtmlToEditor,
+    setEditorContentFromHtml
+} from "./actions";
+
+type EditorEventContent = {
+    html: string;
+    markdown: string;
+};
+
+function getContentToInsert(eventContent: EditorEventContent): string {
+    return eventContent.html || '';
+}
+
+export function listen(editor: LexicalEditor): void {
+    window.$events.listen<EditorEventContent>('editor::replace', eventContent => {
+        const html = getContentToInsert(eventContent);
+        setEditorContentFromHtml(editor, html);
+    });
+
+    window.$events.listen<EditorEventContent>('editor::append', eventContent => {
+        const html = getContentToInsert(eventContent);
+        appendHtmlToEditor(editor, html);
+    });
+
+    window.$events.listen<EditorEventContent>('editor::prepend', eventContent => {
+        const html = getContentToInsert(eventContent);
+        prependHtmlToEditor(editor, html);
+    });
+
+    window.$events.listen<EditorEventContent>('editor::insert', eventContent => {
+        const html = getContentToInsert(eventContent);
+        insertHtmlIntoEditor(editor, html);
+    });
+
+    window.$events.listen<EditorEventContent>('editor::focus', () => {
+        focusEditor(editor);
+    });
+}
diff --git a/resources/js/wysiwyg/index.ts b/resources/js/wysiwyg/index.ts
index 469738e7f..e53b9b057 100644
--- a/resources/js/wysiwyg/index.ts
+++ b/resources/js/wysiwyg/index.ts
@@ -8,6 +8,7 @@ import {getEditorContentAsHtml, setEditorContentFromHtml} from "./actions";
 import {registerTableResizer} from "./ui/framework/helpers/table-resizer";
 import {el} from "./helpers";
 import {EditorUiContext} from "./ui/framework/core";
+import {listen as listenToCommonEvents} from "./common-events";
 
 export function createPageEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
     const config: CreateEditorArgs = {
@@ -47,6 +48,8 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
         registerTableResizer(editor, editWrap),
     );
 
+    listenToCommonEvents(editor);
+
     setEditorContentFromHtml(editor, htmlContent);
 
     const debugView = document.getElementById('lexical-debug');
diff --git a/resources/js/wysiwyg/todo.md b/resources/js/wysiwyg/todo.md
index e39a4c655..c62a6e524 100644
--- a/resources/js/wysiwyg/todo.md
+++ b/resources/js/wysiwyg/todo.md
@@ -21,7 +21,6 @@
 - Table features
 - Image paste upload
 - Keyboard shortcuts support
-- Global/shared editor events support
 - Draft/change management (connect with page editor component)
 - Add ID support to all block types
 - Template drag & drop / insert