diff --git a/resources/js/wysiwyg/todo.md b/resources/js/wysiwyg/todo.md
index 1b10ef91b..194832d5f 100644
--- a/resources/js/wysiwyg/todo.md
+++ b/resources/js/wysiwyg/todo.md
@@ -2,7 +2,7 @@
 
 ## In progress
 
-//
+- Link heading-based ID reference menu
 
 ## Main Todo
 
@@ -10,8 +10,6 @@
 - Alignments: Handle inline block content (image, video)
 - Image paste upload
 - Keyboard shortcuts support
-- Link popup menu for cross-content reference
-- Link heading-based ID reference menu
 - Drawing gallery integration
 - Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
 - Media resize support (like images)
diff --git a/resources/js/wysiwyg/ui/defaults/forms/objects.ts b/resources/js/wysiwyg/ui/defaults/forms/objects.ts
index c37696695..6bd265e6c 100644
--- a/resources/js/wysiwyg/ui/defaults/forms/objects.ts
+++ b/resources/js/wysiwyg/ui/defaults/forms/objects.ts
@@ -5,9 +5,9 @@ import {
     EditorSelectFormFieldDefinition
 } from "../../framework/forms";
 import {EditorUiContext} from "../../framework/core";
-import {$createTextNode, $getSelection} from "lexical";
+import {$createTextNode, $getSelection, $insertNodes} from "lexical";
 import {$isImageNode, ImageNode} from "../../../nodes/image";
-import {$createLinkNode} from "@lexical/link";
+import {$createLinkNode, $isLinkNode} from "@lexical/link";
 import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "../../../nodes/media";
 import {$insertNodeToNearestRoot} from "@lexical/utils";
 import {$getNodeFromSelection} from "../../../utils/selection";
@@ -16,6 +16,8 @@ import {EditorActionField} from "../../framework/blocks/action-field";
 import {EditorButton} from "../../framework/buttons";
 import {showImageManager} from "../../../utils/images";
 import searchImageIcon from "@icons/editor/image-search.svg";
+import searchIcon from "@icons/search.svg";
+import {showLinkSelector} from "../../../utils/links";
 
 export function $showImageForm(image: ImageNode, context: EditorUiContext) {
     const imageModal: EditorFormModal = context.manager.createModal('image');
@@ -97,23 +99,62 @@ export const link: EditorFormDefinition = {
     async action(formData, context: EditorUiContext) {
         context.editor.update(() => {
 
+            const url = formData.get('url')?.toString() || '';
+            const title = formData.get('title')?.toString() || ''
+            const target = formData.get('target')?.toString() || '';
+            const text = formData.get('text')?.toString() || '';
+
             const selection = $getSelection();
+            let link = $getNodeFromSelection(selection, $isLinkNode);
+            if ($isLinkNode(link)) {
+                link.setURL(url);
+                link.setTarget(target);
+                link.setTitle(title);
+            } else {
+                link = $createLinkNode(url, {
+                    title: title,
+                    target: target,
+                });
 
-            const linkNode = $createLinkNode(formData.get('url')?.toString() || '', {
-                title: formData.get('title')?.toString() || '',
-                target: formData.get('target')?.toString() || '',
-            });
-            linkNode.append($createTextNode(formData.get('text')?.toString() || ''));
+                $insertNodes([link]);
+            }
 
-            selection?.insertNodes([linkNode]);
+            if ($isLinkNode(link)) {
+                for (const child of link.getChildren()) {
+                    child.remove(true);
+                }
+                link.append($createTextNode(text));
+            }
         });
         return true;
     },
     fields: [
         {
-            label: 'URL',
-            name: 'url',
-            type: 'text',
+            build() {
+                return new EditorActionField(
+                    new EditorFormField({
+                        label: 'URL',
+                        name: 'url',
+                        type: 'text',
+                    }),
+                    new EditorButton({
+                        label: 'Browse links',
+                        icon: searchIcon,
+                        action(context: EditorUiContext) {
+                            showLinkSelector(entity => {
+                                const modal =  context.manager.getActiveModal('link');
+                                if (modal) {
+                                    modal.getForm().setValues({
+                                        url: entity.link,
+                                        text: entity.name,
+                                        title: entity.name,
+                                    });
+                                }
+                            });
+                        }
+                    }),
+                );
+            },
         },
         {
             label: 'Text to display',
diff --git a/resources/js/wysiwyg/utils/links.ts b/resources/js/wysiwyg/utils/links.ts
new file mode 100644
index 000000000..03c4a5ef0
--- /dev/null
+++ b/resources/js/wysiwyg/utils/links.ts
@@ -0,0 +1,16 @@
+import {EntitySelectorPopup} from "../../components";
+
+type EditorEntityData = {
+    link: string;
+    name: string;
+};
+
+export function showLinkSelector(callback: (entity: EditorEntityData) => any, selectionText?: string) {
+    const selector: EntitySelectorPopup = window.$components.first('entity-selector-popup') as EntitySelectorPopup;
+    selector.show((entity: EditorEntityData) => callback(entity), {
+        initialValue: selectionText,
+        searchEndpoint: '/search/entity-selector',
+        entityTypes: 'page,book,chapter,bookshelf',
+        entityPermission: 'view',
+    });
+}
\ No newline at end of file