Index: paraview/CMake/ParaViewClient.cmake
===================================================================
--- paraview.orig/CMake/ParaViewClient.cmake	2025-01-08 19:46:29.814538431 +0100
+++ paraview/CMake/ParaViewClient.cmake	2025-01-08 19:46:29.810538392 +0100
@@ -20,6 +20,7 @@
   [PLUGINS_TARGETS  <target>...]
   [REQUIRED_PLUGINS <plugin>...]
   [OPTIONAL_PLUGINS <plugin>...]
+  [PLUGIN_CONF_EXTRA_PATHS <path list>]
 
   [APPLICATION_NAME <name>]
   [ORGANIZATION     <organization>]
@@ -59,6 +60,7 @@
     will be called upon startup.
   * `REQUIRED_PLUGINS`: Plugins to load upon startup.
   * `OPTIONAL_PLUGINS`: Plugins to load upon startup if available.
+  * `PLUGIN_CONF_EXTRA_PATHS`: A list of extra plugin paths (no autoload).
   * `APPLICATION_NAME`: (Defaults to `<NAME>`) The displayed name of the
     application.
   * `ORGANIZATION`: (Defaults to `Anonymous`) The organization for the
@@ -93,7 +95,7 @@
   cmake_parse_arguments(_paraview_client
     ""
     "NAME;APPLICATION_NAME;ORGANIZATION;TITLE;SPLASH_IMAGE;BUNDLE_DESTINATION;BUNDLE_ICON;BUNDLE_PLIST;APPLICATION_ICON;MAIN_WINDOW_CLASS;MAIN_WINDOW_INCLUDE;VERSION;FORCE_UNIX_LAYOUT;PLUGINS_TARGET;DEFAULT_STYLE;RUNTIME_DESTINATION;LIBRARY_DESTINATION;NAMESPACE;EXPORT;TRANSLATION_TARGET;TRANSLATE_XML;TRANSLATIONS_DIRECTORY"
-    "REQUIRED_PLUGINS;OPTIONAL_PLUGINS;APPLICATION_XMLS;SOURCES;QCH_FILES;QCH_FILE;PLUGINS_TARGETS"
+    "REQUIRED_PLUGINS;OPTIONAL_PLUGINS;APPLICATION_XMLS;SOURCES;QCH_FILES;QCH_FILE;PLUGINS_TARGETS;PLUGIN_CONF_EXTRA_PATHS"
     ${ARGN})
 
   if (_paraview_client_UNPARSED_ARGUMENTS)
@@ -471,6 +473,7 @@
       PLUGINS_TARGETS ${_paraview_client_PLUGINS_TARGETS}
       BUILD_DESTINATION   "${_paraview_client_binary_destination}"
       INSTALL_DESTINATION "${_paraview_client_conf_destination}"
+      PLUGIN_CONF_EXTRA_PATHS "${_paraview_client_PLUGIN_CONF_EXTRA_PATHS}"
       COMPONENT "runtime")
   endif ()
 
Index: paraview/CMake/ParaViewPlugin.cmake
===================================================================
--- paraview.orig/CMake/ParaViewPlugin.cmake	2025-01-08 19:46:29.814538431 +0100
+++ paraview/CMake/ParaViewPlugin.cmake	2025-01-08 19:46:29.810538392 +0100
@@ -827,6 +827,7 @@
   BUILD_DESTINATION <destination>
 
   [INSTALL_DESTINATION <destination>]
+  [PLUGIN_CONF_EXTRA_PATHS <path list>]
   [COMPONENT <component>])
 ```
 
@@ -838,6 +839,10 @@
   * `INSTALL_DESTINATION`: Where to install the configuration file in the
     install tree. If not provided, the configuration file will not be
     installed.
+  * `PLUGIN_CONF_EXTRA_PATHS`: (Optional) A list of extra plugin paths
+    searched at runtime to find available plugins without autoloading.
+    Items may be separated by the cmake list separator ';' or by the
+    path separator for the OS (e.g. ':' on linux).
   * `COMPONENT`: (Defaults to `runtime`) The component to use when installing
     the configuration file.
 #]==]
@@ -845,7 +850,7 @@
   cmake_parse_arguments(_paraview_plugin_conf
     ""
     "NAME;BUILD_DESTINATION;INSTALL_DESTINATION;COMPONENT"
-    "PLUGINS_TARGETS"
+    "PLUGINS_TARGETS;PLUGIN_CONF_EXTRA_PATHS"
     ${ARGN})
 
   if (_paraview_plugin_conf_UNPARSED_ARGUMENTS)
@@ -938,6 +943,21 @@
     endif ()
   endforeach ()
 
+  if(_paraview_plugin_conf_PLUGIN_CONF_EXTRA_PATHS)
+    # ":" might be used as path separator on linux, in which case convert to cmake list
+    string (REPLACE ":" ";" _paraview_plugin_conf_extra_paths "${_paraview_plugin_conf_PLUGIN_CONF_EXTRA_PATHS}")
+    foreach (_paraview_plugin_conf_extra_path IN LISTS _paraview_plugin_conf_extra_paths)
+      if (_paraview_plugin_conf_plugins_target_xml_build)
+        string(APPEND _paraview_plugin_conf_build_contents
+          "${_paraview_plugin_conf_extra_path}\n")
+      endif ()
+      if (_paraview_plugin_conf_plugins_target_xml_install)
+        string(APPEND _paraview_plugin_conf_install_contents
+          "${_paraview_plugin_conf_extra_path}\n")
+      endif ()
+    endforeach ()
+  endif()
+
   file(GENERATE
     OUTPUT  "${_paraview_plugin_conf_build_file}"
     CONTENT "${_paraview_plugin_conf_build_contents}")
Index: paraview/Clients/CommandLineExecutables/CMakeLists.txt
===================================================================
--- paraview.orig/Clients/CommandLineExecutables/CMakeLists.txt	2025-01-08 19:46:29.814538431 +0100
+++ paraview/Clients/CommandLineExecutables/CMakeLists.txt	2025-01-08 19:46:29.810538392 +0100
@@ -29,6 +29,7 @@
     PLUGINS_TARGETS ParaView::paraview_plugins
     BUILD_DESTINATION "${CMAKE_INSTALL_BINDIR}"
     INSTALL_DESTINATION "${CMAKE_INSTALL_BINDIR}"
+    PLUGIN_CONF_EXTRA_PATHS "${PARAVIEW_PLUGIN_CONF_EXTRA_PATHS}"
     COMPONENT "runtime")
 endif ()
 
Index: paraview/Clients/ParaView/CMakeLists.txt
===================================================================
--- paraview.orig/Clients/ParaView/CMakeLists.txt	2025-01-08 19:46:29.814538431 +0100
+++ paraview/Clients/ParaView/CMakeLists.txt	2025-01-08 19:46:29.810538392 +0100
@@ -88,6 +88,7 @@
   APPLICATION_ICON  "${CMAKE_CURRENT_SOURCE_DIR}/pvIcon.ico"
   SOURCES           ${sources}
   PLUGINS_TARGETS   ParaView::paraview_plugins
+  PLUGIN_CONF_EXTRA_PATHS "${PARAVIEW_PLUGIN_CONF_EXTRA_PATHS}"
   APPLICATION_XMLS  ${xmls}
   ${_paraview_client_add_translation_args})
 
Index: paraview/Documentation/dev/build.md
===================================================================
--- paraview.orig/Documentation/dev/build.md	2025-01-08 19:46:29.814538431 +0100
+++ paraview/Documentation/dev/build.md	2025-01-08 19:46:29.810538392 +0100
@@ -624,6 +624,23 @@
   * `ParaView_DEBUG_PLUGINS_plugin` (default `OFF`): Log information about
     discovered plugins.
 
+Additional paths to XML plugin config files or directories containing
+plugins may be specified in the build configuration.
+
+  * `PARAVIEW_PLUGIN_LOADER_PATHS`: paths to plugins intended to be
+    autoloaded
+  * `PARAVIEW_PLUGIN_CONF_EXTRA_PATHS`: paths to plugins intended to be
+    available to users but not autoloaded
+
+The argument to these options is a path variable, with multiple paths
+separated by the path separator for the operating system, e.g. on linux
+
+```
+cmake -DPARAVIEW_PLUGIN_CONF_EXTRA_PATHS="/usr/share/paraview/plugins:/opt/paraview/plugins"
+```
+
+(or use `;` as the path separator on Windows)
+
 ### Building documentation
 
 The following targets are used to build documentation for ParaView:
Index: paraview/Documentation/release/dev/plugin-path-no-autoload.md
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ paraview/Documentation/release/dev/plugin-path-no-autoload.md	2025-01-08 19:46:29.810538392 +0100
@@ -0,0 +1,11 @@
+## plugin-path-no-autoload
+
+* Cmake option PARAVIEW_PLUGIN_CONF_EXTRA_PATHS may be used to define
+  plugin directories containing plugins that are not intended to be
+  autoloaded. The found plugins may be activated manually in the
+  PluginManager. The option is analogous to
+  PARAVIEW_PLUGIN_LOADER_PATHS, which autoloads found plugins.
+  Example usage:
+```
+  cmake -DPARAVIEW_PLUGIN_CONF_EXTRA_PATHS="/usr/share/paraview/plugins"
+```
Index: paraview/Remoting/Core/CMakeLists.txt
===================================================================
--- paraview.orig/Remoting/Core/CMakeLists.txt	2025-01-08 19:46:29.814538431 +0100
+++ paraview/Remoting/Core/CMakeLists.txt	2025-01-08 19:46:29.810538392 +0100
@@ -116,7 +116,7 @@
     COMPILE_DEFINITIONS "${vtkPVOptions_defines}")
 
 set(PARAVIEW_PLUGIN_LOADER_PATHS ""
-  CACHE STRING "Extra paths to search for plugins")
+  CACHE STRING "Extra paths to search for plugins (with autoload)")
 mark_as_advanced(PARAVIEW_PLUGIN_LOADER_PATHS)
 if (PARAVIEW_PLUGIN_LOADER_PATHS)
   set_property(SOURCE vtkPVPluginLoader.cxx APPEND
Index: paraview/Remoting/Core/vtkPVPluginTracker.cxx
===================================================================
--- paraview.orig/Remoting/Core/vtkPVPluginTracker.cxx	2025-01-08 19:46:29.814538431 +0100
+++ paraview/Remoting/Core/vtkPVPluginTracker.cxx	2025-01-08 19:46:29.810538392 +0100
@@ -5,6 +5,7 @@
 #include "vtkClientServerInterpreterInitializer.h"
 #include "vtkCommand.h"
 #include "vtkObjectFactory.h"
+#include "vtkPDirectory.h"
 #include "vtkPResourceFileLocator.h"
 #include "vtkPSystemTools.h"
 #include "vtkPVDynamicInitializerPluginInterface.h"
@@ -351,7 +352,15 @@
       line = std::string(exe_dir).append("/").append(line);
     }
 
-    this->LoadPluginConfigurationXML(line.c_str(), false);
+    if (vtksys::SystemTools::FileIsDirectory(line))
+    {
+      // load the plugin dir, but without autoload.
+      this->RegisterPluginsFromPath(line.c_str());
+    }
+    else
+    {
+      this->LoadPluginConfigurationXML(line.c_str(), false);
+    }
   }
 }
 
@@ -567,6 +576,84 @@
 }
 
 //----------------------------------------------------------------------------
+// RegisterPluginsFromPath applies the same logic as vtkPVPluginLoader::LoadPluginsFromPath
+// but only registers plugins without autoloading them
+unsigned int vtkPVPluginTracker::RegisterPluginsFromPath(const char* path)
+{
+  unsigned int num_plugins(0);
+
+  vtkVLogIfF(
+    PARAVIEW_LOG_PLUGIN_VERBOSITY(), path != nullptr, "Registering plugins in Path: %s", path);
+
+  vtkNew<vtkPDirectory> dir;
+  if (dir->Load(path) == false)
+  {
+    vtkVLogIfF(PARAVIEW_LOG_PLUGIN_VERBOSITY(), path != nullptr, "Invalid directory: %s", path);
+    return num_plugins;
+  }
+
+#ifdef _WIN32
+  const char* compiled_extension = ".dll";
+#else
+  const char* compiled_extension = ".so";
+#endif
+
+  for (vtkIdType cc = 0; cc < dir->GetNumberOfFiles(); cc++)
+  {
+    const char* file = dir->GetFile(cc);
+    std::string rel_path;
+    bool has_valid_extension;
+    bool assume_exists = false;
+
+    // If we have a directory, search it for a plugin of the same name.
+    if (dir->FileIsDirectory(file))
+    {
+      rel_path = file;
+      rel_path += '/';
+      rel_path += file;
+      rel_path += compiled_extension;
+      has_valid_extension = true;
+    }
+    else
+    {
+      // We have a file, check to see if its extension is acceptable.
+      rel_path = file;
+      std::string ext = vtksys::SystemTools::GetFilenameLastExtension(rel_path);
+      has_valid_extension =
+        (ext == compiled_extension || ext == ".xml" || ext == ".sl" || ext == ".py");
+      assume_exists = true;
+    }
+
+    // No extension, not a plugin.
+    if (!has_valid_extension)
+    {
+      continue;
+    }
+
+    // Calculate the full path to the plugin.
+    std::string full_file = dir->GetPath();
+    full_file += '/';
+    full_file += rel_path;
+
+    // Check if it exists and is a file.
+    if (!assume_exists && !vtksys::SystemTools::FileExists(full_file, true))
+    {
+      continue;
+    }
+
+    // Register the plugin (without loading)
+    vtkVLogF(PARAVIEW_LOG_PLUGIN_VERBOSITY(), "found `%s`", full_file.c_str());
+    unsigned int index = this->RegisterAvailablePlugin(full_file.c_str());
+    std::vector<std::string> xmls;
+    (*this->PluginsList)[index].AutoLoad = false;
+    (*this->PluginsList)[index].DelayedLoad = false;
+    (*this->PluginsList)[index].XMLs = xmls;
+    num_plugins++;
+  }
+  return num_plugins;
+}
+
+//----------------------------------------------------------------------------
 unsigned int vtkPVPluginTracker::RegisterAvailablePlugin(const char* filename)
 {
   std::string defaultname = vtkGetPluginNameFromFileName(filename);
Index: paraview/Remoting/Core/vtkPVPluginTracker.h
===================================================================
--- paraview.orig/Remoting/Core/vtkPVPluginTracker.h	2025-01-08 19:46:29.814538431 +0100
+++ paraview/Remoting/Core/vtkPVPluginTracker.h	2025-01-08 19:46:29.810538392 +0100
@@ -55,6 +55,15 @@
   void RegisterPlugin(vtkPVPlugin*);
 
   /**
+   * This API is used to register available plugins found under the given
+   * path without actually loading them.
+   *
+   * This fires `vtkPVPluginTracker::RegisterAvailablePluginEvent` to notify a
+   * plugins have been made available.
+   */
+  unsigned int RegisterPluginsFromPath(const char* path);
+
+  /**
    * This API is used to register available plugins without actually loading
    * them.
    *
