diff --git a/resources/qml/RoomDirectory.qml b/resources/qml/RoomDirectory.qml
index b01c1e00441e88527cc9d03369e81e6e58e2dfd7..65685b9af3dd59a5678d53f8aadb6789fac5f6ad 100644
--- a/resources/qml/RoomDirectory.qml
+++ b/resources/qml/RoomDirectory.qml
@@ -2,6 +2,7 @@
 //
 // SPDX-License-Identifier: GPL-3.0-or-later
 
+import "./ui"
 import QtQuick 2.9
 import QtQuick.Controls 2.3
 import QtQuick.Layouts 1.3
@@ -11,6 +12,8 @@ ApplicationWindow {
     id: roomDirectoryWindow
     visible: true
 
+    property RoomDirectoryModel publicRooms : RoomDirectoryModel {}
+
     x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
     y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
     minimumWidth: 650
@@ -56,9 +59,7 @@ ApplicationWindow {
     ListView {
         id: roomDirView
         anchors.fill: parent
-        model: RoomDirectoryModel {
-            id: roomDir
-        }
+        model: publicRooms
         delegate: Rectangle {
             id: roomDirDelegate
 
@@ -158,16 +159,36 @@ ApplicationWindow {
                         	width: joinRoomButton.width
 				Button {
                             		id: joinRoomButton
-			    		visible: roomDir.canJoinRoom(model.roomid)
+			    		visible: publicRooms.canJoinRoom(model.roomid)
 					anchors.centerIn: parent 
                             		width: Math.ceil(0.1 * roomDirectoryWindow.width)
 					text: "Join"
-                            		onClicked: roomDir.joinRoom(model.index)
+                            		onClicked: publicRooms.joinRoom(model.index)
                         	}		
 			}
                     }
                 }
             }
-        } 
+        }
+	
+	footer: Item {
+		anchors.horizontalCenter: parent.horizontalCenter
+        	width: parent.width
+        	visible: (publicRooms.reachedEndOfPagination == false) && publicRooms.loadingMoreRooms
+		// hacky but works
+        	height: loadingSpinner.height + 2 * Nheko.paddingLarge
+        	anchors.margins: Nheko.paddingLarge
+
+        	Spinner {
+                	id: loadingSpinner
+
+                	anchors.centerIn: parent
+			anchors.margins: Nheko.paddingLarge
+			running: visible
+			foreground: Nheko.colors.mid 
+                	z: 7
+               }
+    	}
+ 
     }
 }
diff --git a/src/RoomDirectoryModel.cpp b/src/RoomDirectoryModel.cpp
index 2c633491b7ae467f6db21f0b216dddfcbd8075ce..7d6be13e0e92d97e3e435d7812aaf3320ae0e557 100644
--- a/src/RoomDirectoryModel.cpp
+++ b/src/RoomDirectoryModel.cpp
@@ -11,7 +11,6 @@
 RoomDirectoryModel::RoomDirectoryModel(QObject *parent, const std::string &s)
   : QAbstractListModel(parent)
   , server_(s)
-  , canFetchMore_(true)
 {
         connect(this,
                 &RoomDirectoryModel::fetchedRoomsBatch,
@@ -127,6 +126,8 @@ RoomDirectoryModel::data(const QModelIndex &index, int role) const
 void
 RoomDirectoryModel::fetchMore(const QModelIndex &)
 {
+	if (!canFetchMore_) return;	
+
         nhlog::net()->debug("Fetching more rooms from mtxclient...");
 
         mtx::requests::PublicRooms req;
@@ -136,10 +137,19 @@ RoomDirectoryModel::fetchMore(const QModelIndex &)
         // req.third_party_instance_id = third_party_instance_id;
         auto requested_server = server_;
 
+	reachedEndOfPagination_ = false;
+	emit reachedEndOfPaginationChanged();
+
+	loadingMoreRooms_ = true;
+	emit loadingMoreRoomsChanged();
+
         http::client()->post_public_rooms(
           req,
           [requested_server, this, req](const mtx::responses::PublicRooms &res,
                                         mtx::http::RequestErr err) {
+		  loadingMoreRooms_ = false;
+		  emit loadingMoreRoomsChanged();
+
                   if (err) {
                           nhlog::net()->error(
                             "Failed to retrieve rooms from mtxclient - {} - {} - {}",
@@ -149,7 +159,7 @@ RoomDirectoryModel::fetchMore(const QModelIndex &)
                   } else if (req.filter.generic_search_term == this->userSearchString_ &&
                              req.since == this->prevBatch_ && requested_server == this->server_) {
                           nhlog::net()->debug("signalling chunk to GUI thread");
-                          emit fetchedRoomsBatch(res.chunk, res.prev_batch, res.next_batch);
+                          emit fetchedRoomsBatch(res.chunk, res.next_batch);
                   }
           },
           requested_server);
@@ -157,11 +167,9 @@ RoomDirectoryModel::fetchMore(const QModelIndex &)
 
 void
 RoomDirectoryModel::displayRooms(std::vector<mtx::responses::PublicRoomsChunk> fetched_rooms,
-                                 const std::string &prev_batch,
                                  const std::string &next_batch)
 {
-        nhlog::net()->debug("Prev batch: {} | Next batch: {}", prevBatch_, nextBatch_);
-        nhlog::net()->debug("NP batch: {} | NN batch: {}", prev_batch, next_batch);
+        nhlog::net()->debug("Prev batch: {} | Next batch: {}", prevBatch_, next_batch);
 
         if (fetched_rooms.empty()) {
                 nhlog::net()->error("mtxclient helper thread yielded empty chunk!");
@@ -177,7 +185,11 @@ RoomDirectoryModel::displayRooms(std::vector<mtx::responses::PublicRoomsChunk> f
 
         if (next_batch.empty()) {
                 canFetchMore_ = false;
+		reachedEndOfPagination_ = true;
+		emit reachedEndOfPaginationChanged();
         }
 
         prevBatch_ = next_batch;
+
+	nhlog::ui()->debug ("Finished loading rooms");
 }
diff --git a/src/RoomDirectoryModel.h b/src/RoomDirectoryModel.h
index 952ae3ff6003cdd79ba214c8b3db9e9f46b356ba..a7e6c0bcb50b256ea5a54ced23808fd38e0953e9 100644
--- a/src/RoomDirectoryModel.h
+++ b/src/RoomDirectoryModel.h
@@ -27,6 +27,9 @@ class RoomDirectoryModel : public QAbstractListModel
 {
         Q_OBJECT
 
+	Q_PROPERTY (bool loadingMoreRooms READ loadingMoreRooms NOTIFY loadingMoreRoomsChanged)
+	Q_PROPERTY (bool reachedEndOfPagination READ reachedEndOfPagination NOTIFY reachedEndOfPaginationChanged)
+
 public:
         explicit RoomDirectoryModel(QObject *parent = nullptr, const std::string &s = "");
 
@@ -49,10 +52,15 @@ public:
                 return static_cast<int>(publicRoomsData_.size());
         }
 
-        inline bool canFetchMore(const QModelIndex &) const override
+        bool canFetchMore(const QModelIndex &) const override
         {
                 return canFetchMore_;
         }
+
+	bool loadingMoreRooms() const { return loadingMoreRooms_; }
+
+	bool reachedEndOfPagination() const { return reachedEndOfPagination_; }
+
         void fetchMore(const QModelIndex &) override;
 
         Q_INVOKABLE bool canJoinRoom(const QByteArray &room);
@@ -60,15 +68,13 @@ public:
 
 signals:
         void fetchedRoomsBatch(std::vector<mtx::responses::PublicRoomsChunk> rooms,
-                               const std::string &prev_batch,
                                const std::string &next_batch);
-        void serverChanged();
-        void searchTermEntered();
+	void loadingMoreRoomsChanged();
+	void reachedEndOfPaginationChanged();
 
 public slots:
         void displayRooms(std::vector<mtx::responses::PublicRoomsChunk> rooms,
-                          const std::string &prev,
-                          const std::string &next);
+                          const std::string &next_batch);
         void setMatrixServer(const QString &s = "");
         void setSearchTerm(const QString &f);
 
@@ -79,7 +85,9 @@ private:
         std::string userSearchString_;
         std::string prevBatch_;
         std::string nextBatch_;
-        bool canFetchMore_;
+        bool canFetchMore_ {true};
+	bool loadingMoreRooms_ {false};
+	bool reachedEndOfPagination_ {false};
         std::vector<mtx::responses::PublicRoomsChunk> publicRoomsData_;
 
         std::vector<std::string> getViasForRoom(const std::vector<std::string> &room);