diff --git a/resources/icons/editor/image-search.svg b/resources/icons/editor/image-search.svg
new file mode 100644
index 000000000..b8cb2cfc8
--- /dev/null
+++ b/resources/icons/editor/image-search.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h200v80H200v560h560v-214l80 80v134q0 33-23.5 56.5T760-120H200Zm40-160 120-160 90 120 120-160 150 200H240Zm622-144L738-548q-21 14-45 21t-51 7q-74 0-126-52.5T464-700q0-75 52.5-127.5T644-880q75 0 127.5 52.5T824-700q0 27-8 52t-20 46l122 122-56 56ZM644-600q42 0 71-29t29-71q0-42-29-71t-71-29q-42 0-71 29t-29 71q0 42 29 71t71 29Z"/></svg>
\ No newline at end of file
diff --git a/resources/js/wysiwyg/nodes/diagram.ts b/resources/js/wysiwyg/nodes/diagram.ts
index e2ffeaadd..bd37b200c 100644
--- a/resources/js/wysiwyg/nodes/diagram.ts
+++ b/resources/js/wysiwyg/nodes/diagram.ts
@@ -3,15 +3,12 @@ import {
     DOMConversion,
     DOMConversionMap,
     DOMConversionOutput,
-    LexicalEditor, LexicalNode,
+    LexicalEditor,
     SerializedLexicalNode,
     Spread
 } from "lexical";
 import type {EditorConfig} from "lexical/LexicalEditor";
 import {EditorDecoratorAdapter} from "../ui/framework/decorator";
-import * as DrawIO from '../../services/drawio';
-import {EditorUiContext} from "../ui/framework/core";
-import {HttpError} from "../../services/http";
 import {el} from "../utils/dom";
 
 export type SerializedDiagramNode = Spread<{
@@ -156,69 +153,3 @@ export class DiagramNode extends DecoratorNode<EditorDecoratorAdapter> {
 export function $createDiagramNode(drawingId: string = '', drawingUrl: string = ''): DiagramNode {
     return new DiagramNode(drawingId, drawingUrl);
 }
-
-export function $isDiagramNode(node: LexicalNode | null | undefined): node is DiagramNode {
-    return node instanceof DiagramNode;
-}
-
-
-function handleUploadError(error: HttpError, context: EditorUiContext): void {
-    if (error.status === 413) {
-        window.$events.emit('error', context.options.translations.serverUploadLimitText || '');
-    } else {
-        window.$events.emit('error', context.options.translations.imageUploadErrorText || '');
-    }
-    console.error(error);
-}
-
-async function loadDiagramIdFromNode(editor: LexicalEditor, node: DiagramNode): Promise<string> {
-    const drawingId = await new Promise<string>((res, rej) => {
-        editor.getEditorState().read(() => {
-            const {id: drawingId} = node.getDrawingIdAndUrl();
-            res(drawingId);
-        });
-    });
-
-    return drawingId || '';
-}
-
-async function updateDrawingNodeFromData(context: EditorUiContext, node: DiagramNode, pngData: string, isNew: boolean): Promise<void> {
-    DrawIO.close();
-
-    if (isNew) {
-        const loadingImage: string = window.baseUrl('/loading.gif');
-        context.editor.update(() => {
-            node.setDrawingIdAndUrl('', loadingImage);
-        });
-    }
-
-    try {
-        const img = await DrawIO.upload(pngData, context.options.pageId);
-        context.editor.update(() => {
-            node.setDrawingIdAndUrl(String(img.id), img.url);
-        });
-    } catch (err) {
-        if (err instanceof HttpError) {
-            handleUploadError(err, context);
-        }
-
-        if (isNew) {
-            context.editor.update(() => {
-                node.remove();
-            });
-        }
-
-        throw new Error(`Failed to save image with error: ${err}`);
-    }
-}
-
-export function $openDrawingEditorForNode(context: EditorUiContext, node: DiagramNode): void {
-    let isNew = false;
-    DrawIO.show(context.options.drawioUrl, async () => {
-        const drawingId = await loadDiagramIdFromNode(context.editor, node);
-        isNew = !drawingId;
-        return isNew ? '' : DrawIO.load(drawingId);
-    }, async (pngData: string) => {
-        return updateDrawingNodeFromData(context, node, pngData, isNew);
-    });
-}
\ No newline at end of file
diff --git a/resources/js/wysiwyg/todo.md b/resources/js/wysiwyg/todo.md
index c8a0293d5..1b10ef91b 100644
--- a/resources/js/wysiwyg/todo.md
+++ b/resources/js/wysiwyg/todo.md
@@ -2,7 +2,7 @@
 
 ## In progress
 
-// 
+//
 
 ## Main Todo
 
@@ -12,8 +12,6 @@
 - Keyboard shortcuts support
 - Link popup menu for cross-content reference
 - Link heading-based ID reference menu
-- Image gallery integration for insert
-- Image gallery integration for form
 - 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/decorators/diagram.ts b/resources/js/wysiwyg/ui/decorators/diagram.ts
index 7c79f9f41..44d332939 100644
--- a/resources/js/wysiwyg/ui/decorators/diagram.ts
+++ b/resources/js/wysiwyg/ui/decorators/diagram.ts
@@ -1,8 +1,9 @@
 import {EditorDecorator} from "../framework/decorator";
 import {EditorUiContext} from "../framework/core";
 import {BaseSelection} from "lexical";
-import {$openDrawingEditorForNode, DiagramNode} from "../../nodes/diagram";
+import {DiagramNode} from "../../nodes/diagram";
 import {$selectionContainsNode, $selectSingleNode} from "../../utils/selection";
+import {$openDrawingEditorForNode} from "../../utils/diagrams";
 
 
 export class DiagramDecorator extends EditorDecorator {
diff --git a/resources/js/wysiwyg/ui/defaults/buttons/objects.ts b/resources/js/wysiwyg/ui/defaults/buttons/objects.ts
index 0eac497fc..f4075a740 100644
--- a/resources/js/wysiwyg/ui/defaults/buttons/objects.ts
+++ b/resources/js/wysiwyg/ui/defaults/buttons/objects.ts
@@ -5,7 +5,7 @@ import {
     $createNodeSelection,
     $createTextNode,
     $getRoot,
-    $getSelection,
+    $getSelection, $insertNodes,
     $setSelection,
     BaseSelection,
     ElementNode
@@ -20,7 +20,7 @@ import codeBlockIcon from "@icons/editor/code-block.svg";
 import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../../../nodes/code-block";
 import editIcon from "@icons/edit.svg";
 import diagramIcon from "@icons/editor/diagram.svg";
-import {$createDiagramNode, $isDiagramNode, $openDrawingEditorForNode, DiagramNode} from "../../../nodes/diagram";
+import {$createDiagramNode, DiagramNode} from "../../../nodes/diagram";
 import detailsIcon from "@icons/editor/details.svg";
 import mediaIcon from "@icons/editor/media.svg";
 import {$createDetailsNode, $isDetailsNode} from "../../../nodes/details";
@@ -30,6 +30,9 @@ import {
     $insertNewBlockNodeAtSelection,
     $selectionContainsNodeType
 } from "../../../utils/selection";
+import {$isDiagramNode, $openDrawingEditorForNode} from "../../../utils/diagrams";
+import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
+import {$showImageForm} from "../forms/objects";
 
 export const link: EditorButtonDefinition = {
     label: 'Insert/edit link',
@@ -94,28 +97,19 @@ export const image: EditorButtonDefinition = {
     label: 'Insert/Edit Image',
     icon: imageIcon,
     action(context: EditorUiContext) {
-        const imageModal = context.manager.createModal('image');
-        const selection = context.lastSelection;
-        const selectedImage = $getNodeFromSelection(selection, $isImageNode) as ImageNode | null;
-
         context.editor.getEditorState().read(() => {
-            let formDefaults = {};
+            const selectedImage = $getNodeFromSelection(context.lastSelection, $isImageNode) as ImageNode | null;
             if (selectedImage) {
-                formDefaults = {
-                    src: selectedImage.getSrc(),
-                    alt: selectedImage.getAltText(),
-                    height: selectedImage.getHeight(),
-                    width: selectedImage.getWidth(),
-                }
-
-                context.editor.update(() => {
-                    const selection = $createNodeSelection();
-                    selection.add(selectedImage.getKey());
-                    $setSelection(selection);
-                });
+                $showImageForm(selectedImage, context);
+                return;
             }
 
-            imageModal.show(formDefaults);
+            showImageManager((image) => {
+                context.editor.update(() => {
+                    const link = $createLinkedImageNodeFromImageData(image);
+                    $insertNodes([link]);
+                });
+            })
         });
     },
     isActive(selection: BaseSelection | null): boolean {
diff --git a/resources/js/wysiwyg/ui/defaults/forms/objects.ts b/resources/js/wysiwyg/ui/defaults/forms/objects.ts
index dbb89b18f..c37696695 100644
--- a/resources/js/wysiwyg/ui/defaults/forms/objects.ts
+++ b/resources/js/wysiwyg/ui/defaults/forms/objects.ts
@@ -1,31 +1,78 @@
-import {EditorFormDefinition, EditorFormTabs, EditorSelectFormFieldDefinition} from "../../framework/forms";
+import {
+    EditorFormDefinition,
+    EditorFormField,
+    EditorFormTabs,
+    EditorSelectFormFieldDefinition
+} from "../../framework/forms";
 import {EditorUiContext} from "../../framework/core";
 import {$createTextNode, $getSelection} from "lexical";
-import {$createImageNode} from "../../../nodes/image";
+import {$isImageNode, ImageNode} from "../../../nodes/image";
 import {$createLinkNode} from "@lexical/link";
 import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "../../../nodes/media";
 import {$insertNodeToNearestRoot} from "@lexical/utils";
 import {$getNodeFromSelection} from "../../../utils/selection";
+import {EditorFormModal} from "../../framework/modals";
+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";
+
+export function $showImageForm(image: ImageNode, context: EditorUiContext) {
+    const imageModal: EditorFormModal = context.manager.createModal('image');
+    const height = image.getHeight();
+    const width = image.getWidth();
+
+    const formData = {
+        src: image.getSrc(),
+        alt: image.getAltText(),
+        height: height === 0 ? '' : String(height),
+        width: width === 0 ? '' : String(width),
+    };
+
+    imageModal.show(formData);
+}
 
 export const image: EditorFormDefinition = {
     submitText: 'Apply',
     async action(formData, context: EditorUiContext) {
         context.editor.update(() => {
-            const selection = $getSelection();
-            const imageNode = $createImageNode(formData.get('src')?.toString() || '', {
-                alt: formData.get('alt')?.toString() || '',
-                height: Number(formData.get('height')?.toString() || '0'),
-                width: Number(formData.get('width')?.toString() || '0'),
-            });
-            selection?.insertNodes([imageNode]);
+            const selectedImage = $getNodeFromSelection(context.lastSelection, $isImageNode);
+            if ($isImageNode(selectedImage)) {
+                selectedImage.setSrc(formData.get('src')?.toString() || '');
+                selectedImage.setAltText(formData.get('alt')?.toString() || '');
+
+                selectedImage.setWidth(Number(formData.get('width')?.toString() || '0'));
+                selectedImage.setHeight(Number(formData.get('height')?.toString() || '0'));
+            }
         });
         return true;
     },
     fields: [
         {
-            label: 'Source',
-            name: 'src',
-            type: 'text',
+            build() {
+                return new EditorActionField(
+                    new EditorFormField({
+                        label: 'Source',
+                        name: 'src',
+                        type: 'text',
+                    }),
+                    new EditorButton({
+                        label: 'Browse files',
+                        icon: searchImageIcon,
+                        action(context: EditorUiContext) {
+                            showImageManager((image) => {
+                                 const modal =  context.manager.getActiveModal('image');
+                                 if (modal) {
+                                     modal.getForm().setValues({
+                                         src: image.thumbs?.display || image.url,
+                                         alt: image.name,
+                                     });
+                                 }
+                            });
+                        }
+                    }),
+                );
+            },
         },
         {
             label: 'Alternative description',
diff --git a/resources/js/wysiwyg/ui/framework/blocks/action-field.ts b/resources/js/wysiwyg/ui/framework/blocks/action-field.ts
new file mode 100644
index 000000000..1f40c2864
--- /dev/null
+++ b/resources/js/wysiwyg/ui/framework/blocks/action-field.ts
@@ -0,0 +1,26 @@
+import {EditorContainerUiElement, EditorUiElement} from "../core";
+import {el} from "../../../utils/dom";
+import {EditorFormField} from "../forms";
+import {EditorButton} from "../buttons";
+
+
+export class EditorActionField extends EditorContainerUiElement {
+    protected input: EditorFormField;
+    protected action: EditorButton;
+
+    constructor(input: EditorFormField, action: EditorButton) {
+        super([input, action]);
+
+        this.input = input;
+        this.action = action;
+    }
+
+    buildDOM(): HTMLElement {
+        return el('div', {
+            class: 'editor-action-input-container',
+        }, [
+            this.input.getDOMElement(),
+            this.action.getDOMElement(),
+        ]);
+    }
+}
diff --git a/resources/js/wysiwyg/ui/framework/manager.ts b/resources/js/wysiwyg/ui/framework/manager.ts
index 29d959910..92891b540 100644
--- a/resources/js/wysiwyg/ui/framework/manager.ts
+++ b/resources/js/wysiwyg/ui/framework/manager.ts
@@ -11,6 +11,7 @@ export type SelectionChangeHandler = (selection: BaseSelection|null) => void;
 export class EditorUIManager {
 
     protected modalDefinitionsByKey: Record<string, EditorFormModalDefinition> = {};
+    protected activeModalsByKey: Record<string, EditorFormModal> = {};
     protected decoratorConstructorsByType: Record<string, typeof EditorDecorator> = {};
     protected decoratorInstancesByNodeKey: Record<string, EditorDecorator> = {};
     protected context: EditorUiContext|null = null;
@@ -50,12 +51,24 @@ export class EditorUIManager {
             throw new Error(`Attempted to show modal of key [${key}] but no modal registered for that key`);
         }
 
-        const modal = new EditorFormModal(modalDefinition);
+        const modal = new EditorFormModal(modalDefinition, key);
         modal.setContext(this.getContext());
 
         return modal;
     }
 
+    setModalActive(key: string, modal: EditorFormModal): void {
+        this.activeModalsByKey[key] = modal;
+    }
+
+    setModalInactive(key: string): void {
+        delete this.activeModalsByKey[key];
+    }
+
+    getActiveModal(key: string): EditorFormModal|null {
+        return this.activeModalsByKey[key];
+    }
+
     registerDecoratorType(type: string, decorator: typeof EditorDecorator) {
         this.decoratorConstructorsByType[type] = decorator;
     }
diff --git a/resources/js/wysiwyg/ui/framework/modals.ts b/resources/js/wysiwyg/ui/framework/modals.ts
index 1768f6f54..ae69302f6 100644
--- a/resources/js/wysiwyg/ui/framework/modals.ts
+++ b/resources/js/wysiwyg/ui/framework/modals.ts
@@ -13,10 +13,12 @@ export interface EditorFormModalDefinition extends EditorModalDefinition {
 
 export class EditorFormModal extends EditorContainerUiElement {
     protected definition: EditorFormModalDefinition;
+    protected key: string;
 
-    constructor(definition: EditorFormModalDefinition) {
+    constructor(definition: EditorFormModalDefinition, key: string) {
         super([new EditorForm(definition.form)]);
         this.definition = definition;
+        this.key = key;
     }
 
     show(defaultValues: Record<string, string>) {
@@ -26,13 +28,16 @@ export class EditorFormModal extends EditorContainerUiElement {
         const form = this.getForm();
         form.setValues(defaultValues);
         form.setOnCancel(this.hide.bind(this));
+
+        this.getContext().manager.setModalActive(this.key, this);
     }
 
     hide() {
         this.getDOMElement().remove();
+        this.getContext().manager.setModalInactive(this.key);
     }
 
-    protected getForm(): EditorForm {
+    getForm(): EditorForm {
         return this.children[0] as EditorForm;
     }
 
diff --git a/resources/js/wysiwyg/utils/diagrams.ts b/resources/js/wysiwyg/utils/diagrams.ts
new file mode 100644
index 000000000..50d7d5b3f
--- /dev/null
+++ b/resources/js/wysiwyg/utils/diagrams.ts
@@ -0,0 +1,70 @@
+import {LexicalEditor, LexicalNode} from "lexical";
+import {HttpError} from "../../services/http";
+import {EditorUiContext} from "../ui/framework/core";
+import * as DrawIO from "../../services/drawio";
+import {DiagramNode} from "../nodes/diagram";
+
+export function $isDiagramNode(node: LexicalNode | null | undefined): node is DiagramNode {
+    return node instanceof DiagramNode;
+}
+
+function handleUploadError(error: HttpError, context: EditorUiContext): void {
+    if (error.status === 413) {
+        window.$events.emit('error', context.options.translations.serverUploadLimitText || '');
+    } else {
+        window.$events.emit('error', context.options.translations.imageUploadErrorText || '');
+    }
+    console.error(error);
+}
+
+async function loadDiagramIdFromNode(editor: LexicalEditor, node: DiagramNode): Promise<string> {
+    const drawingId = await new Promise<string>((res, rej) => {
+        editor.getEditorState().read(() => {
+            const {id: drawingId} = node.getDrawingIdAndUrl();
+            res(drawingId);
+        });
+    });
+
+    return drawingId || '';
+}
+
+async function updateDrawingNodeFromData(context: EditorUiContext, node: DiagramNode, pngData: string, isNew: boolean): Promise<void> {
+    DrawIO.close();
+
+    if (isNew) {
+        const loadingImage: string = window.baseUrl('/loading.gif');
+        context.editor.update(() => {
+            node.setDrawingIdAndUrl('', loadingImage);
+        });
+    }
+
+    try {
+        const img = await DrawIO.upload(pngData, context.options.pageId);
+        context.editor.update(() => {
+            node.setDrawingIdAndUrl(String(img.id), img.url);
+        });
+    } catch (err) {
+        if (err instanceof HttpError) {
+            handleUploadError(err, context);
+        }
+
+        if (isNew) {
+            context.editor.update(() => {
+                node.remove();
+            });
+        }
+
+        throw new Error(`Failed to save image with error: ${err}`);
+    }
+}
+
+export function $openDrawingEditorForNode(context: EditorUiContext, node: DiagramNode): void {
+    let isNew = false;
+    DrawIO.show(context.options.drawioUrl, async () => {
+        const drawingId = await loadDiagramIdFromNode(context.editor, node);
+        isNew = !drawingId;
+        return isNew ? '' : DrawIO.load(drawingId);
+    }, async (pngData: string) => {
+        return updateDrawingNodeFromData(context, node, pngData, isNew);
+    });
+}
\ No newline at end of file
diff --git a/resources/js/wysiwyg/utils/images.ts b/resources/js/wysiwyg/utils/images.ts
new file mode 100644
index 000000000..89a4a60f0
--- /dev/null
+++ b/resources/js/wysiwyg/utils/images.ts
@@ -0,0 +1,26 @@
+import {ImageManager} from "../../components";
+import {$createImageNode} from "../nodes/image";
+import {$createLinkNode, LinkNode} from "@lexical/link";
+
+type EditorImageData = {
+    url: string;
+    thumbs?: {display: string};
+    name: string;
+};
+
+export function showImageManager(callback: (image: EditorImageData) => any) {
+    const imageManager: ImageManager = window.$components.first('image-manager') as ImageManager;
+    imageManager.show((image: EditorImageData) => {
+        callback(image);
+    }, 'gallery');
+}
+
+export function $createLinkedImageNodeFromImageData(image: EditorImageData): LinkNode {
+    const url = image.thumbs?.display || image.url;
+    const linkNode = $createLinkNode(url, {target: '_blank'});
+    const imageNode = $createImageNode(url, {
+        alt: image.name
+    });
+    linkNode.append(imageNode);
+    return linkNode;
+}
\ No newline at end of file
diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss
index 0cf145559..379c436f4 100644
--- a/resources/sass/_editor.scss
+++ b/resources/sass/_editor.scss
@@ -479,6 +479,16 @@ textarea.editor-form-field-input {
 .editor-form-tab-contents {
   width: 360px;
 }
+.editor-action-input-container {
+  display: flex;
+  flex-direction: row;
+  align-items: end;
+  justify-content: space-between;
+  gap: .1rem;
+  .editor-button {
+    margin-bottom: 12px;
+  }
+}
 
 // Editor theme styles
 .editor-theme-bold {