diff --git a/question/bank/customfields/classes/plugin_feature.php b/question/bank/customfields/classes/plugin_feature.php
index 10e863a1484..69d8d69b5d4 100644
--- a/question/bank/customfields/classes/plugin_feature.php
+++ b/question/bank/customfields/classes/plugin_feature.php
@@ -18,7 +18,7 @@ namespace qbank_customfields;
 
 use core_question\local\bank\plugin_features_base;
 use core_question\local\bank\view;
-use qbank_customfields\customfield\question_handler;
+use qbank_customfields\customfield\question_handler ;
 
 /**
  * Class plugin_feature is the entrypoint for the columns.
@@ -57,4 +57,8 @@ class plugin_feature extends plugin_features_base {
 
         return $columns;
     }
+
+    public function get_question_handler(): question_handler {
+        return question_handler::create();
+    }
 }
diff --git a/question/bank/editquestion/classes/editquestion_helper.php b/question/bank/editquestion/classes/editquestion_helper.php
index 77493bbc888..b06a9a34de8 100644
--- a/question/bank/editquestion/classes/editquestion_helper.php
+++ b/question/bank/editquestion/classes/editquestion_helper.php
@@ -101,4 +101,44 @@ class editquestion_helper {
         return $statuslist;
     }
 
+    /**
+     * Get an array of form handlers from plugin features for all enabled qbank plugins..
+     *
+     * @return array
+     */
+    public static function get_question_form_handlers(): array {
+        $handlers = [];
+        $plugins = \question_bank::get_all_qbank_plugin_features();
+        $colmanager = null;
+        foreach ($plugins as $componentname => $pluginclass) {
+            if ($handler = $pluginclass->get_question_handler()) {
+                $handlers[$componentname] = $handler;
+            }
+            if(!$colmanager) {
+                $colmanager = $pluginclass->get_column_manager();
+            }
+        }
+        // Sort plugin list by column order or component name.
+        if(isset($colmanager)) {
+            $cols = array_flip(array_keys($handlers));
+            foreach($cols as $key => $value ) {
+                foreach($colmanager->columnorder as $col => $order) {
+                    if(strpos($col, $key) !== false) {
+                        $cols[$key] = $order;
+                        break;
+                    }
+                }
+            }
+            asort($cols);
+            foreach($cols as $key => $order) {
+                $cols[$key] = $handlers[$key];
+            }
+            $handlers = $cols;
+
+        } else {
+            ksort($handlers);
+        }
+        return $handlers;
+    }
+
 }
diff --git a/question/bank/editquestion/question.php b/question/bank/editquestion/question.php
index c4b7479dfc5..58b1722b2ad 100644
--- a/question/bank/editquestion/question.php
+++ b/question/bank/editquestion/question.php
@@ -232,9 +232,11 @@ if ($cm !== null) {
 
 $toform->inpopup = $inpopup;
 
-// Prepare custom fields data.
-$customfieldhandler = qbank_customfields\customfield\question_handler::create();
-$customfieldhandler->instance_form_before_set_data($toform);
+// Prepare qbank plugins fields data (include customfields).
+$qbankfieldhandlers = \qbank_editquestion\editquestion_helper::get_question_form_handlers();
+foreach($qbankfieldhandlers as $handler) {
+    $handler->instance_form_before_set_data($toform);
+}
 
 $mform->set_data($toform);
 
@@ -304,8 +306,11 @@ if ($mform->is_cancelled()) {
                 context_course::instance($fromform->courseid), $fromform->coursetags, 0);
     }
 
-    // Update custom fields if there are any of them in the form.
-    $customfieldhandler->instance_form_save($fromform);
+    // Update qbank plugins fields if there are any of them in the form.
+    foreach($qbankfieldhandlers as $handler) {
+        $handler->instance_form_save($fromform);
+    }
+
 
     // Purge this question from the cache.
     question_bank::notify_question_edited($question->id);
diff --git a/question/classes/local/bank/plugin_features_base.php b/question/classes/local/bank/plugin_features_base.php
index 904fa407d51..19529abd00d 100644
--- a/question/classes/local/bank/plugin_features_base.php
+++ b/question/classes/local/bank/plugin_features_base.php
@@ -31,6 +31,8 @@ use core\context;
 use qbank_columnsortorder\local\qbank\column_action_move;
 use qbank_columnsortorder\local\qbank\column_action_remove;
 use qbank_columnsortorder\local\qbank\column_action_resize;
+use core_question\local\bank\question_edit_handler_base as edithandler;
+use \core_customfield\handler as cfhandler;
 
 /**
  * Class plugin_features_base is the base class for qbank plugins.
@@ -114,4 +116,15 @@ class plugin_features_base {
     public function get_question_filters(view $qbank = null): array {
         return [];
     }
+
+    /**
+     * This method will return a form handler object, if this plugin provides one.
+     *
+     * The question edit form handler takes care of adding and managing fields into question MoodleQuickForm
+     *
+     * @return question_edit_handler_base, or customfield_handler or null if not used
+     */
+    public function get_question_handler(): edithandler|cfhandler|null  {
+        return null;
+    }
 }
diff --git a/question/engine/bank.php b/question/engine/bank.php
index 61224337184..6b3c2eb3dd2 100644
--- a/question/engine/bank.php
+++ b/question/engine/bank.php
@@ -477,6 +477,26 @@ abstract class question_bank {
         $qtypes = $DB->get_fieldset_sql($sql, $params);
         return $qtypes;
     }
+
+    /**
+     * Get an array of plugin features objects for all enabled qbank plugins.
+     *
+     * @return array the list of qbank plugins
+     */
+    public static function get_all_qbank_plugin_features(): array {
+        $features = [];
+        $plugins = \core_component::get_plugin_list_with_class('qbank', 'plugin_feature', 'plugin_feature.php');
+        foreach ($plugins as $componentname => $pluginclass) {
+            if (!\core\plugininfo\qbank::is_plugin_enabled($componentname)) {
+                continue;
+            }
+            $features[$componentname] = new $pluginclass();
+        }
+        // Sort plugin list by component name.
+        ksort($features);
+        return $features;
+    }
+
 }
 
 
diff --git a/question/type/edit_question_form.php b/question/type/edit_question_form.php
index 18daca2c286..ce6d649f49c 100644
--- a/question/type/edit_question_form.php
+++ b/question/type/edit_question_form.php
@@ -92,10 +92,8 @@ abstract class question_edit_form extends question_wizard_form {
     public $fileoptions;
     /** @var object instance of question type */
     public $instance;
-    /** @var object instance of custom field */
-    protected $customfieldhandler;
-    /** @var bool custom field plugin enabled or disabled*/
-    protected $customfieldpluginenabled = true;
+    /** @var array of instances of question_edit_handlers */
+    protected $qbankfieldhandlers = [];
 
     public function __construct($submiturl, $question, $category, $contexts, $formeditable = true) {
         global $DB;
@@ -121,9 +119,7 @@ abstract class question_edit_form extends question_wizard_form {
         $this->category = $category;
         $this->categorycontext = context::instance_by_id($category->contextid);
 
-        if (!\core\plugininfo\qbank::is_plugin_enabled('qbank_customfields')) {
-            $this->customfieldpluginenabled = false;
-        }
+        $this->qbankfieldhandlers = \qbank_editquestion\editquestion_helper::get_question_form_handlers();
 
         parent::__construct($submiturl, null, 'post', '', ['data-qtype' => $this->qtype()], $formeditable);
     }
@@ -255,18 +251,15 @@ abstract class question_edit_form extends question_wizard_form {
         // Any questiontype specific fields.
         $this->definition_inner($mform);
 
+        if ($this->qbankfieldhandlers) {
+            $this->add_qbank_fields($mform);
+        }
+
         if (core_tag_tag::is_enabled('core_question', 'question')
             && \core\plugininfo\qbank::is_plugin_enabled('qbank_tagquestion')) {
             $this->add_tag_fields($mform);
         }
 
-        if ($this->customfieldpluginenabled) {
-            // Add custom fields to the form.
-            $this->customfieldhandler = qbank_customfields\customfield\question_handler::create();
-            $this->customfieldhandler->set_parent_context($this->categorycontext); // For question handler only.
-            $this->customfieldhandler->instance_form_definition($mform, empty($this->question->id) ? 0 : $this->question->id);
-        }
-
         $this->add_hidden_fields();
 
         $mform->addElement('hidden', 'qtype');
@@ -311,9 +304,12 @@ abstract class question_edit_form extends question_wizard_form {
      */
     public function definition_after_data() {
         $mform = $this->_form;
-        if ($this->customfieldpluginenabled) {
-            $this->customfieldhandler->instance_form_definition_after_data($mform,
-                empty($this->question->id) ? 0 : $this->question->id);
+
+        if ($this->qbankfieldhandlers) {
+            foreach($this->qbankfieldhandlers as $handler) {
+                $handler->instance_form_definition_after_data($mform,
+                            empty($this->question->id) ? 0 : $this->question->id);
+            }
         }
     }
 
@@ -354,6 +350,34 @@ abstract class question_edit_form extends question_wizard_form {
         return $repeated;
     }
 
+    /**
+     * Add the fields defined in qbank plugins to the mform.
+     *
+     * @param object $mform The form being built
+     */
+    protected function add_qbank_fields($mform) {
+
+        $instanceid = empty($this->question->id) ? 0 : $this->question->id;
+        foreach($this->qbankfieldhandlers as $component => $handler) {
+            $prefix = ($component == 'qbank_customfields') ? 'customfield_' : $component . '_';
+            $previous = $mform->_elementIndex;
+            $handler->set_parent_context($this->categorycontext); // For question handler only.
+            $handler->instance_form_definition($mform, $instanceid);
+            $after = $mform->_elementIndex;
+            $added = array_flip(array_diff_key($after, $previous));
+            // Enforce prefixing fields to avoid conflicts between plugins
+            foreach($added as $elname) {
+                if((strpos($elname, $prefix) !== 0) && !(($prefix == 'customfield_') && ($mform->getElementType($elname) == 'header'))  ) {
+                    throw new \moodle_exception("An element added to question form lacks the proper prefix for $component fields");
+                }
+            }
+            // if user cannot edit, freeze all fields added, if any
+            if(($component != 'qbank_customfields') && !$handler->can_edit($instanceid)) {
+                $mform->freeze($added);
+            }
+        }
+    }
+
     /**
      * Add the tag and course tag fields to the mform.
      *
@@ -894,9 +918,10 @@ abstract class question_edit_form extends question_wizard_form {
             }
         }
 
-        if ($this->customfieldpluginenabled) {
-            // Add the custom field validation.
-            $errors  = array_merge($errors, $this->customfieldhandler->instance_form_validation($fromform, $files));
+        if ($this->qbankfieldhandlers) {
+            foreach($this->qbankfieldhandlers as $handler) {
+                $errors  = array_merge($errors, $handler->instance_form_validation($fromform, $files));
+            }
         }
         return $errors;
     }
