diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php
index 89783bd38c2f74e4b1cdda26bcd324537406ef16..1ba8ff3c4b231a12a1060eb1eaacacf14daeec17 100644
--- a/classes/pref/prefs.php
+++ b/classes/pref/prefs.php
@@ -802,7 +802,7 @@ class Pref_Prefs extends Handler_Protected {
 			if ($about[3] ?? false) {
 				$is_checked = in_array($name, $system_enabled) ? "checked" : "";
 				?>
-				<fieldset class='prefs plugin'>
+				<fieldset class='prefs plugin' data-plugin-name='<?= htmlspecialchars($name) ?>'>
 					<label><?= $name ?>:</label>
 					<label class='checkbox description text-muted' id="PLABEL-<?= htmlspecialchars($name) ?>">
 						<input disabled='1' dojoType='dijit.form.CheckBox' <?= $is_checked ?> type='checkbox'><?= htmlspecialchars($about[1]) ?>
@@ -867,7 +867,7 @@ class Pref_Prefs extends Handler_Protected {
 				}
 				?>
 
-				<fieldset class='prefs plugin'>
+					<fieldset class='prefs plugin' data-plugin-name='<?= htmlspecialchars($name) ?>'>
 					<label><?= $name ?>:</label>
 					<label class='checkbox description text-muted' id="PLABEL-<?= htmlspecialchars($name) ?>">
 						<input name='plugins[]' value="<?= htmlspecialchars($name) ?>"
@@ -1353,14 +1353,20 @@ class Pref_Prefs extends Handler_Protected {
 		}
 	}
 
-	private function _get_available_plugins() {
+	private function _get_available_plugins(array $installed = []) {
 		if ($_SESSION["access_level"] >= 10 && Config::get(Config::ENABLE_PLUGIN_INSTALLER)) {
-			return json_decode(UrlHelper::fetch(['url' => 'https://tt-rss.org/plugins.json']), true);
+			$obj = json_decode(UrlHelper::fetch(['url' => 'https://tt-rss.org/plugins.json']));
+
+			// TODO: filter installed, we'll need class names in the plugins.json
+
+			return $obj;
 		}
 	}
 	function getAvailablePlugins() {
+		$installed = $_REQUEST['installed'];
+
 		if ($_SESSION["access_level"] >= 10) {
-			print json_encode($this->_get_available_plugins());
+			print json_encode($this->_get_available_plugins($installed));
 		}
 	}
 
diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js
index b599e7e3f494a7b48162d5ca723e7c17e7a44ca3..889d93232ac35d5a102a62b7e8204002f54feef4 100644
--- a/js/PrefHelpers.js
+++ b/js/PrefHelpers.js
@@ -370,8 +370,10 @@ const	Helpers = {
 				PI_ERR_NO_TEMPDIR: "PI_ERR_NO_TEMPDIR",
 				PI_ERR_PLUGIN_NOT_FOUND: "PI_ERR_PLUGIN_NOT_FOUND",
 				PI_ERR_NO_WORKDIR: "PI_ERR_NO_WORKDIR",
-				title: __("List of plugins"),
+				title: __("Available plugins"),
 				need_refresh: false,
+				entries: false,
+				search_query: "",
 				onHide: function() {
 					if (this.need_refresh) {
 						Helpers.Prefs.refresh();
@@ -428,18 +430,26 @@ const	Helpers = {
 					install_dialog.show();
 
 				},
-				refresh: function() {
+				search: function() {
+					this.search_query = this.attr('value').search.toLowerCase();
+					this.render_contents();
+				},
+				render_contents: function() {
 					const container = dialog.domNode.querySelector(".contents");
-					container.innerHTML = `<li class='text-center'>${__("Looking for plugins...")}</li>`;
 
-					xhr.json("backend.php", {op: "pref-prefs", method: "getAvailablePlugins"}, (reply) => {
+					if (!dialog.entries) {
+						container.innerHTML = `<li class='text-center text-error'>${__("Operation failed: check event log.")}</li>`;
+					} else {
+						container.innerHTML = "";
 
-						if (!reply) {
-							container.innerHTML = `<li class='text-center text-error'>${__("Operation failed: check event log.")}</li>`;
-						} else {
-							container.innerHTML = "";
+						let results_rendered = 0;
+
+						dialog.entries.forEach((plugin) => {
+							if (!dialog.search_query ||
+									(plugin.name.toLowerCase().indexOf(dialog.search_query) != -1 || plugin.description.toLowerCase().indexOf(dialog.search_query) != -1)) {
+
+								++results_rendered;
 
-							reply.forEach((plugin) => {
 								container.innerHTML += `
 									<li data-row-value="${App.escapeHtml(plugin.name)}">
 										<h3 style="margin-top: 0">${plugin.name}
@@ -456,17 +466,39 @@ const	Helpers = {
 										<hr/>
 									</li>
 									`
-							});
+							}
+						});
 
-							dojo.parser.parse(container);
+						if (results_rendered == 0) {
+							container.innerHTML = `<li class='text-center text-info'>${__("Could not find any plugins for this search query.")}</li>`;
 						}
+
+						dojo.parser.parse(container);
+					}
+				},
+				reload: function() {
+					const container = dialog.domNode.querySelector(".contents");
+					container.innerHTML = `<li class='text-center'>${__("Looking for plugins...")}</li>`;
+
+					const installed = [...document.querySelectorAll('*[data-plugin-name]')].map((p) => p.getAttribute('data-plugin-name'));
+
+					xhr.json("backend.php", {op: "pref-prefs", method: "getAvailablePlugins", 'installed[]': installed}, (reply) => {
+						dialog.entries = reply;
+						dialog.render_contents();
 					});
 				},
 				content: `
-					<ul class="panel panel-scrollable contents"> </ul>
+					<div dojoType='fox.Toolbar'>
+						<div class='pull-right'>
+							<input name="search" placeholder="${__("Search...")}" type="search" dojoType="dijit.form.TextBox" onkeyup="App.dialogOf(this).search()">
+						</div>
+						<div style='height : 16px'>&nbsp;</div> <!-- disgusting -->
+					</div>
+
+					<ul style='clear : both' class="panel panel-scrollable-400px contents"> </ul>
 
 					<footer>
-						${App.FormFields.button_tag(__("Refresh"), "", {class: 'alt-primary', onclick: 'App.dialogOf(this).refresh()'})}
+						${App.FormFields.button_tag(__("Refresh"), "", {class: 'alt-primary', onclick: 'App.dialogOf(this).reload()'})}
 						${App.FormFields.cancel_dialog_tag(__("Close"))}
 					</footer>
 				`,
@@ -474,7 +506,7 @@ const	Helpers = {
 
 			const tmph = dojo.connect(dialog, 'onShow', function () {
 				dojo.disconnect(tmph);
-				dialog.refresh();
+				dialog.reload();
 			});
 
 			dialog.show();
diff --git a/themes/compact.css b/themes/compact.css
index 230aab7da1f7f836760eb68567370dd9f7c07892..1f9d457dcd660c6d96d0236a92849dd4fdd558fc 100644
--- a/themes/compact.css
+++ b/themes/compact.css
@@ -988,6 +988,10 @@ body.ttrss_main .panel-scrollable {
   overflow: auto;
   height: 200px;
 }
+body.ttrss_main .panel-scrollable-400px {
+  overflow: auto;
+  height: 400px;
+}
 body.ttrss_main ul.list li {
   padding: 2px;
 }
@@ -1715,9 +1719,13 @@ body.ttrss_utility.share_popup .content {
   font-size: 13px;
   padding: 0px;
 }
-.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner {
   line-height: 10px;
 }
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder {
+  line-height: 15px;
+  margin-left: 0;
+}
 .flat .dijitToolbar label {
   position: relative;
   top: 2px;
diff --git a/themes/compact_night.css b/themes/compact_night.css
index 12737d903bd83c1823bff224f715ad3e4f19a803..8f2507f4fbad0fc9a76931080964cadd27b84241 100644
--- a/themes/compact_night.css
+++ b/themes/compact_night.css
@@ -988,6 +988,10 @@ body.ttrss_main .panel-scrollable {
   overflow: auto;
   height: 200px;
 }
+body.ttrss_main .panel-scrollable-400px {
+  overflow: auto;
+  height: 400px;
+}
 body.ttrss_main ul.list li {
   padding: 2px;
 }
@@ -1617,9 +1621,13 @@ body.ttrss_utility fieldset > label.checkbox {
   font-size: 13px;
   padding: 0px;
 }
-.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner {
   line-height: 10px;
 }
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder {
+  line-height: 15px;
+  margin-left: 0;
+}
 .flat .dijitToolbar label {
   position: relative;
   top: 2px;
diff --git a/themes/light.css b/themes/light.css
index f8da03ca0ac1215afc8b28b4c8c0657a00975646..c1e5201d21673522cf5d9bdbe0dc4d5b900102a9 100644
--- a/themes/light.css
+++ b/themes/light.css
@@ -988,6 +988,10 @@ body.ttrss_main .panel-scrollable {
   overflow: auto;
   height: 200px;
 }
+body.ttrss_main .panel-scrollable-400px {
+  overflow: auto;
+  height: 400px;
+}
 body.ttrss_main ul.list li {
   padding: 2px;
 }
@@ -1715,9 +1719,13 @@ body.ttrss_utility.share_popup .content {
   font-size: 13px;
   padding: 0px;
 }
-.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner {
   line-height: 10px;
 }
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder {
+  line-height: 15px;
+  margin-left: 0;
+}
 .flat .dijitToolbar label {
   position: relative;
   top: 2px;
diff --git a/themes/light/dijit_basic.less b/themes/light/dijit_basic.less
index 0582ddc5c7170a3aaf4611e0f5de78d0ce52694d..da1138986d725340dad890aeb4771f02c852764d 100644
--- a/themes/light/dijit_basic.less
+++ b/themes/light/dijit_basic.less
@@ -41,8 +41,15 @@
 		font-size: 13px;
 		padding: 0px;
 
-		.dijitTextBox .dijitInputInner {
-			line-height : 10px;
+		.dijitTextBox .dijitInputContainer {
+			.dijitInputInner {
+				line-height : 10px;
+			}
+
+			.dijitPlaceHolder {
+				line-height : 15px;
+				margin-left : 0;
+			}
 		}
 
 		label {
diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less
index 9044afb246210aff6996db21911912664353b3d8..36b73b4f474623976f79390c5ca0d4c2e70e33de 100644
--- a/themes/light/tt-rss.less
+++ b/themes/light/tt-rss.less
@@ -1171,6 +1171,11 @@ body.ttrss_main {
 		height : 200px;
 	}
 
+	.panel-scrollable-400px {
+		overflow : auto;
+		height : 400px;
+	}
+
 	ul.list li {
 		padding : 2px;
 	}
diff --git a/themes/night.css b/themes/night.css
index b30c260f6ab7f961bc8611b5b29db0fd0a08bbbf..1000586cfe69ed063482c6d6d48a1233f1340f0a 100644
--- a/themes/night.css
+++ b/themes/night.css
@@ -989,6 +989,10 @@ body.ttrss_main .panel-scrollable {
   overflow: auto;
   height: 200px;
 }
+body.ttrss_main .panel-scrollable-400px {
+  overflow: auto;
+  height: 400px;
+}
 body.ttrss_main ul.list li {
   padding: 2px;
 }
@@ -1618,9 +1622,13 @@ body.ttrss_utility fieldset > label.checkbox {
   font-size: 13px;
   padding: 0px;
 }
-.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner {
   line-height: 10px;
 }
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder {
+  line-height: 15px;
+  margin-left: 0;
+}
 .flat .dijitToolbar label {
   position: relative;
   top: 2px;
diff --git a/themes/night_blue.css b/themes/night_blue.css
index 46f1aa124b1f4c1f4cc086a9ccf1487199b87e22..4df5f54abf13148a8d2d368d03108802528e2b1d 100644
--- a/themes/night_blue.css
+++ b/themes/night_blue.css
@@ -989,6 +989,10 @@ body.ttrss_main .panel-scrollable {
   overflow: auto;
   height: 200px;
 }
+body.ttrss_main .panel-scrollable-400px {
+  overflow: auto;
+  height: 400px;
+}
 body.ttrss_main ul.list li {
   padding: 2px;
 }
@@ -1618,9 +1622,13 @@ body.ttrss_utility fieldset > label.checkbox {
   font-size: 13px;
   padding: 0px;
 }
-.flat .dijitToolbar .dijitTextBox .dijitInputInner {
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner {
   line-height: 10px;
 }
+.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder {
+  line-height: 15px;
+  margin-left: 0;
+}
 .flat .dijitToolbar label {
   position: relative;
   top: 2px;