diff --git a/include/ChatPage.h b/include/ChatPage.h
index ad1ec9e332944f97773ef3323755d736626c9f91..04464bc56805268b3219d6d2857b387321ea025f 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -54,6 +54,7 @@ signals:
         void close();
         void changeWindowTitle(const QString &msg);
         void unreadMessages(int count);
+        void showNotification(const QString &msg);
 
 private slots:
         void showUnreadMessageNotification(int count);
diff --git a/include/MainWindow.h b/include/MainWindow.h
index a7a2b2e6e304e787b13a1396bf3f1d29f8df2c5e..0c2316a3d1058b92bd7f00814c90e85f71bead23 100644
--- a/include/MainWindow.h
+++ b/include/MainWindow.h
@@ -26,6 +26,7 @@
 #include "MatrixClient.h"
 #include "OverlayModal.h"
 #include "RegisterPage.h"
+#include "SnackBar.h"
 #include "TrayIcon.h"
 #include "WelcomePage.h"
 
@@ -91,4 +92,7 @@ private:
 
         // Tray icon that shows the unread message count.
         TrayIcon *trayIcon_;
+
+        // Notifications display.
+        QSharedPointer<SnackBar> snackBar_;
 };
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index c87f06683aa8a38eecdd4cb52abd0799c243a8ab..927fe3a6539d18d7b455f9830af4e12607ca998b 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -93,6 +93,7 @@ signals:
         void initialSyncCompleted(const SyncResponse &response);
         void syncCompleted(const SyncResponse &response);
         void syncFailed(const QString &msg);
+        void joinFailed(const QString &msg);
         void messageSent(const QString &event_id, const QString &roomid, const int txn_id);
         void emoteSent(const QString &event_id, const QString &roomid, const int txn_id);
         void messagesRetrieved(const QString &room_id, const RoomMessages &msgs);
diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h
index 732f4f61a54d504e67c0b99f7dab7735219cea30..772bdd46d40ba2ea18b6d5ff0c1d4c7c94fdf14d 100644
--- a/include/TextInputWidget.h
+++ b/include/TextInputWidget.h
@@ -30,6 +30,7 @@
 namespace msgs = matrix::events::messages;
 
 static const QString EMOTE_COMMAND("/me ");
+static const QString JOIN_COMMAND("/join ");
 
 class FilteredTextEdit : public QTextEdit
 {
@@ -63,10 +64,12 @@ signals:
         void sendTextMessage(QString msg);
         void sendEmoteMessage(QString msg);
         void uploadImage(QString filename);
+        void sendJoinRoomRequest(const QString &room);
 
 private:
         void showUploadSpinner();
         QString parseEmoteCommand(const QString &cmd);
+        QString parseJoinCommand(const QString &cmd);
 
         QHBoxLayout *topLayout_;
         FilteredTextEdit *input_;
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 5648a83078d8dce13d9e7929e3a37cd610d0f48b..92692fc17e08958c1551eaba49890fde43461099 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -166,10 +166,16 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
                 view_manager_,
                 SLOT(sendEmoteMessage(const QString &)));
 
+        connect(text_input_,
+                &TextInputWidget::sendJoinRoomRequest,
+                client_.data(),
+                &MatrixClient::joinRoom);
+
         connect(text_input_, &TextInputWidget::uploadImage, this, [=](QString filename) {
                 client_->uploadImage(current_room_, filename);
         });
 
+        connect(client_.data(), &MatrixClient::joinFailed, this, &ChatPage::showNotification);
         connect(client_.data(),
                 &MatrixClient::imageUploaded,
                 this,
@@ -203,10 +209,9 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
                 SIGNAL(ownAvatarRetrieved(const QPixmap &)),
                 this,
                 SLOT(setOwnAvatar(const QPixmap &)));
-        connect(client_.data(),
-                SIGNAL(joinedRoom(const QString &)),
-                this,
-                SLOT(addRoom(const QString &)));
+        connect(client_.data(), &MatrixClient::joinedRoom, this, [=]() {
+                emit showNotification("You joined the room.");
+        });
         connect(client_.data(),
                 SIGNAL(leftRoom(const QString &)),
                 this,
@@ -636,9 +641,9 @@ ChatPage::addRoom(const QString &room_id)
                                         QSharedPointer<RoomSettings>(new RoomSettings(room_id)));
 
                 room_list_->addRoom(settingsManager_[room_id], state_manager_[room_id], room_id);
-
-                this->changeTopRoomInfo(room_id);
                 room_list_->highlightSelectedRoom(room_id);
+
+                changeTopRoomInfo(room_id);
         }
 }
 
diff --git a/src/MainWindow.cc b/src/MainWindow.cc
index 8072668356734e9e1a9c6a71d77f5696fd057a98..06f8245c7813a97c174ec3855073002d8a111d83 100644
--- a/src/MainWindow.cc
+++ b/src/MainWindow.cc
@@ -15,8 +15,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "Config.h"
 #include "MainWindow.h"
+#include "Config.h"
 
 #include <QApplication>
 #include <QLayout>
@@ -142,6 +142,15 @@ MainWindow::removeOverlayProgressBar()
                 spinner_.reset();
         });
 
+        // FIXME:  Snackbar doesn't work if it's initialized in the constructor.
+        QTimer::singleShot(100, this, [=]() {
+                snackBar_ = QSharedPointer<SnackBar>(new SnackBar(this));
+                connect(chat_page_,
+                        &ChatPage::showNotification,
+                        snackBar_.data(),
+                        &SnackBar::showMessage);
+        });
+
         timer->start(500);
 }
 
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index 708e1176d5e5dabcbcc77b5d84f61ed6a8bdb4c9..e9e47fcb5ad3a900e2fe1a34ac5d73cadeabf446 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -477,13 +477,22 @@ MatrixClient::onJoinRoomResponse(QNetworkReply *reply)
         int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
 
         if (status == 0 || status >= 400) {
-                qWarning() << reply->errorString();
+                auto data     = reply->readAll();
+                auto response = QJsonDocument::fromJson(data);
+                auto json     = response.object();
+
+                if (json.contains("error"))
+                        emit joinFailed(json["error"].toString());
+                else
+                        qDebug() << reply->errorString();
+
                 return;
         }
 
-        auto data              = reply->readAll();
-        QJsonDocument response = QJsonDocument::fromJson(data);
-        QString room_id        = response.object()["room_id"].toString();
+        auto data     = reply->readAll();
+        auto response = QJsonDocument::fromJson(data);
+        auto room_id  = response.object()["room_id"].toString();
+
         emit joinedRoom(room_id);
 }
 
@@ -899,6 +908,7 @@ MatrixClient::joinRoom(const QString &roomIdOrAlias)
 
         QNetworkReply *reply = post(request, "{}");
         reply->setProperty("endpoint", static_cast<int>(Endpoint::JoinRoom));
+        reply->setProperty("room", roomIdOrAlias);
 }
 
 void
diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc
index f894a247d02628802760cc608ddf7605fcc399c0..b90a7caab1ba9236e7f08d1818730b33670eb3b3 100644
--- a/src/TextInputWidget.cc
+++ b/src/TextInputWidget.cc
@@ -148,6 +148,11 @@ TextInputWidget::onSendButtonClicked()
 
                 if (!text.isEmpty())
                         emit sendEmoteMessage(text);
+        } else if (msgText.startsWith(JOIN_COMMAND)) {
+                auto room = parseJoinCommand(msgText);
+
+                if (!room.isEmpty())
+                        emit sendJoinRoomRequest(room);
         } else {
                 emit sendTextMessage(msgText);
         }
@@ -155,6 +160,17 @@ TextInputWidget::onSendButtonClicked()
         input_->clear();
 }
 
+QString
+TextInputWidget::parseJoinCommand(const QString &cmd)
+{
+        auto room = cmd.right(cmd.size() - JOIN_COMMAND.size()).trimmed();
+
+        if (!room.isEmpty())
+                return room;
+
+        return QString("");
+}
+
 QString
 TextInputWidget::parseEmoteCommand(const QString &cmd)
 {
diff --git a/src/ui/SnackBar.cc b/src/ui/SnackBar.cc
index 673c2f934307c32caf96c2f002b91794e47e8277..39566e99f6a71e484652e582c63e3afa3c0c9455 100644
--- a/src/ui/SnackBar.cc
+++ b/src/ui/SnackBar.cc
@@ -84,7 +84,7 @@ SnackBar::showMessage(const QString &msg)
 void
 SnackBar::onTimeout()
 {
-        offset_ -= 0.5;
+        offset_ -= 1.1;
 
         if (offset_ <= 0.0) {
                 showTimer_->stop();