diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 758d3500dd..af20b25421 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -38,6 +38,7 @@ module.exports = React.createClass({
     getInitialState: function() {
         return {
             activityMap: null,
+            isLoadingLeftRooms: false,
             lists: {},
         }
     },
@@ -89,20 +90,24 @@ module.exports = React.createClass({
     },
 
     onRoom: function(room) {
-        this.refreshRoomList();
+        this._delayedRefreshRoomList();
     },
 
     onDeleteRoom: function(roomId) {
-        this.refreshRoomList();
+        this._delayedRefreshRoomList();
     },
 
     onArchivedHeaderClick: function(isHidden) {
         if (!isHidden) {
+            var self = this;
+            this.setState({ isLoadingLeftRooms: true });
             // we don't care about the response since it comes down via "Room"
             // events.
             MatrixClientPeg.get().syncLeftRooms().catch(function(err) {
                 console.error("Failed to sync left rooms: %s", err);
                 console.error(err);
+            }).finally(function() {
+                self.setState({ isLoadingLeftRooms: false });
             });
         }
     },
@@ -143,22 +148,57 @@ module.exports = React.createClass({
     },
 
     onRoomName: function(room) {
-        this.refreshRoomList();
+        this._delayedRefreshRoomList();
     },
 
     onRoomTags: function(event, room) {
-        this.refreshRoomList();        
+        this._delayedRefreshRoomList();
     },
 
     onRoomStateEvents: function(ev, state) {
-        setTimeout(this.refreshRoomList, 0);
+        this._delayedRefreshRoomList();
     },
 
     onRoomMemberName: function(ev, member) {
-        setTimeout(this.refreshRoomList, 0);
+        this._delayedRefreshRoomList();
+    },
+
+    _delayedRefreshRoomList: function() {
+        // There can be 1000s of JS SDK events when rooms are initially synced;
+        // we don't want to do lots of work rendering until things have settled.
+        // Therefore, keep a 1s refresh buffer which will refresh the room list
+        // at MOST once every 1s to prevent thrashing.
+        var MAX_REFRESH_INTERVAL_MS = 1000;
+        var self = this;
+
+        if (!self._lastRefreshRoomListTs) {
+            self.refreshRoomList(); // first refresh evar
+        }
+        else {
+            var timeWaitedMs = Date.now() - self._lastRefreshRoomListTs;
+            if (timeWaitedMs > MAX_REFRESH_INTERVAL_MS) {
+                clearTimeout(self._refreshRoomListTimerId);
+                self._refreshRoomListTimerId = null;
+                self.refreshRoomList(); // refreshed more than MAX_REFRESH_INTERVAL_MS ago
+            }
+            else {
+                // refreshed less than MAX_REFRESH_INTERVAL_MS ago, wait the difference
+                // if we aren't already waiting. If we are waiting then NOP, it will
+                // fire soon, promise!
+                if (!self._refreshRoomListTimerId) {
+                    self._refreshRoomListTimerId = setTimeout(function() {
+                        self.refreshRoomList();
+                    }, 10 + MAX_REFRESH_INTERVAL_MS - timeWaitedMs); // 10 is a buffer amount
+                }
+            }
+        }
     },
 
     refreshRoomList: function() {
+        // console.log("DEBUG: Refresh room list delta=%s ms",
+        //     (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs))
+        // );
+
         // TODO: rather than bluntly regenerating and re-sorting everything
         // every time we see any kind of room change from the JS SDK
         // we could do incremental updates on our copy of the state
@@ -166,6 +206,7 @@ module.exports = React.createClass({
         // us re-rendering all the sublists every time anything changes anywhere
         // in the state of the client.
         this.setState(this.getRoomLists());
+        this._lastRefreshRoomListTs = Date.now();
     },
 
     getRoomLists: function() {
@@ -320,6 +361,8 @@ module.exports = React.createClass({
                              selectedRoom={ self.props.selectedRoom }
                              collapsed={ self.props.collapsed }
                              alwaysShowHeader={ true }
+                             startAsHidden={ true }
+                             showSpinner={ self.state.isLoadingLeftRooms }
                              onHeaderClick= { self.onArchivedHeaderClick } />
             </div>
             </GeminiScrollbar>