diff --git a/package.json b/package.json
index 33a1f3f966a23be3af6f278987eae7432ef08dc6..1084185ac7442c85504ed6101a6aedf662784284 100644
--- a/package.json
+++ b/package.json
@@ -19,15 +19,16 @@
     "prepublish": "npm run build"
   },
   "dependencies": {
-    "glob": "^5.0.14",
-    "optimist": "^0.6.1",
     "classnames": "^2.1.2",
     "filesize": "^3.1.2",
     "flux": "^2.0.3",
+    "glob": "^5.0.14",
+    "linkifyjs": "^2.0.0-beta.4",
     "matrix-js-sdk": "0.2.0",
+    "optimist": "^0.6.1",
     "q": "^1.4.1",
     "react": "^0.13.3",
-    "linkifyjs": "^2.0.0-beta.4"
+    "react-loader": "^1.4.0"
   },
   "devDependencies": {
     "babel": "^5.8.23",
diff --git a/src/Modal.js b/src/Modal.js
new file mode 100644
index 0000000000000000000000000000000000000000..d18b39e68b44229806c20f8771f1882c29230d75
--- /dev/null
+++ b/src/Modal.js
@@ -0,0 +1,62 @@
+/*
+Copyright 2015 OpenMarket Ltd
+
+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.
+*/
+
+
+'use strict';
+
+var React = require('react');
+var q = require('q');
+
+module.exports = {
+    DialogContainerId: "mx_Dialog_Container",
+
+    getOrCreateContainer: function() {
+        var container = document.getElementById(this.DialogContainerId);
+
+        if (!container) {
+            container = document.createElement("div");
+            container.id = this.DialogContainerId;
+            document.body.appendChild(container);
+        }
+
+        return container;
+    },
+
+    createDialog: function (Element, props) {
+        var self = this;
+
+        var closeDialog = function() {
+            React.unmountComponentAtNode(self.getOrCreateContainer());
+
+            if (props && props.onFinished) props.onFinished.apply(null, arguments);
+        };
+
+        // FIXME: If a dialog uses getDefaultProps it clobbers the onFinished
+        // property set here so you can't close the dialog from a button click!
+        var dialog = (
+            <div className="mx_Dialog_Wrapper">
+                <div className="mx_Dialog">
+                    <Element {...props} onFinished={closeDialog}/>
+                </div>
+                <div className="mx_Dialog_Background" onClick={closeDialog}></div>
+            </div>
+        );
+
+        React.render(dialog, this.getOrCreateContainer());
+
+        return {close: closeDialog};
+    },
+};
diff --git a/src/controllers/molecules/MemberTile.js b/src/controllers/molecules/MemberTile.js
index 811d2a78b1b4a42a3a626afbf21ea2b362d53b81..ad3ba5ea0e63d1374dac52b3139bca9735857c24 100644
--- a/src/controllers/molecules/MemberTile.js
+++ b/src/controllers/molecules/MemberTile.js
@@ -17,6 +17,10 @@ limitations under the License.
 'use strict';
 
 var dis = require("../../dispatcher");
+var Modal = require("../../Modal");
+var sdk = require('../../index.js');
+var QuestionDialog = ComponentBroker.get("organisms/QuestionDialog");
+var Loader = require("react-loader");
 
 var MatrixClientPeg = require("../../MatrixClientPeg");
 
@@ -27,4 +31,30 @@ module.exports = {
             user_id: this.props.member.userId
         });
     },
+
+    onLeaveClick: function() {
+        var roomId = this.props.member.roomId;
+        Modal.createDialog(QuestionDialog, {
+            title: "Leave room",
+            description: "Are you sure you want to leave the room?",
+            onFinished: function(should_leave) {
+                if (should_leave) {
+                    var d = MatrixClientPeg.get().leave(roomId);
+
+                    var modal = Modal.createDialog(Loader);
+
+                    d.then(function() {
+                        modal.close();
+                        dis.dispatch({action: 'view_next_room'});
+                    }, function(err) {
+                        modal.close();
+                        Modal.createDialog(ErrorDialog, {
+                            title: "Failed to leave room",
+                            description: err.toString()
+                        });
+                    });
+                }
+            }
+        });
+    }
 };