diff --git a/jupyter-openbis-extension/sample.py b/jupyter-openbis-extension/sample.py
index 752dfe710c37eb4cf0af6fdf48d8b3fd63310f8a..3b0bf5468ff9a09a3ed0110388facb23c9fed977 100644
--- a/jupyter-openbis-extension/sample.py
+++ b/jupyter-openbis-extension/sample.py
@@ -5,8 +5,8 @@ from .connection import openbis_connections
 from urllib.parse import parse_qs
 
 
-
 def get_entity_for_identifier(conn, identifier):
+    entity = None
     try:
         entity = conn.openbis.get_sample(identifier)
     except Exception as exc:
diff --git a/jupyter-openbis-extension/static/downloadDialog.js b/jupyter-openbis-extension/static/downloadDialog.js
index 304d4a7f75b6a608b9dee59f21bba066a4fb8863..b948043a7f2c414249b059b0513df21177ad0239 100644
--- a/jupyter-openbis-extension/static/downloadDialog.js
+++ b/jupyter-openbis-extension/static/downloadDialog.js
@@ -1,10 +1,11 @@
 define([
         "base/js/dialog",
+        "jquery",
         "./common",
         "./state",
         "./entitySearcher"
     ],
-    function (dialog, common, state, entitySearcher) {
+    function (dialog, $, common, state, entitySearcher) {
 
         var spinner = document.createElement("IMG")
         spinner.className="openbis-feedback"
@@ -120,9 +121,9 @@ define([
                 return false
             }
 
-            currentEntityIdentifier = entityIdentifier.value
+            currentEntityIdentifier = entityIdentifier.firstChild.value
             if (!currentEntityIdentifier) {
-                alert('Please specify a Sample or Experiment identifier/permId')
+                alert('Please specify an Entity identifier/permId')
                 return false
             }
             var url = env.notebook.base_url 
@@ -168,19 +169,11 @@ define([
 
                 var showDataSets = document.createElement("DIV")
                 var title = document.createElement("STRONG")
-                title.textContent = "Sample or Experiment identifier/permId: "
+                title.textContent = "Entity identifier/permId: "
                 showDataSets.appendChild(title)
                 showDataSets.style.marginTop = '10px'
 
-                // TODO Build and replace this component
-                // var entityIdentifier = entitySearcher.getEntitySearcher(state)
-
-                var entityIdentifier = document.createElement("INPUT")
-                entityIdentifier.type = "text"
-                entityIdentifier.name = "entityIdentifier"
-                entityIdentifier.size = 40
-                entityIdentifier.placeholder = "Sample or Experiment identifier/permId"
-                entityIdentifier.value = state.entityIdentifier
+                var entityIdentifier = entitySearcher.getEntitySearcherForDownload(state)
 
                 var datasets_table = document.createElement("DIV")
                 var pagingContainer = document.createElement("DIV")
@@ -228,7 +221,6 @@ define([
                 download_dialog_box.appendChild(path)
 
                 function saveState() {
-                    state.entityIdentifier = entityIdentifier.value
                     state.directPermId = datasetPermId.value
                     state.workingDirectory = downloadPath.value
                 }
diff --git a/jupyter-openbis-extension/static/entitySearcher.js b/jupyter-openbis-extension/static/entitySearcher.js
index 1b74fb9945a1896056574b23baf84b9e129f6554..889db406d2be343c36739cf6107c6ed26a4c455a 100644
--- a/jupyter-openbis-extension/static/entitySearcher.js
+++ b/jupyter-openbis-extension/static/entitySearcher.js
@@ -1,78 +1,240 @@
-define([],
-    function () {
+define(["jquery", "./jquery-select2/js/select2.min"],
+    function($, select2) {
         return {
-            loadResorce(pathToResource, onLoad, jsOrCss) {
-                var resource = null;
+            loadResource(pathToResource, jsOrCss, onLoad) {
+                var resource = null
 
-                if(jsOrCss === 'js') {
-                    resource = document.createElement('script');
-                    resource.type = 'text/javascript';
-                    resource.src = pathToResource;
+                if (jsOrCss === 'js') {
+                    resource = document.createElement('script')
+                    resource.type = 'text/javascript'
+                    resource.src = pathToResource
                 }
-                
-                if(jsOrCss === 'css') {
-                    resource = document.createElement('link');
-                    resource.type = 'text/css';
-                    resource.rel = 'stylesheet';
-                    resource.href = pathToResource;
+
+                if (jsOrCss === 'css') {
+                    resource = document.createElement('link')
+                    resource.type = 'text/css'
+                    resource.rel = 'stylesheet'
+                    resource.href = pathToResource
                 }
 
-                resource.onload = onLoad;
-                resource.onreadystatechange= function () {
-                        if (this.readyState == 'complete') { 
-                            onLoad();
-                        }
+                resource.onload = onLoad
+                resource.onreadystatechange = function() {
+                    if (this.readyState == 'complete') {
+                        onLoad()
+                    }
                 }
-                
-                var head = document.getElementsByTagName('head')[0];
-                head.appendChild(resource);
+
+                var head = document.getElementsByTagName('head')[0]
+                head.appendChild(resource)
             },
             getRequireJSV3Config(baseURL) {
-            	return {
-					baseUrl : baseURL + "/openbis/resources/api/v3",
-					paths : {
-						"stjs" : "lib/stjs/js/stjs",
-						"underscore" : "lib/underscore/js/underscore",
-						"moment" : "lib/moment/js/moment"
-					},
-					shim : {
-						"stjs" : {
-							exports : "stjs",
-							deps : [ "underscore" ]
-						},
-						"underscore" : {
-							exports : "_"
-						}
-					}
-				}
-			},
-            getEntitySearcher(state) {
-            	var _this = this;
+                return {
+                    baseUrl: baseURL + "/openbis/resources/api/v3",
+                    paths: {
+                        "stjs": "lib/stjs/js/stjs",
+                        "underscore": "lib/underscore/js/underscore",
+                        "moment": "lib/moment/js/moment"
+                    },
+                    shim: {
+                        "stjs": {
+                            exports: "stjs",
+                            deps: ["underscore"]
+                        },
+                        "underscore": {
+                            exports: "_"
+                        }
+                    }
+                }
+            },
+            getEntitySearcherForDownload(state) {
+                return this.getEntitySearcher(state, false)
+            },
+            getEntitySearcherForUpload(state) {
+                return this.getEntitySearcher(state, true)
+            },
+            getEntitySearcher(state, upload) {
+                var _this = this
                 var connection_name = state.connection.name
                 if (!connection_name) {
                     alert('Please choose a connection')
                     return false
                 }
 
-                if(!state.openbisService) {
-                	var config = this.getRequireJSV3Config(state.connection.dto.url)
-                	require.config(config)
-	                require(['openbis'], function(openbis) {
-						var apiUrl = state.connection.dto.url + "/openbis/openbis/rmi-application-server-v3.json"
-					    var v3 = new openbis(apiUrl)
-					    v3.login(state.connection.dto.username, state.connection.dto.password)
-					    .done(function(sessionToken) {
-					    	state.openbisService = v3
-					    	alert('openbis v3 service login succeed for ' + apiUrl +' : trusted-cross-origin-domains is set.')
-					 	}).fail(function(result) {
-					 		alert('openbis v3 service login failed for ' + apiUrl +' : trusted-cross-origin-domains is probably not set.')
-            			});
-        			});
+                var element = document.createElement("SPAN")
+                element.innerHTML = "<span style='color:orange;margin:5px'>loading...</span>"
+                if (!state.openbisService) {
+                    require.config(this.getRequireJSV3Config(state.connection.dto.url))
+                    require(["openbis", "as/dto/experiment/search/ExperimentSearchCriteria", 
+                             "as/dto/experiment/fetchoptions/ExperimentFetchOptions",
+                             "as/dto/sample/search/SampleSearchCriteria",
+                             "as/dto/sample/fetchoptions/SampleFetchOptions"], function(openbis) {
+                        var apiUrl = state.connection.dto.url + "/openbis/openbis/rmi-application-server-v3.json"
+                        var v3 = new openbis(apiUrl)
+                        v3.login(state.connection.dto.username, state.connection.dto.password)
+                            .done(function(sessionToken) {
+                                state.openbisService = v3
+                                _this.loadResource("/nbextensions/openbis/jquery-select2/css/select2.min.css", 'css', function() {
+                                    _this.createDropdown(element, state, upload)
+                                })
+                            }).fail(function(result) {
+                                alert('openbis v3 service login failed for ' + apiUrl 
+                                        + " : property 'trusted-cross-origin-domains' is probably not set in service.properties.")
+                            })
+                    })
+                } else {
+                    _this.createDropdown(element, state, upload)
                 }
 
-                var element = document.createElement("SPAN")
                 return element
+            },
+            createDropdown(container, state, upload) {
+                var _this = this
+                var $select = $("<select>", {class : 'form-control'})
+                $select.attr("multiple", "multiple")
+                $select.attr("required", "required")
+
+                container.innerHTML = null
+                $select.each(function() {
+                    container.appendChild(this)
+                })
+                $select.select2({
+                    width: upload ? '100%' : '80%', 
+                    maximumSelectionLength: 1,
+                    minimumInputLength: 2,
+                    placeholder : "Entity identifier/permId",
+                    ajax: {
+                        delay: 1000,
+                        processResults: function (data) {
+                            var results = []
+
+                            for(var dIdx = 0; dIdx < data.length; dIdx++) {
+                                var group = {
+                                        text: data[dIdx].type, 
+                                        children : []
+                                }
+
+                                var entities = data[dIdx].objects
+                                for (var eIdx = 0; eIdx < entities.length; eIdx++) {
+                                    group.children.push({
+                                        id : entities[eIdx].permId.permId,
+                                        text : _this.getDisplayName(entities[eIdx]),
+                                        data : {
+                                            id : entities[eIdx].permId.permId,
+                                            text : _this.getDisplayName(entities[eIdx]),
+                                            data : entities[eIdx]
+                                        }
+                                    })
+                                }
+
+                                if (entities.length > 0) {
+                                    results.push(group)
+                                }
+                            }
+
+                            return {
+                                "results": results,
+                                "pagination": {
+                                    "more": false
+                                }
+                            }
+                        },
+                        transport: function (params, success, failure) {
+                            var searchResults = []
+                            _this.searchExperiments(state, params, function(result) {
+                                searchResults.push(result)
+                                _this.searchSamples(state, params, function(result) {
+                                    searchResults.push(result)
+                                    success(searchResults)
+                                })
+                            })
+                            return {
+                                abort : function () { /* Not implemented */ }
+                            }
+                        }
+                    }
+                })
+                $select.on("select2:select", function() {
+                    if (upload) {
+                        state.uploadEntity = _this.getSelected($select)[0]
+                    } else {
+                        state.entity = _this.getSelected($select)[0]
+                    }
+                })
+                if (upload && state.uploadEntity) {
+                    _this.addSelected($select, state.uploadEntity)
+                } else if (upload == false && state.entity) {
+                    _this.addSelected($select, state.entity)
+                }
+            },
+            searchExperiments(state, params, action) {
+                var ExperimentSearchCriteria = require("as/dto/experiment/search/ExperimentSearchCriteria")
+                var searchCriteria = new ExperimentSearchCriteria().withOrOperator()
+                searchCriteria.withCode().thatContains(params.data.q)
+                searchCriteria.withProperty("$NAME").thatContains(params.data.q)
+                var ExperimentFetchOptions = require("as/dto/experiment/fetchoptions/ExperimentFetchOptions")
+                var fetchOptions = new ExperimentFetchOptions()
+                fetchOptions.withProperties()
+                state.openbisService.searchExperiments(searchCriteria, fetchOptions).done(action)
+            },
+            searchSamples(state, params, action) {
+                var SampleSearchCriteria = require("as/dto/sample/search/SampleSearchCriteria")
+                var searchCriteria = new SampleSearchCriteria().withOrOperator()
+                searchCriteria.withCode().thatContains(params.data.q)
+                searchCriteria.withProperty("$NAME").thatContains(params.data.q)
+                var SampleFetchOptions = require("as/dto/sample/fetchoptions/SampleFetchOptions")
+                var fetchOptions = new SampleFetchOptions()
+                fetchOptions.withProperties()
+                state.openbisService.searchSamples(searchCriteria, fetchOptions).done(action)
+            },
+            getDisplayName(entity) {
+                var text = ""
+                var propertyReplacingCode = "$NAME"
+                if (entity.identifier && entity.identifier.identifier) {
+                    text = entity.identifier.identifier
+                }
+                if (entity.properties && entity.properties[propertyReplacingCode]) {
+                    text += " (" + entity.properties[propertyReplacingCode] + ")"
+                }
+                return text
+            },
+            getSelected($select) {
+                var selected = $select.select2('data')
+                var entities = []
+                for (var eIdx = 0; eIdx < selected.length; eIdx++) {
+                    if (selected[eIdx].data) {
+                        entities.push(selected[eIdx].data.data)
+                    }
+                    if (selected[eIdx].element.data) {
+                        entities.push(selected[eIdx].element.data.data)
+                    }
+                }
+                return entities
+            },
+            addSelected($select, v3entity) {
+                var text = this.getDisplayName(v3entity)
+                var id = null
+                if (v3entity.permId && v3entity.permId.permId) { //Only v3 objects supported
+                    id = v3entity.permId.permId
+                } else {
+                    throw {
+                        name: "NonV3ObjectException",
+                        message: "Object without v3 permId",
+                        toString: function() {
+                            return this.name + ": " + this.message
+                        }
+                    }
+                }
+                
+                var data = {
+                        id : id,
+                        text : text,
+                        data : v3entity
+                }
+                
+                var newOption = new Option(text, id, true, true)
+                newOption.data = data
+                $select.append(newOption).trigger('change')
             }
         }
     }
-)
\ No newline at end of file
+)
diff --git a/jupyter-openbis-extension/static/spinner.gif b/jupyter-openbis-extension/static/spinner.gif
new file mode 100644
index 0000000000000000000000000000000000000000..58d18d4c3a5baac5a486e28b639f050baceffb3e
Binary files /dev/null and b/jupyter-openbis-extension/static/spinner.gif differ
diff --git a/jupyter-openbis-extension/static/state.js b/jupyter-openbis-extension/static/state.js
index 2a0f35c8254ced361b30f7c9f122c8affb5b41f0..71ea8e23d5a13982ed94c120171ef2e9674048d1 100644
--- a/jupyter-openbis-extension/static/state.js
+++ b/jupyter-openbis-extension/static/state.js
@@ -12,7 +12,7 @@ define([],
             // upload dialog
             uploadDataSetType: null,
             uploadDataSetTypes: {},
-            uploadEntityIdentifier: '',
+            uploadEntity: null,
             datasetCheckboxes: [],
             fileCheckboxes: [],
             selectedFiles: [],
@@ -20,11 +20,11 @@ define([],
 
             // download dialog
             selectedDatasets: new Set([]),
-            entityIdentifier: '',
+            entity: null,
             workingDirectory: '',
 
             // openBIS v3 connection
-            openBISV3 : null
+            openbisService : null
         }
     }
 )
\ No newline at end of file
diff --git a/jupyter-openbis-extension/static/uploadDialog.js b/jupyter-openbis-extension/static/uploadDialog.js
index 0690d40cf1d5bd58e64824a8eef1ce93fa945b08..9f848ee0a4d2d4dc6a0d8a122a1a2749803aeb06 100644
--- a/jupyter-openbis-extension/static/uploadDialog.js
+++ b/jupyter-openbis-extension/static/uploadDialog.js
@@ -4,9 +4,9 @@ define([
         "jquery",
         "./state",
         "./common",
+        "./entitySearcher"
     ],
-    function (dialog, utils, $, state, common) {
-
+    function (dialog, utils, $, state, common, entitySearcher) {
         var errorElements = { }
         function createErrorElement(name) {
             var element = document.createElement("STRONG")
@@ -23,10 +23,10 @@ define([
         var spinner = document.createElement("IMG")
         spinner.className="openbis-feedback"
         spinner.src=""
-        function showSpinner() {
-            spinner.src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.5.8/ajax-loader.gif"
+        function showSpinner(env) {
+            spinner.src = env.notebook.base_url + "nbextensions/openbis/spinner.gif"
         }
-        function hideSpinner() {
+        function hideSpinner(env) {
             spinner.src=""
         }
 
@@ -68,7 +68,7 @@ define([
                         response.json()
                             .then(function (data) {
                                 var change_input_fields = function () {
-                                    hideSpinner()
+                                    hideSpinner(env)
                                     cleanErrors()
 
                                     var oldType = state.uploadDataSetType
@@ -203,22 +203,16 @@ define([
                 getDatasetTypes(env, state.connection.name, dataset_types, input_fields)
 
                 var sample_title = document.createElement("STRONG")
-                sample_title.textContent = "Sample or Experiment identifier/permId"
+                sample_title.textContent = "Entity"
 
                 var sample_error = createErrorElement('entityIdentifier')
 
-                var entityIdentifier = document.createElement("INPUT")
-                entityIdentifier.type = "text"
-                entityIdentifier.name = 'entityIdentifier'
-                entityIdentifier.placeholder = "Sample or Experiment identifier/permId"
-                entityIdentifier.value = state.uploadEntityIdentifier
-                entityIdentifier.size = "90"
-                entityIdentifier.style.width="100%"
+                var entityIdentifier = entitySearcher.getEntitySearcherForUpload(state)
 
                 var ds_title = document.createElement("STRONG")
                 var dataSetListContainer = document.createElement("DIV")
                 if (env.notebook.metadata.datasets) {
-                    ds_title.textContent = "DataSets"
+                    ds_title.textContent = "Parent DataSets"
                     dataSetListContainer.style.maxHeight="150px"
                     dataSetListContainer.style.overflow="auto"
                     get_dataset_list(env, dataSetListContainer)
@@ -256,7 +250,6 @@ define([
                             state.uploadDataSetTypes[state.uploadDataSetType][element.name] = element.value
                         }
                     }
-                    state.uploadEntityIdentifier = entityIdentifier.value
                     state.unselectedDatasets = state.datasetCheckboxes.filter(cb => !cb.checked).map(cb => cb.value)
                     state.selectedFiles = state.fileCheckboxes.filter(cb => cb.checked).map(cb => cb.value)
                 }
@@ -286,7 +279,7 @@ define([
                         "type": dataset_types.value,
                         "files": files,
                         "parents": state.datasetCheckboxes.filter(cb => cb.checked).map(cb => cb.value),
-                        "entityIdentifier": entityIdentifier.value,
+                        "entityIdentifier": entityIdentifier.firstChild.value,
                         "props": props
                     }
 
@@ -333,7 +326,7 @@ define([
                         }
                     }
 
-                    showSpinner()
+                    showSpinner(env)
                     cleanErrors()
                     utils.ajax(settings)
                     return false