diff --git a/src/Cache.cpp b/src/Cache.cpp
index 0bd6fe0d8f9be2be166447535591a0d5ba1cd425..8b1798d6ba4f57725e4d6d477dd087f8c487c361 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -905,7 +905,9 @@ Cache::runMigrations()
                                                    std::reverse(oldMessages.events.begin(),
                                                                 oldMessages.events.end());
                                                    // save messages using the new method
-                                                   saveTimelineMessages(txn, room_id, oldMessages);
+                                                   auto eventsDb = getEventsDb(txn, room_id);
+                                                   saveTimelineMessages(
+                                                     txn, eventsDb, room_id, oldMessages);
                                            }
 
                                            // delete old messages db
@@ -1208,13 +1210,24 @@ Cache::saveState(const mtx::responses::Sync &res)
                 auto statesdb    = getStatesDb(txn, room.first);
                 auto stateskeydb = getStatesKeyDb(txn, room.first);
                 auto membersdb   = getMembersDb(txn, room.first);
-
-                saveStateEvents(
-                  txn, statesdb, stateskeydb, membersdb, room.first, room.second.state.events);
-                saveStateEvents(
-                  txn, statesdb, stateskeydb, membersdb, room.first, room.second.timeline.events);
-
-                saveTimelineMessages(txn, room.first, room.second.timeline);
+                auto eventsDb    = getEventsDb(txn, room.first);
+
+                saveStateEvents(txn,
+                                statesdb,
+                                stateskeydb,
+                                membersdb,
+                                eventsDb,
+                                room.first,
+                                room.second.state.events);
+                saveStateEvents(txn,
+                                statesdb,
+                                stateskeydb,
+                                membersdb,
+                                eventsDb,
+                                room.first,
+                                room.second.timeline.events);
+
+                saveTimelineMessages(txn, eventsDb, room.first, room.second.timeline);
 
                 RoomInfo updatedInfo;
                 updatedInfo.name       = getRoomName(txn, statesdb, membersdb).toStdString();
@@ -2634,11 +2647,12 @@ void
 Cache::savePendingMessage(const std::string &room_id,
                           const mtx::events::collections::TimelineEvent &message)
 {
-        auto txn = lmdb::txn::begin(env_);
+        auto txn      = lmdb::txn::begin(env_);
+        auto eventsDb = getEventsDb(txn, room_id);
 
         mtx::responses::Timeline timeline;
         timeline.events.push_back(message.data);
-        saveTimelineMessages(txn, room_id, timeline);
+        saveTimelineMessages(txn, eventsDb, room_id, timeline);
 
         auto pending = getPendingMessagesDb(txn, room_id);
 
@@ -2706,13 +2720,13 @@ Cache::removePendingStatus(const std::string &room_id, const std::string &txn_id
 
 void
 Cache::saveTimelineMessages(lmdb::txn &txn,
+                            lmdb::dbi &eventsDb,
                             const std::string &room_id,
                             const mtx::responses::Timeline &res)
 {
         if (res.events.empty())
                 return;
 
-        auto eventsDb    = getEventsDb(txn, room_id);
         auto relationsDb = getRelationsDb(txn, room_id);
 
         auto orderDb     = getEventOrderDb(txn, room_id);
diff --git a/src/Cache_p.h b/src/Cache_p.h
index 064f4882b51d52bf16bbd20cc68a7f18381070fd..e35e78ec00846c1769742d0ed64c437a666bc617 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -335,6 +335,7 @@ private:
         std::string getLastEventId(lmdb::txn &txn, const std::string &room_id);
         DescInfo getLastMessageInfo(lmdb::txn &txn, const std::string &room_id);
         void saveTimelineMessages(lmdb::txn &txn,
+                                  lmdb::dbi &eventsDb,
                                   const std::string &room_id,
                                   const mtx::responses::Timeline &res);
 
@@ -353,11 +354,12 @@ private:
                              lmdb::dbi &statesdb,
                              lmdb::dbi &stateskeydb,
                              lmdb::dbi &membersdb,
+                             lmdb::dbi &eventsDb,
                              const std::string &room_id,
                              const std::vector<T> &events)
         {
                 for (const auto &e : events)
-                        saveStateEvent(txn, statesdb, stateskeydb, membersdb, room_id, e);
+                        saveStateEvent(txn, statesdb, stateskeydb, membersdb, eventsDb, room_id, e);
         }
 
         template<class T>
@@ -365,6 +367,7 @@ private:
                             lmdb::dbi &statesdb,
                             lmdb::dbi &stateskeydb,
                             lmdb::dbi &membersdb,
+                            lmdb::dbi &eventsDb,
                             const std::string &room_id,
                             const T &event)
         {
@@ -401,8 +404,10 @@ private:
                 }
 
                 std::visit(
-                  [&txn, &statesdb, &stateskeydb](auto e) {
-                          if constexpr (isStateEvent(e))
+                  [&txn, &statesdb, &stateskeydb, &eventsDb](auto e) {
+                          if constexpr (isStateEvent(e)) {
+                                  eventsDb.put(txn, e.event_id, json(e).dump());
+
                                   if (e.type != EventType::Unsupported) {
                                           if (e.state_key.empty())
                                                   statesdb.put(
@@ -417,6 +422,7 @@ private:
                                                                  })
                                                       .dump());
                                   }
+                          }
                   },
                   event);
         }
diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp
index 88464bf9ad3ca4ecdb525bc80598a5b59e3e481f..6ff953d26f38d3f64ce55fb46cc3aeda9bf04e0c 100644
--- a/src/timeline/CommunitiesModel.cpp
+++ b/src/timeline/CommunitiesModel.cpp
@@ -185,6 +185,15 @@ CommunitiesModel::setCurrentTagId(QString tagId)
                                 return;
                         }
                 }
+        } else if (tagId.startsWith("space:")) {
+                auto tag = tagId.mid(6);
+                for (const auto &t : spaceOrder_) {
+                        if (t == tag) {
+                                this->currentTagId_ = tagId;
+                                emit currentTagIdChanged(currentTagId_);
+                                return;
+                        }
+                }
         }
 
         this->currentTagId_ = "";
diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp
index 0f980c6cb5fb558509d10744f1d9abf581af1e36..3b6ad54ae2c33f03049efccdfe1dc5f12d0b39eb 100644
--- a/src/timeline/RoomlistModel.cpp
+++ b/src/timeline/RoomlistModel.cpp
@@ -530,6 +530,30 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons
                                         return false;
                 }
                 return true;
+        } else if (filterType == FilterBy::Space) {
+                auto roomid = sourceModel()
+                                ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::RoomId)
+                                .toString();
+                auto tags = sourceModel()
+                              ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::Tags)
+                              .toStringList();
+
+                auto contains = [](const std::vector<std::string> &v, const std::string &str) {
+                        for (const auto &e : v)
+                                if (e == str)
+                                        return true;
+                        return false;
+                };
+                auto parents = cache::client()->getParentRoomIds(roomid.toStdString());
+
+                if (!contains(parents, filterStr.toStdString()))
+                        return false;
+                else if (!hiddenTags.empty()) {
+                        for (const auto &t : tags)
+                                if (hiddenTags.contains(t))
+                                        return false;
+                }
+                return true;
         } else {
                 return true;
         }
diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h
index b024488611ace19ecd334209fe9807d2633326c9..5f8b8bd8e0ea30c725784bbb85950f4f5169b59b 100644
--- a/src/timeline/RoomlistModel.h
+++ b/src/timeline/RoomlistModel.h
@@ -134,6 +134,9 @@ public slots:
                 if (tagId.startsWith("tag:")) {
                         filterType = FilterBy::Tag;
                         filterStr  = tagId.mid(4);
+                } else if (tagId.startsWith("space:")) {
+                        filterType = FilterBy::Space;
+                        filterStr  = tagId.mid(6);
                 } else {
                         filterType = FilterBy::Nothing;
                         filterStr.clear();