-                
+            
+                
                 { this._canUserModify() && addWidget }
             
         );
     }
 }
+
+const PersistentVResizer = ({
+    id,
+    minHeight,
+    maxHeight,
+    className,
+    handleWrapperClass,
+    handleClass,
+    resizeNotifier,
+    children,
+}) => {
+    const [height, setHeight] = useLocalStorageState("pvr_" + id, 100);
+    const [resizing, setResizing] = useState(false);
+
+    return 
 {
+            if (!resizing) setResizing(true);
+            resizeNotifier.startResizing();
+        }}
+        onResize={() => {
+            resizeNotifier.notifyTimelineHeightChanged();
+        }}
+        onResizeStop={(e, dir, ref, d) => {
+            setHeight(height + d.height);
+            if (resizing) setResizing(false);
+            resizeNotifier.stopResizing();
+        }}
+        handleWrapperClass={handleWrapperClass}
+        handleClasses={{bottom: handleClass}}
+        className={classNames(className, {
+            mx_AppsDrawer_resizing: resizing,
+        })}
+        enable={{bottom: true}}
+    >
+        { children }
+    ;
+};
diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js
index fc31d66160..1f6f104487 100644
--- a/src/components/views/rooms/AuxPanel.js
+++ b/src/components/views/rooms/AuxPanel.js
@@ -204,6 +204,7 @@ export default class AuxPanel extends React.Component {
             maxHeight={this.props.maxHeight}
             showApps={this.props.showApps}
             hide={this.props.hideAppsDrawer}
+            resizeNotifier={this.props.resizeNotifier}
         />;
 
         let stateViews = null;
diff --git a/src/hooks/useLocalStorageState.ts b/src/hooks/useLocalStorageState.ts
new file mode 100644
index 0000000000..ce3b574f86
--- /dev/null
+++ b/src/hooks/useLocalStorageState.ts
@@ -0,0 +1,44 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import {Dispatch, SetStateAction, useCallback, useEffect, useState} from "react";
+
+const getValue = 
(key: string, initialValue: T): T => {
+    try {
+        const item = window.localStorage.getItem(key);
+        return item ? JSON.parse(item) : initialValue;
+    } catch (error) {
+        return initialValue;
+    }
+};
+
+// Hook behaving like useState but persisting the value to localStorage. Returns same as useState
+export const useLocalStorageState = (key: string, initialValue: T) => {
+    const lsKey = "mx_" + key;
+
+    const [value, setValue] = useState(getValue(lsKey, initialValue));
+
+    useEffect(() => {
+        setValue(getValue(lsKey, initialValue));
+    }, [lsKey, initialValue]);
+
+    const _setValue: Dispatch> = useCallback((v: T) => {
+        window.localStorage.setItem(lsKey, JSON.stringify(v));
+        setValue(v);
+    }, [lsKey]);
+
+    return [value, _setValue];
+};
diff --git a/src/resizer/resizer.js b/src/resizer/resizer.js
index 2234fc5bdf..1e75bf3bdf 100644
--- a/src/resizer/resizer.js
+++ b/src/resizer/resizer.js
@@ -105,6 +105,9 @@ export default class Resizer {
         if (this.classNames.resizing) {
             this.container.classList.add(this.classNames.resizing);
         }
+        if (this.config.onResizeStart) {
+            this.config.onResizeStart();
+        }
 
         const {sizer, distributor} = this._createSizerAndDistributor(resizeHandle);
         distributor.start();
@@ -119,6 +122,9 @@ export default class Resizer {
             if (this.classNames.resizing) {
                 this.container.classList.remove(this.classNames.resizing);
             }
+            if (this.config.onResizeStop) {
+                this.config.onResizeStop();
+            }
             distributor.finish();
             body.removeEventListener("mouseup", finishResize, false);
             document.removeEventListener("mouseleave", finishResize, false);
diff --git a/src/resizer/sizer.js b/src/resizer/sizer.js
index 50861d34d5..4ce9232457 100644
--- a/src/resizer/sizer.js
+++ b/src/resizer/sizer.js
@@ -56,6 +56,18 @@ export default class Sizer {
         return this.vertical ? this.container.offsetTop : this.container.offsetLeft;
     }
 
+    /** @return {number} container offset to document */
+    _getPageOffset() {
+        let element = this.container;
+        let offset = 0;
+        while (element) {
+            const pos = this.vertical ? element.offsetTop : element.offsetLeft;
+            offset = offset + pos;
+            element = element.offsetParent;
+        }
+        return offset;
+    }
+
     setItemSize(item, size) {
         if (this.vertical) {
             item.style.height = `${Math.round(size)}px`;
@@ -80,9 +92,9 @@ export default class Sizer {
     offsetFromEvent(event) {
         const pos = this.vertical ? event.pageY : event.pageX;
         if (this.reverse) {
-            return (this._getOffset() + this.getTotalSize()) - pos;
+            return (this._getPageOffset() + this.getTotalSize()) - pos;
         } else {
-            return pos - this._getOffset();
+            return pos - this._getPageOffset();
         }
     }
 }
diff --git a/src/utils/ResizeNotifier.js b/src/utils/ResizeNotifier.js
index 5467716576..512946828b 100644
--- a/src/utils/ResizeNotifier.js
+++ b/src/utils/ResizeNotifier.js
@@ -31,6 +31,19 @@ export default class ResizeNotifier extends EventEmitter {
         // with default options, will call fn once at first call, and then every x ms
         // if there was another call in that timespan
         this._throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200);
+        this._isResizing = false;
+    }
+
+    get isResizing() {
+        return this._isResizing;
+    }
+
+    startResizing() {
+        this._isResizing = true;
+    }
+
+    stopResizing() {
+        this._isResizing = false;
     }
 
     _noisyMiddlePanel() {