diff --git a/src/core/js/form_elements.js b/src/core/js/form_elements.js
index 62b1b01c17ac1a98e9109b8570be627fdd4930c8..248523ba61c3b97ebf14413675abb88edac13ce9 100644
--- a/src/core/js/form_elements.js
+++ b/src/core/js/form_elements.js
@@ -326,7 +326,7 @@ var form_elements = new function () {
          * @returns {HTMLElement} SELECT element with entity options.
          */
         this.make_reference_select = async function (entities, make_desc,
-                make_value, multiple) {
+                make_value, name, multiple) {
             caosdb_utils.assert_array(entities, "param `entities`", false);
             if (typeof make_desc !== "undefined") {
                 caosdb_utils.assert_type(make_desc, "function",
@@ -336,7 +336,7 @@ var form_elements = new function () {
                 caosdb_utils.assert_type(make_value, "function",
                     "param `make_value`");
             }
-            const ret = $(form_elements._make_select(multiple));
+            const ret = $(form_elements._make_select(multiple, name));
             for (let entity of entities) {
                 this.logger.trace("add option", entity);
                 let entity_id = getEntityID(entity);
@@ -349,8 +349,12 @@ var form_elements = new function () {
             return ret[0];
         }
 
-        this._make_select = function (multiple) {
-            const ret = $('<select class="selectpicker form-control" title="Nothing selected"/>');
+        this._make_select = function (multiple, name) {
+            const ret = $(`<select class="selectpicker form-control" name="${name}" title="Nothing selected"/>`);
+            if (typeof name !== "undefined") {
+                caosdb_utils.assert_string(name, "param `name`");
+                ret.attr("name", name);
+            }
             if (multiple) {
                 ret.attr("multiple", "");
             } else {
@@ -405,7 +409,9 @@ var form_elements = new function () {
         }
 
         this._run_script = async function (script, form) {
-            const json_str = JSON.stringify(form_elements.form_to_object(form[0]));
+            const form_objects = form_elements.form_to_object(form[0]);
+            const json_str = JSON.stringify(form_objects[0]);
+
             const params = {
                 "-p0": {
                     "filename": "form.json",
@@ -414,6 +420,15 @@ var form_elements = new function () {
                     })
                 }
             };
+
+            const files = form_objects[1];
+            for (let i = 0; i < files.length; i++) {
+                params[`file_${i}`] = {
+                    "filename": `${files[i]["fieldname"]}_${files[i]["filename"]}`,
+                    "blob": files[i]["blob"]
+                };
+            }
+
             const result = await connection.runScript(script, params);
             this.logger.debug("server-side script returned", result);
             return this.parse_script_result(result);
@@ -464,15 +479,15 @@ var form_elements = new function () {
     this.make_reference_drop_down = function (config) {
         let ret = $(this._make_field_wrapper(config.name));
         let label = this._make_input_label_str(config);
-        let loading = $('<div class="caosdb-f-field-not-ready">loading...</div>');
+        let loading = $(createWaitingNotification("loading..."))
+            .addClass("caosdb-f-field-not-ready");
         let input_col = $('<div class="col-sm-9"/>');
 
         input_col.append(loading);
         this._query(config.query).then(async function (entities) {
             let select = $(await form_elements.make_reference_select(
-                entities, config.make_desc, config.make_value, config.multiple,
+                entities, config.make_desc, config.make_value, config.name, config.multiple,
                 config.value));
-            select.attr("name", config.name);
             loading.remove();
             input_col.append(select);
             form_elements.init_select_picker(ret[0], config.value);
@@ -590,8 +605,8 @@ var form_elements = new function () {
         this.logger.trace("entity form_to_json", form);
         caosdb_utils.assert_html_element(form, "parameter `form`");
 
-        const _to_json = (element, data) => {
-            this.logger.trace("enter element_to_json", element, data);
+        const _to_json = (element, data, files) => {
+            this.logger.trace("enter element_to_json", element, data, files);
 
             for (const child of element.children) {
                 // ignore disabled fields and subforms
@@ -603,7 +618,7 @@ var form_elements = new function () {
                 if (is_subform) {
                     const subform = $(child).data("subform-name");
                     // recursive
-                    var subform_obj = _to_json(child, {});
+                    var subform_obj = _to_json(child, {}, files)[0];
                     if (typeof data[subform] === "undefined") {
                         data[subform] = subform_obj;
                     } else if (Array.isArray(data[subform])) {
@@ -614,7 +629,27 @@ var form_elements = new function () {
                 } else if (name && name !== "") {
                     // input elements
                     const not_checkbox = !$(child).is(":checkbox");
-                    if (not_checkbox || $(child).is(":checked")) {
+                    const is_file = $(child).is("input:file");
+                    if (is_file) {
+                        var fileList = child.files;
+                        if(fileList.length > 0) {
+                            for (let i = 0; i < fileList.length; i++) {
+                                value = name + "_" + fileList[i].name;
+                                if (typeof data[name] === "undefined") {
+                                    data[name] = value
+                                } else if (Array.isArray(data[name])) {
+                                    data[name].push(value);
+                                } else {
+                                    data[name] = [data[name], value]
+                                }
+                                files.push({
+                                    "fieldname": name,
+                                    "filename": fileList[i].name,
+                                    "blob": fileList[i]
+                                });
+                            }
+                        }
+                    } else if (not_checkbox || $(child).is(":checked")) {
                         // checked or not a checkbox
                         var value = $(child).val();
                         if (typeof data[name] === "undefined") {
@@ -629,15 +664,15 @@ var form_elements = new function () {
                     }
                 } else if (child.children.length > 0) {
                     // recursive
-                    _to_json(child, data);
+                    _to_json(child, data, files);
                 }
             }
 
-            this.logger.trace("leave element_to_json", element, data);
-            return data;
+            this.logger.trace("leave element_to_json", element, data, files);
+            return [ data, files ];
         };
 
-        const ret = _to_json(form, {});
+        const ret = _to_json(form, {}, []);
         this.logger.trace("leave form_to_json", ret);
         return ret;
     }
@@ -657,11 +692,9 @@ var form_elements = new function () {
     }
 
     /**
-     * TODO make syncronous
-     *
      * @return {HTMLElement}
      */
-    this.make_form_field = async function (config) {
+    this.make_form_field = function (config) {
         caosdb_utils.assert_type(config, "object", "param `config`");
         caosdb_utils.assert_string(config.type, "`config.type` of param `config`");
 
@@ -669,6 +702,8 @@ var form_elements = new function () {
         const type = config.type;
         if (type === "date") {
             field = this.make_date_input(config);
+        } else if (type === "file") {
+            field = this.make_file_input(config);
         } else if (type === "checkbox") {
             field = this.make_checkbox_input(config);
         } else if (type === "text") {
@@ -678,14 +713,13 @@ var form_elements = new function () {
         } else if (type === "integer") {
             field = this.make_integer_input(config);
         } else if (type === "range") {
-            field = await this.make_range_input(config);
+            field = this.make_range_input(config);
         } else if (type === "reference_drop_down") {
             field = this.make_reference_drop_down(config);
         } else if (type === "select") {
             field = this.make_select_input(config);
         } else if (type === "subform") {
-            // TODO handle cache and required for subforms
-            return await this.make_subform(config);
+            return this.make_subform(config);
         } else {
             throw new TypeError("undefined field type `" + type + "`");
         }
@@ -745,7 +779,7 @@ var form_elements = new function () {
         var header = this.make_heading(config);
         wrapper.append(header);
 
-        var loading = $('<div>loading...</div>');
+        var loading = $(createWaitingNotification("loading..."));
         var logger = this.logger;
         var cancel = (e) => {
             logger.trace("cancel form", e);
@@ -812,9 +846,9 @@ var form_elements = new function () {
     }
 
     /**
-     * TODO make syncronous
+     * @return {HTMLElement}
      */
-    this.make_subform = async function (config) {
+    this.make_subform = function (config) {
         this.logger.trace("enter make_subform");
         caosdb_utils.assert_type(config, "object", "param `config`");
         caosdb_utils.assert_string(config.name, "`config.name` of param `config`");
@@ -825,7 +859,7 @@ var form_elements = new function () {
 
         for (let field of config.fields) {
             this.logger.trace("add subform field", field);
-            let elem = await this.make_form_field(field);
+            let elem = this.make_form_field(field);
             form.append(elem);
         }
 
@@ -875,6 +909,7 @@ var form_elements = new function () {
 
     this.disable_fields = function (fields) {
         $(fields).toggleClass("caosdb-f-field-disabled", true).hide();
+        $(fields).find(":input").prop("required", false);
         for (const field of $(fields)) {
             field.dispatchEvent(this.field_disabled_event);
         }
@@ -882,6 +917,7 @@ var form_elements = new function () {
 
     this.enable_fields = function (fields) {
         $(fields).toggleClass("caosdb-f-field-disabled", false).show();
+        $(fields).filter(".caosdb-f-form-field-required").find("input.caosdb-f-property-single-raw-value, select.selectpicker").prop("required", true);
         for (const field of $(fields)) {
             field.dispatchEvent(this.field_enabled_event);
         }
@@ -934,9 +970,9 @@ var form_elements = new function () {
      * The `config.fields` array may contain `form_elements.field_config`
      * objects or HTMLElements.
      *
-     * TODO
+     * @return {HTMLElement}
      */
-    this.make_generic_form = async function (config) {
+    this.make_generic_form = function (config) {
         this.logger.trace("enter make_generic_form");
 
         caosdb_utils.assert_type(config, "object", "param `config`");
@@ -956,7 +992,7 @@ var form_elements = new function () {
             if (field instanceof HTMLElement) {
                 form.append(field);
             } else {
-                let elem = await this.make_form_field(field);
+                let elem = this.make_form_field(field);
                 form.append(elem);
             }
         }
@@ -1135,9 +1171,9 @@ var form_elements = new function () {
     }
 
     /**
-     * TODO make syncronous
+     * @return {HTMLElement}
      */
-    this.make_range_input = async function (config) {
+    this.make_range_input = function (config) {
 
         // TODO 
         // 1. wrapp both inputs to separate it from the label into a container
@@ -1154,8 +1190,8 @@ var form_elements = new function () {
             type: "double"
         }, config.to);
 
-        const from_input = await this.make_form_field(from_config);
-        const to_input = await this.make_form_field(to_config);
+        const from_input = this.make_form_field(from_config);
+        const to_input = this.make_form_field(to_config);
 
         const ret = $(this._make_field_wrapper(config.name));
         if (config.label) {
@@ -1191,10 +1227,16 @@ var form_elements = new function () {
             .css({"padding": "0"})[0];
     }
 
+    /**
+     * @return {HTMLElement}
+     */
     this.make_date_input = function (config) {
         return this._make_input(config);
     }
 
+    /**
+     * @return {HTMLElement}
+     */
     this.make_text_input = function (config) {
         return this._make_input(config);
     }
@@ -1229,9 +1271,32 @@ var form_elements = new function () {
     this.make_integer_input = function (config) {
         var ret = $(this.make_double_input(config));
         ret.find("input").attr("step", "1");
+        if (config.min) {
+            ret.find("input").attr("min", config.min);
+        }
+        if (config.max) {
+            ret.find("input").attr("max", config.max);
+        }
         return ret[0];
     }
 
+    /**
+     * @return {HTMLElement}
+     */
+    this.make_file_input = function (config) {
+        const ret = this._make_input(config);
+        $(ret)
+            .find("input:file")
+            .prop("multiple", !!config.multiple)
+            .css({"display": "block"});
+        if (config.accept) {
+            $(ret)
+                .find("input:file")
+                .attr("accept", config.accept);
+        }
+
+        return ret;
+    }
 
     /**
      * Return a select field.
@@ -1241,23 +1306,15 @@ var form_elements = new function () {
      */
     this.make_select_input = function (config) {
         const options = config.options;
-        const multiple = config.multiple;
-        const select = $(form_elements._make_select(multiple));
+        const select = $(form_elements._make_select(config.multiple, config.name));
 
         for (let option of options) {
             select.append(form_elements._make_option(option.value, option.label));
         }
+        const ret = form_elements._make_input(config, select[0]);
         form_elements.init_select_picker(select[0], config.value);
 
-        const ret = $(form_elements._make_field_wrapper(config.name));
-        const label = form_elements._make_input_label_str(config);
-        select.change(function () {
-            ret[0].dispatchEvent(form_elements.field_changed_event);
-        });
-
-        const input_col = $('<div class="caosdb-f-property-value col-sm-9"/>');
-        input_col.append(select);
-        return ret.append(label, input_col)[0];
+        return ret;
     }
 
 
@@ -1380,23 +1437,23 @@ var form_elements = new function () {
      *      optional `label`
      * @returns {HTMLElement} a form field.
      */
-    this._make_input = function (config) {
+    this._make_input = function (config, input) {
         caosdb_utils.assert_string(config.name, "the name of a form field");
         let ret = $(this._make_field_wrapper(config.name));
         let name = config.name;
         let label = this._make_input_label_str(config);
         let type = config.type || "text";
         let value = config.value;
-        let input = $('<input class="form-control caosdb-f-property-single-raw-value" type="' + type +
-            '" name="' + name +
-            '" />');
-        input.change(function () {
+        const _input = $(input ||
+            '<input class="form-control caosdb-f-property-single-raw-value" type="'
+            + type + '" name="' + name + '" />');
+        _input.change(function () {
             ret[0].dispatchEvent(form_elements.field_changed_event);
         });
         let input_col = $('<div class="caosdb-f-property-value col-sm-9"/>');
-        input_col.append(input);
+        input_col.append(_input);
         if (value) {
-            input.val(value);
+            _input.val(value);
         }
         return ret.append(label, input_col)[0];
     }
diff --git a/test/core/js/modules/form_elements.js.js b/test/core/js/modules/form_elements.js.js
index 5b595ee06f892f45699c8c6ae747d81f5a379cb5..1bf83675aa2b5ad8d33834fff31b47cd10158b3d 100644
--- a/test/core/js/modules/form_elements.js.js
+++ b/test/core/js/modules/form_elements.js.js
@@ -64,8 +64,6 @@ QUnit.test("make_reference_option", function(assert) {
 
 QUnit.test("make_reference_select", async function(assert) {
     assert.equal(typeof form_elements.make_reference_select, "function", "function available");
-    //assert.throws(()=> unasync(form_elements.make_reference_select), /param `entities` is expected to be an array/, "undefined entities throws");
-    //assert.throws(()=> unasync(form_elements.make_reference_select, "test"), /param `entities` is expected to be an array/, "string entities throws");
     var select = await form_elements.make_reference_select([
         {dataset: {entityId : "id17"}},
         {dataset: {entityId : "id18"}},
@@ -101,6 +99,11 @@ QUnit.test("make_script_form", async function(assert) {
         ],
         fields: [
             {type: "date", name: "baldate"},
+            {type: "select", name: "Sex", label: "Sex", value: "female", required: true, options: [
+                { value: "female", label: "female" },
+                { value: "diverse", label: "diverse" },
+                { value: "male", label: "male" }
+            ]}
         ],
     };
 
@@ -115,8 +118,9 @@ QUnit.test("make_script_form", async function(assert) {
     assert.equal(cancel_button.length, 1, "has cancel button");
 
     var field = $(script_form).find(".caosdb-f-field");
-    assert.equal(field.length, 1, "has one field");
+    assert.equal(field.length, 2, "has two field");
     assert.equal(field.find("input[type='date']").length, 1, "has date input");
+    assert.equal(field.find("select").length, 1, "has select input");
 
     script_form.addEventListener("caosdb.form.cancel", function(e) {
         done();
@@ -221,7 +225,7 @@ QUnit.test("make_form_field", async function(assert) {
 
 
 QUnit.test("make_subform", async function(assert) {
-    assert.equal(typeof form_elements.make_reference_drop_down, "function", "function available");
+    assert.equal(typeof form_elements.make_subform, "function", "function available");
 
     const config = {
         type: "subform",
@@ -274,7 +278,7 @@ QUnit.test("make_checkbox_input", function(assert) {
     assert.equal(input.attr("name"), "approved", "input has name");
     assert.ok(input.is(":checked"), "input is checked");
 
-    var obj = form_elements.form_to_object(field);
+    var obj = form_elements.form_to_object(field)[0];
     assert.equal(obj["approved"], "yes!!!", "checked value");
 
 
@@ -286,7 +290,7 @@ QUnit.test("make_checkbox_input", function(assert) {
     assert.equal(input.attr("name"), "approved", "input has name");
     assert.notOk(input.is(":checked"), "input is not checked");
 
-    obj = form_elements.form_to_object(field);
+    obj = form_elements.form_to_object(field)[0];
     assert.equal(typeof obj["approved"], "undefined", "no checked value");
 
 
@@ -309,7 +313,7 @@ QUnit.test("form_to_object", async function(assert) {
 
     var form = await form_elements.make_script_form(config, "bla");
 
-    var json = form_elements.form_to_object(form);
+    var json = form_elements.form_to_object(form)[0];
 
     assert.equal(typeof json["cancel"], "undefined", "cancel button not serialized");
     assert.equal(json["the-date"], "", "date");
@@ -638,6 +642,7 @@ QUnit.test("make_select_input", function(assert) {
     }
     const select = $(form_elements.make_select_input(config));
     assert.equal(select.find("select").length, 1, "select input there");
+    assert.equal(select.find("select").attr("name"), "sex", "has select with correct name");
     assert.equal(select.find("select option").length, 3, "three options there");
 });