diff --git a/app/Config/setting-defaults.php b/app/Config/setting-defaults.php
index 4e7ba2fcb..5e1e4348a 100644
--- a/app/Config/setting-defaults.php
+++ b/app/Config/setting-defaults.php
@@ -29,8 +29,6 @@ return [
         'ui-shortcuts'          => '{}',
         'ui-shortcuts-enabled'  => false,
         'dark-mode-enabled'     => env('APP_DEFAULT_DARK_MODE', false),
-        'md-show-preview'       => true,
-        'md-scroll-sync'        => true,
         'bookshelves_view_type' => env('APP_VIEWS_BOOKSHELVES', 'grid'),
         'bookshelf_view_type'   => env('APP_VIEWS_BOOKSHELF', 'grid'),
         'books_view_type'       => env('APP_VIEWS_BOOKS', 'grid'),
diff --git a/app/Http/Controllers/UserPreferencesController.php b/app/Http/Controllers/UserPreferencesController.php
index 11ff338f3..560dd1632 100644
--- a/app/Http/Controllers/UserPreferencesController.php
+++ b/app/Http/Controllers/UserPreferencesController.php
@@ -139,25 +139,4 @@ class UserPreferencesController extends Controller
         setting()->putForCurrentUser('code-language-favourites', implode(',', $currentFavorites));
         return response('', 204);
     }
-
-    /**
-     * Update a boolean user preference setting.
-     */
-    public function updateBooleanPreference(Request $request)
-    {
-        $allowedKeys = ['md-scroll-sync', 'md-show-preview'];
-        $validated = $this->validate($request, [
-            'name'  => ['required', 'string'],
-            'value' => ['required'],
-        ]);
-
-        if (!in_array($validated['name'], $allowedKeys)) {
-            return response('Invalid boolean preference', 500);
-        }
-
-        $value = $validated['value'] === 'true' ? 'true' : 'false';
-        setting()->putForCurrentUser($validated['name'], $value);
-
-        return response('', 204);
-    }
 }
diff --git a/resources/js/components/markdown-editor.js b/resources/js/components/markdown-editor.js
index 373fedf48..4c3de91f6 100644
--- a/resources/js/components/markdown-editor.js
+++ b/resources/js/components/markdown-editor.js
@@ -14,7 +14,11 @@ export class MarkdownEditor extends Component {
 
         this.display = this.$refs.display;
         this.input = this.$refs.input;
-        this.settingContainer = this.$refs.settingContainer;
+        this.divider = this.$refs.divider;
+        this.displayWrap = this.$refs.displayWrap;
+
+        const settingContainer = this.$refs.settingContainer;
+        const settingInputs = settingContainer.querySelectorAll('input[type="checkbox"]');
 
         this.editor = null;
         initEditor({
@@ -23,11 +27,11 @@ export class MarkdownEditor extends Component {
             displayEl: this.display,
             inputEl: this.input,
             drawioUrl: this.getDrawioUrl(),
+            settingInputs: Array.from(settingInputs),
             text: {
                 serverUploadLimit: this.serverUploadLimitText,
                 imageUploadError: this.imageUploadErrorText,
             },
-            settings: this.loadSettings(),
         }).then(editor => {
             this.editor = editor;
             this.setupListeners();
@@ -76,30 +80,40 @@ export class MarkdownEditor extends Component {
             toolbarLabel.closest('.markdown-editor-wrap').classList.add('active');
         });
 
-        // Setting changes
-        this.settingContainer.addEventListener('change', e => {
-            const actualInput = e.target.parentNode.querySelector('input[type="hidden"]');
-            const name = actualInput.getAttribute('name');
-            const value = actualInput.getAttribute('value');
-            window.$http.patch('/preferences/update-boolean', {name, value});
-            this.editor.settings.set(name, value === 'true');
-        });
-
         // Refresh CodeMirror on container resize
         const resizeDebounced = debounce(() => this.editor.cm.refresh(), 100, false);
         const observer = new ResizeObserver(resizeDebounced);
         observer.observe(this.elem);
+
+        this.handleDividerDrag();
     }
 
-    loadSettings() {
-        const settings = {};
-        const inputs = this.settingContainer.querySelectorAll('input[type="hidden"]');
+    handleDividerDrag() {
+        this.divider.addEventListener('pointerdown', event => {
+            const wrapRect = this.elem.getBoundingClientRect();
+            const moveListener = (event) => {
+                const xRel = event.pageX - wrapRect.left;
+                const xPct = Math.min(Math.max(20, Math.floor((xRel / wrapRect.width) * 100)), 80);
+                this.displayWrap.style.flexBasis = `${100-xPct}%`;
+                this.editor.settings.set('editorWidth', xPct);
+            };
+            const upListener = (event) => {
+                window.removeEventListener('pointermove', moveListener);
+                window.removeEventListener('pointerup', upListener);
+                this.display.style.pointerEvents = null;
+                document.body.style.userSelect = null;
+                this.editor.cm.refresh();
+            };
 
-        for (const input of inputs) {
-            settings[input.getAttribute('name')] = input.value === 'true';
+            this.display.style.pointerEvents = 'none';
+            document.body.style.userSelect = 'none';
+            window.addEventListener('pointermove', moveListener);
+            window.addEventListener('pointerup', upListener);
+        });
+        const widthSetting = this.editor.settings.get('editorWidth');
+        if (widthSetting) {
+            this.displayWrap.style.flexBasis = `${100-widthSetting}%`;
         }
-
-        return settings;
     }
 
     scrollToTextIfNeeded() {
diff --git a/resources/js/markdown/editor.js b/resources/js/markdown/editor.js
index f2e34b9f0..1cf4cef2b 100644
--- a/resources/js/markdown/editor.js
+++ b/resources/js/markdown/editor.js
@@ -19,7 +19,7 @@ export async function init(config) {
     const editor = {
         config,
         markdown: new Markdown(),
-        settings: new Settings(config.settings),
+        settings: new Settings(config.settingInputs),
     };
 
     editor.actions = new Actions(editor);
@@ -39,8 +39,8 @@ export async function init(config) {
  * @property {Element} displayEl
  * @property {HTMLTextAreaElement} inputEl
  * @property {String} drawioUrl
+ * @property {HTMLInputElement[]} settingInputs
  * @property {Object<String, String>} text
- * @property {Object<String, any>} settings
  */
 
 /**
diff --git a/resources/js/markdown/settings.js b/resources/js/markdown/settings.js
index 6dd142210..62aab82e9 100644
--- a/resources/js/markdown/settings.js
+++ b/resources/js/markdown/settings.js
@@ -1,40 +1,62 @@
-import {kebabToCamel} from "../services/text";
-
-
 export class Settings {
 
-    constructor(initialSettings) {
-        this.settingMap = {};
+    constructor(settingInputs) {
+        this.settingMap = {
+            scrollSync: true,
+            showPreview: true,
+            editorWidth: 50,
+        };
         this.changeListeners = {};
-        this.merge(initialSettings);
+        this.loadFromLocalStorage();
+        this.applyToInputs(settingInputs);
+        this.listenToInputChanges(settingInputs);
+    }
+
+    applyToInputs(inputs) {
+        for (const input of inputs) {
+            const name = input.getAttribute('name').replace('md-', '');
+            input.checked = this.settingMap[name];
+        }
+    }
+
+    listenToInputChanges(inputs) {
+        for (const input of inputs) {
+            input.addEventListener('change', event => {
+                const name = input.getAttribute('name').replace('md-', '');
+                this.set(name, input.checked);
+            });
+        }
+    }
+
+    loadFromLocalStorage() {
+        const lsValString = window.localStorage.getItem('md-editor-settings');
+        if (!lsValString) {
+            return;
+        }
+
+        const lsVals = JSON.parse(lsValString);
+        for (const [key, value] of Object.entries(lsVals)) {
+            if (value !== null && this.settingMap[key] !== undefined) {
+                this.settingMap[key] = value;
+            }
+        }
     }
 
     set(key, value) {
-        key = this.normaliseKey(key);
         this.settingMap[key] = value;
+        window.localStorage.setItem('md-editor-settings', JSON.stringify(this.settingMap));
         for (const listener of (this.changeListeners[key] || [])) {
             listener(value);
         }
     }
 
     get(key) {
-        return this.settingMap[this.normaliseKey(key)] || null;
-    }
-
-    merge(settings) {
-        for (const [key, value] of Object.entries(settings)) {
-            this.set(key, value);
-        }
+        return this.settingMap[key] || null;
     }
 
     onChange(key, callback) {
-        key = this.normaliseKey(key);
-        const listeners = this.changeListeners[this.normaliseKey(key)] || [];
+        const listeners = this.changeListeners[key] || [];
         listeners.push(callback);
-        this.changeListeners[this.normaliseKey(key)] = listeners;
-    }
-
-    normaliseKey(key) {
-        return kebabToCamel(key.replace('md-', ''));
+        this.changeListeners[key] = listeners;
     }
 }
\ No newline at end of file
diff --git a/resources/sass/_forms.scss b/resources/sass/_forms.scss
index e4331a03f..ef14f6221 100644
--- a/resources/sass/_forms.scss
+++ b/resources/sass/_forms.scss
@@ -60,10 +60,6 @@
       outline: 0;
     }
   }
-  .markdown-display, .markdown-editor-wrap {
-    flex: 1;
-    position: relative;
-  }
   &.fullscreen {
     position: fixed;
     top: 0;
@@ -74,17 +70,22 @@
 }
 
 .markdown-editor-wrap {
-  display: flex;
-  flex-direction: column;
   border-top: 1px solid #DDD;
   border-bottom: 1px solid #DDD;
   @include lightDark(border-color, #ddd, #000);
-  width: 50%;
+  position: relative;
+  flex: 1;
+}
+.markdown-editor-wrap + .markdown-editor-wrap {
+  flex-basis: 50%;
+  flex-shrink: 0;
+  flex-grow: 0;
 }
 
-.markdown-editor-wrap + .markdown-editor-wrap {
-  border-inline-start: 1px solid;
-  @include lightDark(border-color, #ddd, #000);
+.markdown-panel-divider {
+  width: 2px;
+  @include lightDark(background-color, #ddd, #000);
+  cursor: col-resize;
 }
 
 @include smaller-than($m) {
@@ -95,6 +96,7 @@
     width: 100%;
     max-width: 100%;
     flex-grow: 1;
+    flex-basis: auto !important;
   }
   .editor-toolbar-label {
     float: none !important;
diff --git a/resources/views/pages/parts/markdown-editor.blade.php b/resources/views/pages/parts/markdown-editor.blade.php
index f90a2f54c..fd8a20a04 100644
--- a/resources/views/pages/parts/markdown-editor.blade.php
+++ b/resources/views/pages/parts/markdown-editor.blade.php
@@ -5,7 +5,7 @@
      option:markdown-editor:server-upload-limit-text="{{ trans('errors.server_upload_limit') }}"
      class="flex-fill flex code-fill">
 
-    <div class="markdown-editor-wrap active">
+    <div class="markdown-editor-wrap active flex-container-column">
         <div class="editor-toolbar flex-container-row items-stretch justify-space-between">
             <div class="editor-toolbar-label text-mono px-m py-xs flex-container-row items-center flex">
                 <span>{{ trans('entities.pages_md_editor') }}</span>
@@ -20,11 +20,11 @@
                 <button refs="dropdown@toggle" class="text-button" type="button" title="{{ trans('common.more') }}">@icon('more')</button>
                 <div refs="dropdown@menu markdown-editor@setting-container" class="dropdown-menu" role="menu">
                     <div class="px-m">
-                        @include('form.toggle-switch', ['name' => 'md-show-preview', 'label' => trans('entities.pages_md_show_preview'), 'value' => setting()->getForCurrentUser('md-show-preview')])
+                        @include('form.custom-checkbox', ['name' => 'md-showPreview', 'label' => trans('entities.pages_md_show_preview'), 'value' => true, 'checked' => true])
                     </div>
                     <hr class="m-none">
                     <div class="px-m">
-                        @include('form.toggle-switch', ['name' => 'md-scroll-sync', 'label' => trans('entities.pages_md_sync_scroll'), 'value' => setting()->getForCurrentUser('md-scroll-sync')])
+                        @include('form.custom-checkbox', ['name' => 'md-scrollSync', 'label' => trans('entities.pages_md_sync_scroll'), 'value' => true, 'checked' => true])
                     </div>
                 </div>
             </div>
@@ -40,14 +40,17 @@
 
     </div>
 
-    <div class="markdown-editor-wrap" @if(!setting()->getForCurrentUser('md-show-preview')) style="display: none;" @endif>
-        <div class="editor-toolbar">
-            <div class="editor-toolbar-label text-mono px-m py-xs">{{ trans('entities.pages_md_preview') }}</div>
+    <div refs="markdown-editor@display-wrap" class="markdown-editor-wrap flex-container-row items-stretch" style="display: none">
+        <div refs="markdown-editor@divider" class="markdown-panel-divider flex-fill"></div>
+        <div class="flex-container-column flex flex-fill">
+            <div class="editor-toolbar">
+                <div class="editor-toolbar-label text-mono px-m py-xs">{{ trans('entities.pages_md_preview') }}</div>
+            </div>
+            <iframe src="about:blank"
+                    refs="markdown-editor@display"
+                    class="markdown-display flex flex-fill"
+                    sandbox="allow-same-origin"></iframe>
         </div>
-        <iframe src="about:blank"
-                refs="markdown-editor@display"
-                class="markdown-display"
-                sandbox="allow-same-origin"></iframe>
     </div>
 </div>
 
diff --git a/tests/User/UserPreferencesTest.php b/tests/User/UserPreferencesTest.php
index c8f4d2754..03dad7990 100644
--- a/tests/User/UserPreferencesTest.php
+++ b/tests/User/UserPreferencesTest.php
@@ -191,22 +191,4 @@ class UserPreferencesTest extends TestCase
         $resp = $this->get($page->getUrl('/edit'));
         $resp->assertSee('option:code-editor:favourites="javascript,ruby"', false);
     }
-
-    public function test_update_boolean()
-    {
-        $editor = $this->getEditor();
-
-        $this->assertTrue(setting()->getUser($editor, 'md-show-preview'));
-
-        $resp = $this->actingAs($editor)->patch('/preferences/update-boolean', ['name' => 'md-show-preview', 'value' => 'false']);
-        $resp->assertStatus(204);
-
-        $this->assertFalse(setting()->getUser($editor, 'md-show-preview'));
-    }
-
-    public function test_update_boolean_rejects_unfamiliar_key()
-    {
-        $resp = $this->asEditor()->patch('/preferences/update-boolean', ['name' => 'md-donkey-show', 'value' => 'false']);
-        $resp->assertStatus(500);
-    }
 }