diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index 222c82b1c..db6f920ff 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -39,6 +39,7 @@ GameList::GameList(QWidget* parent) : QWidget{parent} {
 
     connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
     connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
+    connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
 
     // We must register all custom types with the Qt Automoc system so that we are able to use it
     // with signals/slots. In this case, QList falls under the umbrells of custom types.
@@ -103,6 +104,12 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
     item_model->removeRows(0, item_model->rowCount());
 
     emit ShouldCancelWorker();
+
+    auto watch_dirs = watcher.directories();
+    if (!watch_dirs.isEmpty()) {
+        watcher.removePaths(watch_dirs);
+    }
+    UpdateWatcherList(dir_path.toStdString(), deep_scan ? 256 : 0);
     GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
 
     connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
@@ -140,6 +147,45 @@ static bool HasSupportedFileExtension(const std::string& file_name) {
     return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
 }
 
+void GameList::RefreshGameDirectory() {
+    if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
+        LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
+        PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
+    }
+}
+
+/**
+ * Adds the game list folder to the QFileSystemWatcher to check for updates.
+ *
+ * The file watcher will fire off an update to the game list when a change is detected in the game
+ * list folder.
+ *
+ * Notice: This method is run on the UI thread because QFileSystemWatcher is not thread safe and
+ * this function is fast enough to not stall the UI thread. If performance is an issue, it should
+ * be moved to another thread and properly locked to prevent concurrency issues.
+ *
+ * @param dir folder to check for changes in
+ * @param recursion 0 if recursion is disabled. Any positive number passed to this will add each
+ *        directory recursively to the watcher and will update the file list if any of the folders
+ *        change. The number determines how deep the recursion should traverse.
+ */
+void GameList::UpdateWatcherList(const std::string& dir, unsigned int recursion) {
+    const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
+                                            const std::string& virtual_name) -> bool {
+        std::string physical_name = directory + DIR_SEP + virtual_name;
+
+        if (FileUtil::IsDirectory(physical_name)) {
+            UpdateWatcherList(physical_name, recursion - 1);
+        }
+        return true;
+    };
+
+    watcher.addPath(QString::fromStdString(dir));
+    if (recursion > 0) {
+        FileUtil::ForeachDirectoryEntry(nullptr, dir, callback);
+    }
+}
+
 void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
     const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
                                             const std::string& virtual_name) -> bool {
@@ -182,6 +228,6 @@ void GameListWorker::run() {
 }
 
 void GameListWorker::Cancel() {
-    disconnect(this, nullptr, nullptr, nullptr);
+    this->disconnect();
     stop_processing = true;
 }
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index e6b7eea0b..b141fa3a5 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <QFileSystemWatcher>
 #include <QModelIndex>
 #include <QSettings>
 #include <QStandardItem>
@@ -46,8 +47,11 @@ private:
     void DonePopulating();
 
     void PopupContextMenu(const QPoint& menu_location);
+    void UpdateWatcherList(const std::string& path, unsigned int recursion);
+    void RefreshGameDirectory();
 
     QTreeView* tree_view = nullptr;
     QStandardItemModel* item_model = nullptr;
     GameListWorker* current_worker = nullptr;
+    QFileSystemWatcher watcher;
 };