diff --git a/include/ChatPage.h b/include/ChatPage.h
index 416f787086b67800dbbed483bf8a99b0186418b5..849d60e7e1a2469bf685a20885ce9fbe1fad4ebc 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -45,8 +45,9 @@ class UserInfoWidget;
 class JoinedRoom;
 class LeftRoom;
 
-constexpr int CONSENSUS_TIMEOUT    = 1000;
-constexpr int SHOW_CONTENT_TIMEOUT = 3000;
+constexpr int CONSENSUS_TIMEOUT      = 1000;
+constexpr int SHOW_CONTENT_TIMEOUT   = 3000;
+constexpr int TYPING_REFRESH_TIMEOUT = 10000;
 
 class ChatPage : public QWidget
 {
@@ -141,6 +142,7 @@ private:
 
         // Keeps track of the users currently typing on each room.
         QMap<QString, QList<QString>> typingUsers_;
+        QTimer *typingRefresher_;
 
         QSharedPointer<QuickSwitcher> quickSwitcher_;
         QSharedPointer<OverlayModal> quickSwitcherModal_;
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index ef9e82e616679544289485314b5cdb12bfd64386..d6dd71628a8fe8d0851c25671e33e5723087a6ff 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -56,6 +56,8 @@ public:
         void uploadImage(const QString &roomid, const QString &filename);
         void joinRoom(const QString &roomIdOrAlias);
         void leaveRoom(const QString &roomId);
+        void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000);
+        void removeTypingNotification(const QString &roomid);
 
         QUrl getHomeServer() { return server_; };
         int transactionId() { return txn_id_; };
diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h
index 08b62f45e41fd249f160cd50847723c2f0f82e46..e32ce2ff6c149b1016183feefba17838ef208b56 100644
--- a/include/TextInputWidget.h
+++ b/include/TextInputWidget.h
@@ -35,12 +35,20 @@ static const QString JOIN_COMMAND("/join ");
 class FilteredTextEdit : public QTextEdit
 {
         Q_OBJECT
+
+private:
+        QTimer *typingTimer_;
+
 public:
         explicit FilteredTextEdit(QWidget *parent = nullptr);
         void keyPressEvent(QKeyEvent *event);
 
+        void stopTyping();
+
 signals:
         void enterPressed();
+        void startedTyping();
+        void stoppedTyping();
 };
 
 class TextInputWidget : public QFrame
@@ -51,6 +59,8 @@ public:
         TextInputWidget(QWidget *parent = 0);
         ~TextInputWidget();
 
+        void stopTyping();
+
 public slots:
         void onSendButtonClicked();
         void openFileSelection();
@@ -66,6 +76,9 @@ signals:
         void uploadImage(QString filename);
         void sendJoinRoomRequest(const QString &room);
 
+        void startedTyping();
+        void stoppedTyping();
+
 private:
         void showUploadSpinner();
         QString parseEmoteCommand(const QString &cmd);
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 5fefd76727c351c9651750e6851403578d12d93b..d81b64fb7d6dc70fc5fb2e9fde103a8b72e307c9 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -122,6 +122,9 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
         contentLayout_->addWidget(typingDisplay_);
         contentLayout_->addWidget(text_input_);
 
+        typingRefresher_ = new QTimer(this);
+        typingRefresher_->setInterval(TYPING_REFRESH_TIMEOUT);
+
         user_info_widget_ = new UserInfoWidget(sideBarTopWidget_);
         sideBarTopWidgetLayout_->addWidget(user_info_widget_);
 
@@ -139,6 +142,7 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
 
                 typingDisplay_->setUsers(users);
         });
+        connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::stopTyping);
 
         connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo);
         connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit);
@@ -159,6 +163,20 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
                                 room_list_->updateUnreadMessageCount(roomid, count);
                 });
 
+        connect(text_input_, &TextInputWidget::startedTyping, this, [=]() {
+                typingRefresher_->start();
+                client_->sendTypingNotification(current_room_);
+        });
+
+        connect(text_input_, &TextInputWidget::stoppedTyping, this, [=]() {
+                typingRefresher_->stop();
+                client_->removeTypingNotification(current_room_);
+        });
+
+        connect(typingRefresher_, &QTimer::timeout, this, [=]() {
+                client_->sendTypingNotification(current_room_);
+        });
+
         connect(view_manager_,
                 &TimelineViewManager::updateRoomsLastMessage,
                 room_list_,
@@ -600,13 +618,20 @@ ChatPage::updateTypingUsers(const QString &roomid, const QList<QString> &user_id
 {
         QStringList users;
 
-        for (const auto uid : user_ids)
+        QSettings settings;
+        QString user_id = settings.value("auth/user_id").toString();
+
+        for (const auto uid : user_ids) {
+                if (uid == user_id)
+                        continue;
                 users.append(TimelineViewManager::displayName(uid));
+        }
 
         users.sort();
 
-        if (current_room_ == roomid)
+        if (current_room_ == roomid) {
                 typingDisplay_->setUsers(users);
+        }
 
         typingUsers_.insert(roomid, users);
 }
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index e1085e820dc09deb2cf3cf3f6d799e31d948b9a4..c6ef501f6fa0575f1dc15364d4a20df7b2ab4303 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -794,3 +794,53 @@ MatrixClient::leaveRoom(const QString &roomId)
                 emit leftRoom(roomId);
         });
 }
+
+void
+MatrixClient::sendTypingNotification(const QString &roomid, int timeoutInMillis)
+{
+        QSettings settings;
+        QString user_id = settings.value("auth/user_id").toString();
+
+        QUrlQuery query;
+        query.addQueryItem("access_token", token_);
+
+        QUrl endpoint(server_);
+        endpoint.setPath(clientApiUrl_ + QString("/rooms/%1/typing/%2").arg(roomid).arg(user_id));
+
+        endpoint.setQuery(query);
+
+        QString msgType("");
+        QJsonObject body;
+
+        body = { { "typing", true }, { "timeout", timeoutInMillis } };
+
+        QNetworkRequest request(QString(endpoint.toEncoded()));
+        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+        put(request, QJsonDocument(body).toJson(QJsonDocument::Compact));
+}
+
+void
+MatrixClient::removeTypingNotification(const QString &roomid)
+{
+        QSettings settings;
+        QString user_id = settings.value("auth/user_id").toString();
+
+        QUrlQuery query;
+        query.addQueryItem("access_token", token_);
+
+        QUrl endpoint(server_);
+        endpoint.setPath(clientApiUrl_ + QString("/rooms/%1/typing/%2").arg(roomid).arg(user_id));
+
+        endpoint.setQuery(query);
+
+        QString msgType("");
+        QJsonObject body;
+
+        body = { { "typing", false } };
+
+        QNetworkRequest request(QString(endpoint.toEncoded()));
+        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+        put(request, QJsonDocument(body).toJson(QJsonDocument::Compact));
+}
diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc
index 0d5e1102ae16505bb0e33f06c46c9dee8633f9a1..7ebef6b1146074c01d5bd48a979e6a18696f80e1 100644
--- a/src/TextInputWidget.cc
+++ b/src/TextInputWidget.cc
@@ -29,15 +29,37 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
   : QTextEdit(parent)
 {
         setAcceptRichText(false);
+
+        typingTimer_ = new QTimer(this);
+        typingTimer_->setInterval(1000);
+        typingTimer_->setSingleShot(true);
+
+        connect(typingTimer_, &QTimer::timeout, this, &FilteredTextEdit::stopTyping);
 }
 
 void
 FilteredTextEdit::keyPressEvent(QKeyEvent *event)
 {
-        if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
+        if (!typingTimer_->isActive()) {
+                emit startedTyping();
+        }
+
+        typingTimer_->start();
+
+        if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
+                stopTyping();
+
                 emit enterPressed();
-        else
+        } else {
                 QTextEdit::keyPressEvent(event);
+        }
+}
+
+void
+FilteredTextEdit::stopTyping()
+{
+        typingTimer_->stop();
+        emit stoppedTyping();
 }
 
 TextInputWidget::TextInputWidget(QWidget *parent)
@@ -104,6 +126,10 @@ TextInputWidget::TextInputWidget(QWidget *parent)
                 SIGNAL(emojiSelected(const QString &)),
                 this,
                 SLOT(addSelectedEmoji(const QString &)));
+
+        connect(input_, &FilteredTextEdit::startedTyping, this, &TextInputWidget::startedTyping);
+
+        connect(input_, &FilteredTextEdit::stoppedTyping, this, &TextInputWidget::stoppedTyping);
 }
 
 void
@@ -227,3 +253,9 @@ TextInputWidget::hideUploadSpinner()
 }
 
 TextInputWidget::~TextInputWidget() {}
+
+void
+TextInputWidget::stopTyping()
+{
+        input_->stopTyping();
+}