diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js
index f78835b45b..3260c97559 100644
--- a/src/components/structures/LeftPanel.js
+++ b/src/components/structures/LeftPanel.js
@@ -17,22 +17,31 @@ limitations under the License.
 'use strict';
 
 import React from 'react';
+import PropTypes from 'prop-types';
 import classNames from 'classnames';
+import { DragDropContext } from 'react-beautiful-dnd';
+import { MatrixClient } from 'matrix-js-sdk';
 import { KeyCode } from 'matrix-react-sdk/lib/Keyboard';
 import sdk from 'matrix-react-sdk';
 import dis from 'matrix-react-sdk/lib/dispatcher';
-import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
-import CallHandler from 'matrix-react-sdk/lib/CallHandler';
-import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
 import VectorConferenceHandler from '../../VectorConferenceHandler';
 
+import SettingsStore from 'matrix-react-sdk/lib/settings/SettingsStore';
+import TagOrderActions from 'matrix-react-sdk/lib/actions/TagOrderActions';
+import RoomListActions from 'matrix-react-sdk/lib/actions/RoomListActions';
+
+
 var LeftPanel = React.createClass({
     displayName: 'LeftPanel',
 
     // NB. If you add props, don't forget to update
     // shouldComponentUpdate!
     propTypes: {
-        collapsed: React.PropTypes.bool.isRequired,
+        collapsed: PropTypes.bool.isRequired,
+    },
+
+    contextTypes: {
+        matrixClient: PropTypes.instanceOf(MatrixClient),
     },
 
     getInitialState: function() {
@@ -161,13 +170,59 @@ var LeftPanel = React.createClass({
         this.setState({ searchFilter: term });
     },
 
+    onDragEnd: function(result) {
+        // Dragged to an invalid destination, not onto a droppable
+        if (!result.destination) {
+            return;
+        }
+
+        const dest = result.destination.droppableId;
+
+        if (dest === 'tag-panel-droppable') {
+            // Dispatch synchronously so that the TagPanel receives an
+            // optimistic update from TagOrderStore before the previous
+            // state is shown.
+            dis.dispatch(TagOrderActions.moveTag(
+                this.context.matrixClient,
+                result.draggableId,
+                result.destination.index,
+            ), true);
+        } else {
+            this.onRoomTileEndDrag(result);
+        }
+    },
+
+    onRoomTileEndDrag: function(result) {
+        let newTag = result.destination.droppableId.split('_')[1];
+        let prevTag = result.source.droppableId.split('_')[1];
+        if (newTag === 'undefined') newTag = undefined;
+        if (prevTag === 'undefined') prevTag = undefined;
+
+        const roomId = result.draggableId.split('_')[1];
+
+        const oldIndex = result.source.index;
+        const newIndex = result.destination.index;
+
+        dis.dispatch(RoomListActions.tagRoom(
+            this.context.matrixClient,
+            this.context.matrixClient.getRoom(roomId),
+            prevTag, newTag,
+            oldIndex, newIndex,
+        ), true);
+    },
+
+    collectRoomList: function(ref) {
+        this._roomList = ref;
+    },
+
     render: function() {
         const RoomList = sdk.getComponent('rooms.RoomList');
+        const TagPanel = sdk.getComponent('structures.TagPanel');
         const BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu');
         const CallPreview = sdk.getComponent('voip.CallPreview');
 
         let topBox;
-        if (MatrixClientPeg.get().isGuest()) {
+        if (this.context.matrixClient.isGuest()) {
             const LoginBox = sdk.getComponent('structures.LoginBox');
             topBox = <LoginBox collapsed={ this.props.collapsed }/>;
         } else {
@@ -184,15 +239,21 @@ var LeftPanel = React.createClass({
         );
 
         return (
-            <aside className={classes} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
-                { topBox }
-                <CallPreview ConferenceHandler={VectorConferenceHandler} />
-                <RoomList
-                    collapsed={this.props.collapsed}
-                    searchFilter={this.state.searchFilter}
-                    ConferenceHandler={VectorConferenceHandler} />
-                <BottomLeftMenu collapsed={this.props.collapsed}/>
-            </aside>
+            <DragDropContext onDragEnd={this.onDragEnd}>
+                <div className="mx_LeftPanel_container">
+                    { SettingsStore.isFeatureEnabled("feature_tag_panel") ? <TagPanel /> : <div /> }
+                    <aside className={classes} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
+                        { topBox }
+                        <CallPreview ConferenceHandler={VectorConferenceHandler} />
+                        <RoomList
+                            ref={this.collectRoomList}
+                            collapsed={this.props.collapsed}
+                            searchFilter={this.state.searchFilter}
+                            ConferenceHandler={VectorConferenceHandler} />
+                        <BottomLeftMenu collapsed={this.props.collapsed}/>
+                    </aside>
+                </div>
+            </DragDropContext>
         );
     }
 });
diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js
index 15f56c0069..d119e2714a 100644
--- a/src/components/structures/RoomSubList.js
+++ b/src/components/structures/RoomSubList.js
@@ -38,31 +38,6 @@ var debug = false;
 
 const TRUNCATE_AT = 10;
 
-var roomListTarget = {
-    canDrop: function() {
-        return true;
-    },
-
-    drop: function(props, monitor, component) {
-        if (debug) console.log("dropped on sublist")
-    },
-
-    hover: function(props, monitor, component) {
-        var item = monitor.getItem();
-
-        if (component.state.sortedList.length == 0 && props.editable) {
-            if (debug) console.log("hovering on sublist " + props.label + ", isOver=" + monitor.isOver());
-
-            if (item.targetList !== component) {
-                 item.targetList.removeRoomTile(item.room);
-                 item.targetList = component;
-            }
-
-            component.moveRoomTile(item.room, 0);
-        }
-    },
-};
-
 var RoomSubList = React.createClass({
     displayName: 'RoomSubList',
 
@@ -110,13 +85,17 @@ var RoomSubList = React.createClass({
     },
 
     componentWillMount: function() {
-        this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order);
+        this.setState({
+            sortedList: this.applySearchFilter(this.props.list, this.props.searchFilter),
+        });
     },
 
     componentWillReceiveProps: function(newProps) {
         // order the room list appropriately before we re-render
         //if (debug) console.log("received new props, list = " + newProps.list);
-        this.sortList(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order);
+        this.setState({
+            sortedList: this.applySearchFilter(newProps.list, newProps.searchFilter),
+        });
     },
 
     applySearchFilter: function(list, filter) {
@@ -164,71 +143,6 @@ var RoomSubList = React.createClass({
         });
     },
 
-    tsOfNewestEvent: function(room) {
-        for (var i = room.timeline.length - 1; i >= 0; --i) {
-            var ev = room.timeline[i];
-            if (ev.getTs() &&
-                (Unread.eventTriggersUnreadCount(ev) ||
-                (ev.getSender() === MatrixClientPeg.get().credentials.userId))
-            ) {
-                return ev.getTs();
-            }
-        }
-
-        // we might only have events that don't trigger the unread indicator,
-        // in which case use the oldest event even if normally it wouldn't count.
-        // This is better than just assuming the last event was forever ago.
-        if (room.timeline.length && room.timeline[0].getTs()) {
-            return room.timeline[0].getTs();
-        } else {
-            return Number.MAX_SAFE_INTEGER;
-        }
-    },
-
-    // TODO: factor the comparators back out into a generic comparator
-    // so that view_prev_room and view_next_room can do the right thing
-
-    recentsComparator: function(roomA, roomB) {
-        return this.tsOfNewestEvent(roomB) - this.tsOfNewestEvent(roomA);
-    },
-
-    lexicographicalComparator: function(roomA, roomB) {
-        return roomA.name > roomB.name ? 1 : -1;
-    },
-
-    // Generates the manual comparator using the given list
-    manualComparator: function(roomA, roomB) {
-        if (!roomA.tags[this.props.tagName] || !roomB.tags[this.props.tagName]) return 0;
-
-        // Make sure the room tag has an order element, if not set it to be the bottom
-        var a = roomA.tags[this.props.tagName].order;
-        var b = roomB.tags[this.props.tagName].order;
-
-        // Order undefined room tag orders to the bottom
-        if (a === undefined && b !== undefined) {
-            return 1;
-        } else if (a !== undefined && b === undefined) {
-            return -1;
-        }
-
-        return a == b ? this.lexicographicalComparator(roomA, roomB) : ( a > b  ? 1 : -1);
-    },
-
-    sortList: function(list, order) {
-        if (list === undefined) list = this.state.sortedList;
-        if (order === undefined) order = this.props.order;
-        var comparator;
-        list = list || [];
-        if (order === "manual") comparator = this.manualComparator;
-        if (order === "recent") comparator = this.recentsComparator;
-
-        // Fix undefined orders here, and make sure the backend gets updated as well
-        this._fixUndefinedOrder(list);
-
-        //if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list);
-        this.setState({ sortedList: list.sort(comparator) });
-    },
-
     _shouldShowNotifBadge: function(roomNotifState) {
         const showBadgeInStates = [RoomNotifs.ALL_MESSAGES, RoomNotifs.ALL_MESSAGES_LOUD];
         return showBadgeInStates.indexOf(roomNotifState) > -1;
@@ -279,98 +193,6 @@ var RoomSubList = React.createClass({
         this.setState(this.state);
     },
 
-    moveRoomTile: function(room, atIndex) {
-        if (debug) console.log("moveRoomTile: id " + room.roomId + ", atIndex " + atIndex);
-        //console.log("moveRoomTile before: " + JSON.stringify(this.state.rooms));
-        var found = this.findRoomTile(room);
-        var rooms = this.state.sortedList;
-        if (found.room) {
-            if (debug) console.log("removing at index " + found.index + " and adding at index " + atIndex);
-            rooms.splice(found.index, 1);
-            rooms.splice(atIndex, 0, found.room);
-        }
-        else {
-            if (debug) console.log("Adding at index " + atIndex);
-            rooms.splice(atIndex, 0, room);
-        }
-        this.setState({ sortedList: rooms });
-        // console.log("moveRoomTile after: " + JSON.stringify(this.state.rooms));
-    },
-
-    // XXX: this isn't invoked via a property method but indirectly via
-    // the roomList property method.  Unsure how evil this is.
-    removeRoomTile: function(room) {
-        if (debug) console.log("remove room " + room.roomId);
-        var found = this.findRoomTile(room);
-        var rooms = this.state.sortedList;
-        if (found.room) {
-            rooms.splice(found.index, 1);
-        }
-        else {
-            console.warn("Can't remove room " + room.roomId + " - can't find it");
-        }
-        this.setState({ sortedList: rooms });
-    },
-
-    findRoomTile: function(room) {
-        var index = this.state.sortedList.indexOf(room);
-        if (index >= 0) {
-            // console.log("found: room: " + room.roomId + " with index " + index);
-        }
-        else {
-            if (debug) console.log("didn't find room");
-            room = null;
-        }
-        return ({
-            room: room,
-            index: index,
-        });
-    },
-
-    calcManualOrderTagData: function(index) {
-        // we sort rooms by the lexicographic ordering of the 'order' metadata on their tags.
-        // for convenience, we calculate this for now a floating point number between 0.0 and 1.0.
-
-        let orderA = 0.0; // by default we're next to the beginning of the list
-        if (index > 0) {
-            const prevTag = this.state.sortedList[index - 1].tags[this.props.tagName];
-            if (!prevTag) {
-                console.error("Previous room in sublist is not tagged to be in this list. This should never happen.");
-            } else if (prevTag.order === undefined) {
-                console.error("Previous room in sublist has no ordering metadata. This should never happen.");
-            } else {
-                orderA = prevTag.order;
-            }
-        }
-
-        let orderB = 1.0; // by default we're next to the end of the list too
-        if (index < this.state.sortedList.length - 1) {
-            const nextTag = this.state.sortedList[index + 1].tags[this.props.tagName];
-            if (!nextTag) {
-                console.error("Next room in sublist is not tagged to be in this list. This should never happen.");
-            } else if (nextTag.order === undefined) {
-                console.error("Next room in sublist has no ordering metadata. This should never happen.");
-            } else {
-                orderB = nextTag.order;
-            }
-        }
-
-        const order = (orderA + orderB) / 2.0;
-
-        if (order === orderA || order === orderB) {
-            console.error("Cannot describe new list position.  This should be incredibly unlikely.");
-            this.state.sortedList.forEach((room, index) => {
-                MatrixClientPeg.get().setRoomTag(
-                    room.roomId, this.props.tagName,
-                    {order: index / this.state.sortedList.length},
-                );
-            });
-            return index / this.state.sortedList.length;
-        }
-
-        return order;
-    },
-
     makeRoomTiles: function() {
         var self = this;
         var DNDRoomTile = sdk.getComponent("rooms.DNDRoomTile");
@@ -497,47 +319,6 @@ var RoomSubList = React.createClass({
         this.props.onHeaderClick(false);
     },
 
-    // Fix any undefined order elements of a room in a manual ordered list
-    //     room.tag[tagname].order
-    _fixUndefinedOrder: function(list) {
-        if (this.props.order === "manual") {
-            var order = 0.0;
-            var self = this;
-
-            // Find the highest (lowest position) order of a room in a manual ordered list
-            list.forEach(function(room) {
-                if (room.tags.hasOwnProperty(self.props.tagName)) {
-                    if (order < room.tags[self.props.tagName].order) {
-                        order = room.tags[self.props.tagName].order;
-                    }
-                }
-            });
-
-            // Fix any undefined order elements of a room in a manual ordered list
-            // Do this one at a time, as each time a rooms tag data is updated the RoomList
-            // gets triggered and another list is passed in. Doing it one at a time means that
-            // we always correctly calculate the highest order for the list - stops multiple
-            // rooms getting the same order. This is only really relevant for the first time this
-            // is run with historical room tag data, after that there should only be undefined
-            // in the list at a time anyway.
-            for (let i = 0; i < list.length; i++) {
-                if (list[i].tags[self.props.tagName] && list[i].tags[self.props.tagName].order === undefined) {
-                    MatrixClientPeg.get().setRoomTag(list[i].roomId, self.props.tagName, {order: (order + 1.0) / 2.0}).finally(function() {
-                        // Do any final stuff here
-                    }).catch(function(err) {
-                        var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-                        console.error("Failed to add tag " + self.props.tagName + " to room" + err);
-                        Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, {
-                            title: _t('Failed to add tag %(tagName)s to room', {tagName: self.props.tagName}),
-                            description: ((err && err.message) ? err.message : _t('Operation failed')),
-                        });
-                    });
-                    break;
-                };
-            };
-        }
-    },
-
     render: function() {
         var connectDropTarget = this.props.connectDropTarget;
         var TruncatedList = sdk.getComponent('elements.TruncatedList');
@@ -572,13 +353,17 @@ var RoomSubList = React.createClass({
                 { subList }
             </div>;
 
-            return this.props.editable ? <Droppable droppableId={"room-sub-list-droppable_" + this.props.tagName}>
-                { (provided, snapshot) => (
-                    <div ref={provided.innerRef}>
-                        { subListContent }
-                    </div>
-                ) }
-            </Droppable> : subListContent;
+            return this.props.editable ?
+                <Droppable
+                    droppableId={"room-sub-list-droppable_" + this.props.tagName}
+                    type="draggable-RoomTile"
+                >
+                    { (provided, snapshot) => (
+                        <div ref={provided.innerRef}>
+                            { subListContent }
+                        </div>
+                    ) }
+                </Droppable> : subListContent;
         }
         else {
             var Loader = sdk.getComponent("elements.Spinner");
diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js
index 36602cfac6..06eb347d6c 100644
--- a/src/components/views/context_menus/RoomTileContextMenu.js
+++ b/src/components/views/context_menus/RoomTileContextMenu.js
@@ -20,6 +20,7 @@ limitations under the License.
 import Promise from 'bluebird';
 import React from 'react';
 import classNames from 'classnames';
+import PropTypes from 'prop-types';
 import sdk from 'matrix-react-sdk';
 import { _t, _td } from 'matrix-react-sdk/lib/languageHandler';
 import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
@@ -28,14 +29,15 @@ import DMRoomMap from 'matrix-react-sdk/lib/utils/DMRoomMap';
 import * as Rooms from 'matrix-react-sdk/lib/Rooms';
 import * as RoomNotifs from 'matrix-react-sdk/lib/RoomNotifs';
 import Modal from 'matrix-react-sdk/lib/Modal';
+import RoomListActions from 'matrix-react-sdk/lib/actions/RoomListActions';
 
 module.exports = React.createClass({
     displayName: 'RoomTileContextMenu',
 
     propTypes: {
-        room: React.PropTypes.object.isRequired,
+        room: PropTypes.object.isRequired,
         /* callback called when the menu is dismissed */
-        onFinished: React.PropTypes.func,
+        onFinished: PropTypes.func,
     },
 
     getInitialState() {
@@ -45,7 +47,7 @@ module.exports = React.createClass({
             isFavourite: this.props.room.tags.hasOwnProperty("m.favourite"),
             isLowPriority: this.props.room.tags.hasOwnProperty("m.lowpriority"),
             isDirectMessage: Boolean(dmRoomMap.getUserIdForRoomId(this.props.room.roomId)),
-        }
+        };
     },
 
     componentWillMount: function() {
@@ -57,42 +59,16 @@ module.exports = React.createClass({
     },
 
     _toggleTag: function(tagNameOn, tagNameOff) {
-        var self = this;
-        const roomId = this.props.room.roomId;
-        var cli = MatrixClientPeg.get();
-        if (!cli.isGuest()) {
-            Promise.delay(500).then(function() {
-                if (tagNameOff !== null && tagNameOff !== undefined) {
-                    cli.deleteRoomTag(roomId, tagNameOff).finally(function() {
-                        // Close the context menu
-                        if (self.props.onFinished) {
-                            self.props.onFinished();
-                        };
-                    }).catch(function(err) {
-                        var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-                        Modal.createTrackedDialog('Failed to remove tag from room 1', '', ErrorDialog, {
-                            title: _t('Failed to remove tag %(tagName)s from room', {tagName: tagNameOff}),
-                            description: ((err && err.message) ? err.message : _t('Operation failed')),
-                        });
-                    });
-                }
+        if (!MatrixClientPeg.get().isGuest()) {
+            Promise.delay(500).then(() => {
+                dis.dispatch(RoomListActions.tagRoom(
+                    MatrixClientPeg.get(),
+                    this.props.room,
+                    tagNameOff, tagNameOn,
+                    undefined, 0,
+                ), true);
 
-                if (tagNameOn !== null && tagNameOn !== undefined) {
-                    // If the tag ordering meta data is required, it is added by
-                    // the RoomSubList when it sorts its rooms
-                    cli.setRoomTag(roomId, tagNameOn, {}).finally(function() {
-                        // Close the context menu
-                        if (self.props.onFinished) {
-                            self.props.onFinished();
-                        };
-                    }).catch(function(err) {
-                        var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-                        Modal.createTrackedDialog('Failed to remove tag from room 2', '', ErrorDialog, {
-                            title: _t('Failed to remove tag %(tagName)s from room', {tagName: tagNameOn}),
-                            description: ((err && err.message) ? err.message : _t('Operation failed')),
-                        });
-                    });
-                }
+                this.props.onFinished();
             });
         }
     },
@@ -132,22 +108,22 @@ module.exports = React.createClass({
     },
 
     _onClickDM: function() {
+        if (MatrixClientPeg.get().isGuest()) return;
+
         const newIsDirectMessage = !this.state.isDirectMessage;
         this.setState({
             isDirectMessage: newIsDirectMessage,
         });
 
-        if (MatrixClientPeg.get().isGuest()) return;
-
         Rooms.guessAndSetDMRoom(
-            this.props.room, newIsDirectMessage
+            this.props.room, newIsDirectMessage,
         ).delay(500).finally(() => {
             // Close the context menu
             if (this.props.onFinished) {
                 this.props.onFinished();
-            };
+            }
         }, (err) => {
-            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
             Modal.createTrackedDialog('Failed to set Direct Message status of room', '', ErrorDialog, {
                 title: _t('Failed to set Direct Message status of room'),
                 description: ((err && err.message) ? err.message : _t('Operation failed')),
@@ -165,7 +141,7 @@ module.exports = React.createClass({
         // Close the context menu
         if (this.props.onFinished) {
             this.props.onFinished();
-        };
+        }
     },
 
     _onClickReject: function() {
@@ -177,7 +153,7 @@ module.exports = React.createClass({
         // Close the context menu
         if (this.props.onFinished) {
             this.props.onFinished();
-        };
+        }
     },
 
     _onClickForget: function() {
@@ -185,8 +161,8 @@ module.exports = React.createClass({
         MatrixClientPeg.get().forget(this.props.room.roomId).done(function() {
             dis.dispatch({ action: 'view_next_room' });
         }, function(err) {
-            var errCode = err.errcode || _td("unknown error code");
-            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+            const errCode = err.errcode || _td("unknown error code");
+            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
             Modal.createTrackedDialog('Failed to forget room', '', ErrorDialog, {
                 title: _t('Failed to forget room %(errCode)s', {errCode: errCode}),
                 description: ((err && err.message) ? err.message : _t('Operation failed')),
@@ -196,20 +172,19 @@ module.exports = React.createClass({
         // Close the context menu
         if (this.props.onFinished) {
             this.props.onFinished();
-        };
+        }
     },
 
     _saveNotifState: function(newState) {
+        if (MatrixClientPeg.get().isGuest()) return;
+
         const oldState = this.state.roomNotifState;
         const roomId = this.props.room.roomId;
-        var cli = MatrixClientPeg.get();
-
-        if (cli.isGuest()) return;
 
         this.setState({
             roomNotifState: newState,
         });
-        RoomNotifs.setRoomNotifsState(this.props.room.roomId, newState).done(() => {
+        RoomNotifs.setRoomNotifsState(roomId, newState).done(() => {
             // delay slightly so that the user can see their state change
             // before closing the menu
             return Promise.delay(500).then(() => {
@@ -217,7 +192,7 @@ module.exports = React.createClass({
                 // Close the context menu
                 if (this.props.onFinished) {
                     this.props.onFinished();
-                };
+                }
             });
         }, (error) => {
             // TODO: some form of error notification to the user
@@ -247,22 +222,22 @@ module.exports = React.createClass({
     },
 
     _renderNotifMenu: function() {
-        var alertMeClasses = classNames({
+        const alertMeClasses = classNames({
             'mx_RoomTileContextMenu_notif_field': true,
             'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES_LOUD,
         });
 
-        var allNotifsClasses = classNames({
+        const allNotifsClasses = classNames({
             'mx_RoomTileContextMenu_notif_field': true,
             'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.ALL_MESSAGES,
         });
 
-        var mentionsClasses = classNames({
+        const mentionsClasses = classNames({
             'mx_RoomTileContextMenu_notif_field': true,
             'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.MENTIONS_ONLY,
         });
 
-        var muteNotifsClasses = classNames({
+        const muteNotifsClasses = classNames({
             'mx_RoomTileContextMenu_notif_field': true,
             'mx_RoomTileContextMenu_notif_fieldSet': this.state.roomNotifState == RoomNotifs.MUTE,
         });
@@ -272,22 +247,22 @@ module.exports = React.createClass({
                 <div className="mx_RoomTileContextMenu_notif_picker" >
                     <img src="img/notif-slider.svg" width="20" height="107" />
                 </div>
-                <div className={ alertMeClasses } onClick={this._onClickAlertMe} >
+                <div className={alertMeClasses} onClick={this._onClickAlertMe} >
                     <img className="mx_RoomTileContextMenu_notif_activeIcon" src="img/notif-active.svg" width="12" height="12" />
                     <img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src="img/icon-context-mute-off-copy.svg" width="16" height="12" />
                     { _t('All messages (noisy)') }
                 </div>
-                <div className={ allNotifsClasses } onClick={this._onClickAllNotifs} >
+                <div className={allNotifsClasses} onClick={this._onClickAllNotifs} >
                     <img className="mx_RoomTileContextMenu_notif_activeIcon" src="img/notif-active.svg" width="12" height="12" />
                     <img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src="img/icon-context-mute-off.svg" width="16" height="12" />
                     { _t('All messages') }
                 </div>
-                <div className={ mentionsClasses } onClick={this._onClickMentions} >
+                <div className={mentionsClasses} onClick={this._onClickMentions} >
                     <img className="mx_RoomTileContextMenu_notif_activeIcon" src="img/notif-active.svg" width="12" height="12" />
                     <img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src="img/icon-context-mute-mentions.svg" width="16" height="12" />
                     { _t('Mentions only') }
                 </div>
-                <div className={ muteNotifsClasses } onClick={this._onClickMute} >
+                <div className={muteNotifsClasses} onClick={this._onClickMute} >
                     <img className="mx_RoomTileContextMenu_notif_activeIcon" src="img/notif-active.svg" width="12" height="12" />
                     <img className="mx_RoomTileContextMenu_notif_icon mx_filterFlipColor" src="img/icon-context-mute.svg" width="16" height="12" />
                     { _t('Mute') }
@@ -322,7 +297,7 @@ module.exports = React.createClass({
 
         return (
             <div>
-                <div className="mx_RoomTileContextMenu_leave" onClick={ leaveClickHandler } >
+                <div className="mx_RoomTileContextMenu_leave" onClick={leaveClickHandler} >
                     <img className="mx_RoomTileContextMenu_tag_icon" src="img/icon_context_delete.svg" width="15" height="15" />
                     { leaveText }
                 </div>
@@ -351,17 +326,17 @@ module.exports = React.createClass({
 
         return (
             <div>
-                <div className={ favouriteClasses } onClick={this._onClickFavourite} >
+                <div className={favouriteClasses} onClick={this._onClickFavourite} >
                     <img className="mx_RoomTileContextMenu_tag_icon" src="img/icon_context_fave.svg" width="15" height="15" />
                     <img className="mx_RoomTileContextMenu_tag_icon_set" src="img/icon_context_fave_on.svg" width="15" height="15" />
                     { _t('Favourite') }
                 </div>
-                <div className={ lowPriorityClasses } onClick={this._onClickLowPriority} >
+                <div className={lowPriorityClasses} onClick={this._onClickLowPriority} >
                     <img className="mx_RoomTileContextMenu_tag_icon" src="img/icon_context_low.svg" width="15" height="15" />
                     <img className="mx_RoomTileContextMenu_tag_icon_set" src="img/icon_context_low_on.svg" width="15" height="15" />
                     { _t('Low Priority') }
                 </div>
-                <div className={ dmClasses } onClick={this._onClickDM} >
+                <div className={dmClasses} onClick={this._onClickDM} >
                     <img className="mx_RoomTileContextMenu_tag_icon" src="img/icon_context_person.svg" width="15" height="15" />
                     <img className="mx_RoomTileContextMenu_tag_icon_set" src="img/icon_context_person_on.svg" width="15" height="15" />
                     { _t('Direct Chat') }
@@ -372,7 +347,7 @@ module.exports = React.createClass({
 
     render: function() {
         const myMember = this.props.room.getMember(
-            MatrixClientPeg.get().credentials.userId
+            MatrixClientPeg.get().credentials.userId,
         );
 
         // Can't set notif level or tags on non-join rooms
@@ -389,5 +364,5 @@ module.exports = React.createClass({
                 { this._renderRoomTagMenu() }
             </div>
         );
-    }
+    },
 });
diff --git a/src/components/views/rooms/DNDRoomTile.js b/src/components/views/rooms/DNDRoomTile.js
index 129e3f4597..b8f8b4026e 100644
--- a/src/components/views/rooms/DNDRoomTile.js
+++ b/src/components/views/rooms/DNDRoomTile.js
@@ -41,6 +41,7 @@ export default class DNDRoomTile extends React.Component {
                 key={props.room.roomId}
                 draggableId={props.tagName + '_' + props.room.roomId}
                 index={props.index}
+                type="draggable-RoomTile"
             >
                 { (provided, snapshot) => {
                     return (
diff --git a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss
index 8ae1fe152c..a2147a025d 100644
--- a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss
+++ b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss
@@ -21,6 +21,10 @@ limitations under the License.
     flex-direction: column;
 }
 
+.mx_LeftPanel_container {
+    display: flex;
+}
+
 .mx_LeftPanel_hideButton {
     position: absolute;
     top: 10px;