diff --git a/resources/js/wysiwyg/nodes/image.ts b/resources/js/wysiwyg/nodes/image.ts
index 1e2cbd83c..289e36c4b 100644
--- a/resources/js/wysiwyg/nodes/image.ts
+++ b/resources/js/wysiwyg/nodes/image.ts
@@ -9,6 +9,7 @@ import {
 } from "lexical";
 import type {EditorConfig} from "lexical/LexicalEditor";
 import {el} from "../helpers";
+import {EditorDecoratorAdapter} from "../ui/framework/decorator";
 
 export interface ImageNodeOptions {
     alt?: string;
@@ -23,7 +24,7 @@ export type SerializedImageNode = Spread<{
     height: number;
 }, SerializedLexicalNode>
 
-export class ImageNode extends DecoratorNode<HTMLElement> {
+export class ImageNode extends DecoratorNode<EditorDecoratorAdapter> {
     __src: string = '';
     __alt: string = '';
     __width: number = 0;
@@ -79,6 +80,7 @@ export class ImageNode extends DecoratorNode<HTMLElement> {
     setWidth(width: number): void {
         const self = this.getWritable();
         self.__width = width;
+        console.log('widrg', width)
     }
 
     getWidth(): number {
@@ -90,17 +92,16 @@ export class ImageNode extends DecoratorNode<HTMLElement> {
         return true;
     }
 
-    decorate(editor: LexicalEditor, config: EditorConfig): HTMLElement {
-        console.log('decorate!');
-        return el('div', {
-            class: 'editor-image-decorator',
-        }, ['decoration!!!']);
+    decorate(editor: LexicalEditor, config: EditorConfig): EditorDecoratorAdapter {
+        return {
+            type: 'image',
+            getNode: () => this,
+        };
     }
 
     createDOM(_config: EditorConfig, _editor: LexicalEditor) {
         const element = document.createElement('img');
         element.setAttribute('src', this.__src);
-        element.textContent
 
         if (this.__width) {
             element.setAttribute('width', String(this.__width));
@@ -116,9 +117,38 @@ export class ImageNode extends DecoratorNode<HTMLElement> {
         ]);
     }
 
-    updateDOM(prevNode: unknown, dom: HTMLElement) {
-        // Returning false tells Lexical that this node does not need its
-        // DOM element replacing with a new copy from createDOM.
+    updateDOM(prevNode: ImageNode, dom: HTMLElement) {
+        const image = dom.querySelector('img');
+        if (!image) return false;
+
+        if (prevNode.__src !== this.__src) {
+            image.setAttribute('src', this.__src);
+        }
+
+        if (prevNode.__width !== this.__width) {
+            if (this.__width) {
+                image.setAttribute('width', String(this.__width));
+            } else {
+                image.removeAttribute('width');
+            }
+        }
+
+        if (prevNode.__height !== this.__height) {
+            if (this.__height) {
+                image.setAttribute('height', String(this.__height));
+            } else {
+                image.removeAttribute('height');
+            }
+        }
+
+        if (prevNode.__alt !== this.__alt) {
+            if (this.__alt) {
+                image.setAttribute('alt', String(this.__alt));
+            } else {
+                image.removeAttribute('alt');
+            }
+        }
+
         return false;
     }
 
diff --git a/resources/js/wysiwyg/ui/decorators/image.ts b/resources/js/wysiwyg/ui/decorators/image.ts
new file mode 100644
index 000000000..fd333fa54
--- /dev/null
+++ b/resources/js/wysiwyg/ui/decorators/image.ts
@@ -0,0 +1,91 @@
+import {EditorDecorator} from "../framework/decorator";
+import {el} from "../../helpers";
+import {$createNodeSelection, $setSelection} from "lexical";
+import {EditorUiContext} from "../framework/core";
+import {ImageNode} from "../../nodes/image";
+
+
+export class ImageDecorator extends EditorDecorator {
+    protected dom: HTMLElement|null = null;
+
+    buildDOM(context: EditorUiContext) {
+        const handleClasses = ['nw', 'ne', 'se', 'sw'];
+        const handleEls = handleClasses.map(c => {
+            return el('div', {class: `editor-image-decorator-handle ${c}`});
+        });
+
+        const decorateEl = el('div', {
+            class: 'editor-image-decorator',
+        }, handleEls);
+
+        const windowClick = (event: MouseEvent) => {
+            if (!decorateEl.contains(event.target as Node)) {
+                unselect();
+            }
+        };
+
+        const select = () => {
+            decorateEl.classList.add('selected');
+            window.addEventListener('click', windowClick);
+        };
+
+        const unselect = () => {
+            decorateEl.classList.remove('selected');
+            window.removeEventListener('click', windowClick);
+        };
+
+        decorateEl.addEventListener('click', (event) => {
+            context.editor.update(() => {
+                const nodeSelection = $createNodeSelection();
+                nodeSelection.add(this.getNode().getKey());
+                $setSelection(nodeSelection);
+            });
+
+            select();
+        });
+
+        decorateEl.addEventListener('mousedown', (event: MouseEvent) => {
+            const handle = (event.target as Element).closest('.editor-image-decorator-handle');
+            if (handle) {
+                this.startHandlingResize(handle, event, context);
+            }
+        });
+
+        return decorateEl;
+    }
+
+    render(context: EditorUiContext): HTMLElement {
+        if (this.dom) {
+            return this.dom;
+        }
+
+        this.dom = this.buildDOM(context);
+        return this.dom;
+    }
+
+    startHandlingResize(element: Node, event: MouseEvent, context: EditorUiContext) {
+        const startingX = event.screenX;
+        const startingY = event.screenY;
+
+        const mouseMoveListener = (event: MouseEvent) => {
+            const xChange = event.screenX - startingX;
+            const yChange = event.screenY - startingY;
+            console.log({ xChange, yChange });
+
+            context.editor.update(() => {
+                const node = this.getNode() as ImageNode;
+                node.setWidth(node.getWidth() + xChange);
+                node.setHeight(node.getHeight() + yChange);
+            });
+        };
+
+        const mouseUpListener = (event: MouseEvent) => {
+            window.removeEventListener('mousemove', mouseMoveListener);
+            window.removeEventListener('mouseup', mouseUpListener);
+        }
+
+        window.addEventListener('mousemove', mouseMoveListener);
+        window.addEventListener('mouseup', mouseUpListener);
+    }
+
+}
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/framework/decorator.ts b/resources/js/wysiwyg/ui/framework/decorator.ts
new file mode 100644
index 000000000..890774126
--- /dev/null
+++ b/resources/js/wysiwyg/ui/framework/decorator.ts
@@ -0,0 +1,32 @@
+import {EditorUiContext} from "./core";
+import {LexicalNode} from "lexical";
+
+export interface EditorDecoratorAdapter {
+    type: string;
+    getNode(): LexicalNode;
+}
+
+export abstract class EditorDecorator {
+
+    protected node: LexicalNode | null = null;
+    protected context: EditorUiContext;
+
+    constructor(context: EditorUiContext) {
+        this.context = context;
+    }
+
+    protected getNode(): LexicalNode {
+        if (!this.node) {
+            throw new Error('Attempted to get use node without it being set');
+        }
+
+        return this.node;
+    }
+
+    setNode(node: LexicalNode) {
+        this.node = node;
+    }
+
+    abstract render(context: EditorUiContext): HTMLElement;
+
+}
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/framework/manager.ts b/resources/js/wysiwyg/ui/framework/manager.ts
index c3fe9ecd8..1684b6628 100644
--- a/resources/js/wysiwyg/ui/framework/manager.ts
+++ b/resources/js/wysiwyg/ui/framework/manager.ts
@@ -1,10 +1,13 @@
 import {EditorFormModal, EditorFormModalDefinition} from "./modals";
 import {EditorUiContext} from "./core";
+import {EditorDecorator} from "./decorator";
 
 
 export class EditorUIManager {
 
     protected modalDefinitionsByKey: Record<string, EditorFormModalDefinition> = {};
+    protected decoratorConstructorsByType: Record<string, typeof EditorDecorator> = {};
+    protected decoratorInstancesByNodeKey: Record<string, EditorDecorator> = {};
     protected context: EditorUiContext|null = null;
 
     setContext(context: EditorUiContext) {
@@ -26,7 +29,7 @@ export class EditorUIManager {
     createModal(key: string): EditorFormModal {
         const modalDefinition = this.modalDefinitionsByKey[key];
         if (!modalDefinition) {
-            console.error(`Attempted to show modal of key [${key}] but no modal registered for that key`);
+            throw new Error(`Attempted to show modal of key [${key}] but no modal registered for that key`);
         }
 
         const modal = new EditorFormModal(modalDefinition);
@@ -35,4 +38,23 @@ export class EditorUIManager {
         return modal;
     }
 
+    registerDecoratorType(type: string, decorator: typeof EditorDecorator) {
+        this.decoratorConstructorsByType[type] = decorator;
+    }
+
+    getDecorator(decoratorType: string, nodeKey: string): EditorDecorator {
+        if (this.decoratorInstancesByNodeKey[nodeKey]) {
+            return this.decoratorInstancesByNodeKey[nodeKey];
+        }
+
+        const decoratorClass = this.decoratorConstructorsByType[decoratorType];
+        if (!decoratorClass) {
+            throw new Error(`Attempted to use decorator of type [${decoratorType}] but not decorator registered for that type`);
+        }
+
+        // @ts-ignore
+        const decorator = new decoratorClass(nodeKey);
+        this.decoratorInstancesByNodeKey[nodeKey] = decorator;
+        return decorator;
+    }
 }
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/index.ts b/resources/js/wysiwyg/ui/index.ts
index 9206f8b40..19320b262 100644
--- a/resources/js/wysiwyg/ui/index.ts
+++ b/resources/js/wysiwyg/ui/index.ts
@@ -9,7 +9,8 @@ import {EditorUIManager} from "./framework/manager";
 import {link as linkFormDefinition} from "./defaults/form-definitions";
 import {DecoratorListener} from "lexical/LexicalEditor";
 import type {NodeKey} from "lexical/LexicalNode";
-import {el} from "../helpers";
+import {EditorDecoratorAdapter} from "./framework/decorator";
+import {ImageDecorator} from "./decorators/image";
 
 export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
     const manager = new EditorUIManager();
@@ -33,11 +34,15 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
 
     // Register decorator listener
     // Maybe move to manager?
-    const domDecorateListener: DecoratorListener<HTMLElement> = (decorator: Record<NodeKey, HTMLElement>) => {
-        const keys = Object.keys(decorator);
+    manager.registerDecoratorType('image', ImageDecorator);
+    const domDecorateListener: DecoratorListener<EditorDecoratorAdapter> = (decorators: Record<NodeKey, EditorDecoratorAdapter>) => {
+        const keys = Object.keys(decorators);
         for (const key of keys) {
             const decoratedEl = editor.getElementByKey(key);
-            const decoratorEl = decorator[key];
+            const adapter = decorators[key];
+            const decorator = manager.getDecorator(adapter.type, key);
+            decorator.setNode(adapter.getNode());
+            const decoratorEl = decorator.render(context);
             if (decoratedEl) {
                 decoratedEl.append(decoratorEl);
             }
diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss
index 2633e8539..94fe2c756 100644
--- a/resources/sass/_editor.scss
+++ b/resources/sass/_editor.scss
@@ -1,3 +1,8 @@
+// Common variables
+:root {
+  --editor-color-primary: #206ea7;
+}
+
 // Main UI elements
 .editor-toolbar-main {
   display: flex;
@@ -72,4 +77,47 @@
 }
 .editor-modal-title {
   font-weight: 700;
-}
\ No newline at end of file
+}
+
+// In-editor elements
+.editor-image-wrap {
+  position: relative;
+  display: inline-flex;
+}
+.editor-image-decorator {
+  display: inline-block;
+  position: absolute;
+  border: 1px solid var(--editor-color-primary);
+  left: 0;
+  right: 0;
+  width: 100%;
+  height: 100%;
+}
+.editor-image-decorator-handle {
+  position: absolute;
+  display: block;
+  width: 10px;
+  height: 10px;
+  background-color: var(--editor-color-primary);
+  user-select: none;
+  &.nw {
+    inset-inline-start: -5px;
+    inset-block-start: -5px;
+    cursor: nw-resize;
+  }
+  &.ne {
+    inset-inline-end: -5px;
+    inset-block-start: -5px;
+    cursor: ne-resize;
+  }
+  &.se {
+    inset-inline-end: -5px;
+    inset-block-end: -5px;
+    cursor: se-resize;
+  }
+  &.sw {
+    inset-inline-start: -5px;
+    inset-block-end: -5px;
+    cursor: sw-resize;
+  }
+}