diff --git a/resources/js/wysiwyg/actions.ts b/resources/js/wysiwyg/actions.ts
new file mode 100644
index 000000000..bca31e51b
--- /dev/null
+++ b/resources/js/wysiwyg/actions.ts
@@ -0,0 +1,26 @@
+import {$getRoot, LexicalEditor} from "lexical";
+import {$generateHtmlFromNodes, $generateNodesFromDOM} from "@lexical/html";
+
+
+export function setEditorContentFromHtml(editor: LexicalEditor, html: string) {
+    const parser = new DOMParser();
+    const dom = parser.parseFromString(html, 'text/html');
+
+    editor.update(() => {
+        const nodes = $generateNodesFromDOM(editor, dom);
+        const root = $getRoot();
+        for (const child of root.getChildren()) {
+            child.remove(true);
+        }
+        root.append(...nodes);
+    });
+}
+
+export function getEditorContentAsHtml(editor: LexicalEditor): Promise<string> {
+    return new Promise((resolve, reject) => {
+        editor.getEditorState().read(() => {
+            const html = $generateHtmlFromNodes(editor);
+            resolve(html);
+        });
+    });
+}
\ No newline at end of file
diff --git a/resources/js/wysiwyg/index.ts b/resources/js/wysiwyg/index.ts
index 0dcbf27f5..41207b706 100644
--- a/resources/js/wysiwyg/index.ts
+++ b/resources/js/wysiwyg/index.ts
@@ -1,10 +1,10 @@
-import {$getRoot, createEditor, CreateEditorArgs} from 'lexical';
+import {createEditor, CreateEditorArgs} from 'lexical';
 import {createEmptyHistoryState, registerHistory} from '@lexical/history';
 import {registerRichText} from '@lexical/rich-text';
 import {mergeRegister} from '@lexical/utils';
-import {$generateNodesFromDOM} from '@lexical/html';
 import {getNodesForPageEditor} from './nodes';
 import {buildEditorUI} from "./ui";
+import {setEditorContentFromHtml} from "./actions";
 
 export function createPageEditorInstance(editArea: HTMLElement) {
     const config: CreateEditorArgs = {
@@ -14,8 +14,6 @@ export function createPageEditorInstance(editArea: HTMLElement) {
     };
 
     const startingHtml = editArea.innerHTML;
-    const parser = new DOMParser();
-    const dom = parser.parseFromString(startingHtml, 'text/html');
 
     const editor = createEditor(config);
     editor.setRootElement(editArea);
@@ -25,11 +23,7 @@ export function createPageEditorInstance(editArea: HTMLElement) {
         registerHistory(editor, createEmptyHistoryState(), 300),
     );
 
-    editor.update(() => {
-        const startingNodes = $generateNodesFromDOM(editor, dom);
-        const root = $getRoot();
-        root.append(...startingNodes);
-    });
+    setEditorContentFromHtml(editor, startingHtml);
 
     const debugView = document.getElementById('lexical-debug');
     editor.registerUpdateListener(({editorState}) => {
diff --git a/resources/js/wysiwyg/ui/defaults/button-definitions.ts b/resources/js/wysiwyg/ui/defaults/button-definitions.ts
index e549e69a2..077bcae21 100644
--- a/resources/js/wysiwyg/ui/defaults/button-definitions.ts
+++ b/resources/js/wysiwyg/ui/defaults/button-definitions.ts
@@ -28,6 +28,7 @@ import {EditorUiContext} from "../framework/core";
 import {$isImageNode, ImageNode} from "../../nodes/image";
 import {$createDetailsNode, $isDetailsNode} from "../../nodes/details";
 import {$insertNodeToNearestRoot} from "@lexical/utils";
+import {getEditorContentAsHtml} from "../../actions";
 
 export const undo: EditorButtonDefinition = {
     label: 'Undo',
@@ -230,3 +231,14 @@ export const details: EditorButtonDefinition = {
     }
 }
 
+export const source: EditorButtonDefinition = {
+    label: 'Source code',
+    async action(context: EditorUiContext) {
+        const modal = context.manager.createModal('source');
+        const source = await getEditorContentAsHtml(context.editor);
+        modal.show({source});
+    },
+    isActive() {
+        return false;
+    }
+};
diff --git a/resources/js/wysiwyg/ui/defaults/form-definitions.ts b/resources/js/wysiwyg/ui/defaults/form-definitions.ts
index 13e7a9c9f..04147a4f0 100644
--- a/resources/js/wysiwyg/ui/defaults/form-definitions.ts
+++ b/resources/js/wysiwyg/ui/defaults/form-definitions.ts
@@ -3,6 +3,7 @@ import {EditorUiContext} from "../framework/core";
 import {$createLinkNode} from "@lexical/link";
 import {$createTextNode, $getSelection} from "lexical";
 import {$createImageNode} from "../../nodes/image";
+import {setEditorContentFromHtml} from "../../actions";
 
 
 export const link: EditorFormDefinition = {
@@ -86,4 +87,19 @@ export const image: EditorFormDefinition = {
             type: 'text',
         },
     ],
+};
+
+export const source: EditorFormDefinition = {
+    submitText: 'Save',
+    action(formData, context: EditorUiContext) {
+        setEditorContentFromHtml(context.editor, formData.get('source')?.toString() || '');
+        return true;
+    },
+    fields: [
+        {
+            label: 'Source',
+            name: 'source',
+            type: 'textarea',
+        },
+    ],
 };
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/framework/forms.ts b/resources/js/wysiwyg/ui/framework/forms.ts
index c6338f798..a7fcb45ba 100644
--- a/resources/js/wysiwyg/ui/framework/forms.ts
+++ b/resources/js/wysiwyg/ui/framework/forms.ts
@@ -5,7 +5,7 @@ import {el} from "../../helpers";
 export interface EditorFormFieldDefinition {
     label: string;
     name: string;
-    type: 'text' | 'select';
+    type: 'text' | 'select' | 'textarea';
 }
 
 export interface EditorSelectFormFieldDefinition extends EditorFormFieldDefinition {
@@ -28,7 +28,7 @@ export class EditorFormField extends EditorUiElement {
     }
 
     setValue(value: string) {
-        const input = this.getDOMElement().querySelector('input,select') as HTMLInputElement;
+        const input = this.getDOMElement().querySelector('input,select,textarea') as HTMLInputElement;
         input.value = value;
     }
 
@@ -45,6 +45,8 @@ export class EditorFormField extends EditorUiElement {
             const labels = Object.keys(options);
             const optionElems = labels.map(label => el('option', {value: options[label]}, [label]));
             input = el('select', {id, name: this.definition.name, class: 'editor-form-field-input'}, optionElems);
+        } else if (this.definition.type === 'textarea') {
+            input = el('textarea', {id, name: this.definition.name, class: 'editor-form-field-input'});
         } else {
             input = el('input', {id, name: this.definition.name, class: 'editor-form-field-input'});
         }
diff --git a/resources/js/wysiwyg/ui/index.ts b/resources/js/wysiwyg/ui/index.ts
index 8227dec68..b2fd7e05a 100644
--- a/resources/js/wysiwyg/ui/index.ts
+++ b/resources/js/wysiwyg/ui/index.ts
@@ -6,7 +6,7 @@ import {
 } from "lexical";
 import {getMainEditorFullToolbar} from "./toolbars";
 import {EditorUIManager} from "./framework/manager";
-import {image as imageFormDefinition, link as linkFormDefinition} from "./defaults/form-definitions";
+import {image as imageFormDefinition, link as linkFormDefinition, source as sourceFormDefinition} from "./defaults/form-definitions";
 import {DecoratorListener} from "lexical/LexicalEditor";
 import type {NodeKey} from "lexical/LexicalNode";
 import {EditorDecoratorAdapter} from "./framework/decorator";
@@ -36,7 +36,11 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
     manager.registerModal('image', {
         title: 'Insert/Edit Image',
         form: imageFormDefinition
-    })
+    });
+    manager.registerModal('source', {
+        title: 'Source code',
+        form: sourceFormDefinition,
+    });
 
     // Register decorator listener
     // Maybe move to manager?
diff --git a/resources/js/wysiwyg/ui/toolbars.ts b/resources/js/wysiwyg/ui/toolbars.ts
index b5d151fc1..63ff8a053 100644
--- a/resources/js/wysiwyg/ui/toolbars.ts
+++ b/resources/js/wysiwyg/ui/toolbars.ts
@@ -4,7 +4,7 @@ import {
     dangerCallout, details,
     h2, h3, h4, h5, image,
     infoCallout, italic, link, paragraph,
-    redo, strikethrough, subscript,
+    redo, source, strikethrough, subscript,
     successCallout, superscript, underline,
     undo,
     warningCallout
@@ -42,5 +42,7 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
         new EditorButton(link),
         new EditorButton(image),
         new EditorButton(details),
+
+        new EditorButton(source),
     ]);
 }
\ No newline at end of file