diff --git a/core/modules/node/src/Plugin/views/wizard/Node.php b/core/modules/node/src/Plugin/views/wizard/Node.php
index 5f53d97..8c07ad0 100644
--- a/core/modules/node/src/Plugin/views/wizard/Node.php
+++ b/core/modules/node/src/Plugin/views/wizard/Node.php
@@ -169,13 +169,14 @@ protected function defaultDisplayFiltersUser(array $form, FormStateInterface $fo
       }
     }
     if (!empty($tids)) {
-      $vid = reset($form['displays']['show']['tagged_with']['#selection_settings']['target_bundles']);
+      $selection_settings = $form['displays']['show']['tagged_with']['#selection_settings'];
       $filters['tid'] = [
         'id' => 'tid',
         'table' => 'taxonomy_index',
         'field' => 'tid',
         'value' => $tids,
-        'vid' => $vid,
+        'widget' => 'autocomplete',
+        'handler_settings' => $selection_settings,
         'plugin_id' => 'taxonomy_index_tid',
       ];
       // If the user entered more than one valid term in the autocomplete
@@ -252,19 +253,6 @@ protected function buildFilters(&$form, FormStateInterface $form_state) {
 
     // Add the "tagged with" filter to the view.
 
-    // We construct this filter using taxonomy_index.tid (which limits the
-    // filtering to a specific vocabulary) rather than
-    // taxonomy_term_field_data.name (which matches terms in any vocabulary).
-    // This is because it is a more commonly-used filter that works better with
-    // the autocomplete UI, and also to avoid confusion with other vocabularies
-    // on the site that may have terms with the same name but are not used for
-    // free tagging.
-
-    // The downside is that if there *is* more than one vocabulary on the site
-    // that is used for free tagging, the wizard will only be able to make the
-    // "tagged with" filter apply to one of them (see below for the method it
-    // uses to choose).
-
     // Find all "tag-like" taxonomy fields associated with the view's
     // entities. If a particular entity type (i.e., bundle) has been
     // selected above, then we only search for taxonomy fields associated
@@ -292,24 +280,28 @@ protected function buildFilters(&$form, FormStateInterface $form_state) {
     }
     if (!empty($tag_fields)) {
       // If there is more than one "tag-like" taxonomy field available to
-      // the view, we can only make our filter apply to one of them (as
-      // described above). We choose 'field_tags' if it is available, since
-      // that is created by the Standard install profile in core and also
-      // commonly used by contrib modules; thus, it is most likely to be
-      // associated with the "main" free-tagging vocabulary on the site.
-      if (array_key_exists('field_tags', $tag_fields)) {
-        $tag_field_name = 'field_tags';
-      }
-      else {
-        $tag_field_name = key($tag_fields);
+      // the view, we make our filter apply to all of them, and to all their
+      // vocabularies.
+      $target_bundles = [];
+      foreach ($tag_fields as $tag_field) {
+        foreach ($tag_field->getSetting('handler_settings')['target_bundles'] as $vid) {
+          $target_bundles[$vid] = $vid;
+        }
       }
       // Add the autocomplete textfield to the wizard.
-      $target_bundles = $tag_fields[$tag_field_name]->getSetting('handler_settings')['target_bundles'];
       $form['displays']['show']['tagged_with'] = [
         '#type' => 'entity_autocomplete',
         '#title' => $this->t('tagged with'),
         '#target_type' => 'taxonomy_term',
-        '#selection_settings' => ['target_bundles' => $target_bundles],
+        '#selection_settings' => [
+          'target_bundles' => $target_bundles,
+          'sort' => [
+            'field' => 'name',
+            'direction' => 'asc',
+          ],
+          'auto_create' => FALSE,
+          'auto_create_bundle' => '',
+        ],
         '#tags' => TRUE,
         '#size' => 30,
         '#maxlength' => 1024,
diff --git a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
index 9ec9758..2493b0c 100644
--- a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
+++ b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
@@ -117,21 +117,12 @@ views.field.taxonomy_index_tid:
         label: 'Vocabulary'
 
 views.filter.taxonomy_index_tid:
-  type: views.filter.many_to_one
+  type: views.filter.entity_reference
   label: 'Taxonomy term ID'
   mapping:
-    vid:
-      type: string
-      label: 'Vocabulary'
-    type:
-      type: string
-      label: 'Selection type'
     hierarchy:
       type: boolean
       label: 'Show hierarchy in dropdown'
-    limit:
-      type: boolean
-      label: 'Limit to vocabulary'
     error_message:
       type: boolean
       label: 'Display error message'
diff --git a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
index 1ccee97..06ffcb9 100644
--- a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
+++ b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
@@ -70,7 +70,8 @@ public function getReferenceableEntities($match = NULL, $match_operator = 'CONTA
               $unpublished_terms[] = $term->id();
               continue;
             }
-            $options[$vocabulary->id()][$term->id()] = str_repeat('-', $term->depth) . Html::escape($this->entityRepository->getTranslationFromContext($term)->label());
+            $hierarchy = isset($this->getConfiguration()['hierarchy']) ? $this->getConfiguration()['hierarchy'] : TRUE;
+            $options[$vocabulary->id()][$term->id()] = ($hierarchy ? str_repeat('-', $term->depth) : '') . Html::escape($this->entityRepository->getTranslationFromContext($term)->label());
           }
         }
       }
diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
index 5829681..a7696e0 100644
--- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
@@ -2,15 +2,10 @@
 
 namespace Drupal\taxonomy\Plugin\views\filter;
 
-use Drupal\Core\Entity\Element\EntityAutocomplete;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\taxonomy\Entity\Term;
-use Drupal\taxonomy\TermStorageInterface;
-use Drupal\taxonomy\VocabularyStorageInterface;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
-use Drupal\views\Plugin\views\filter\ManyToOne;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\views\Plugin\views\filter\EntityReference;
 
 /**
  * Filter by term id.
@@ -19,57 +14,7 @@
  *
  * @ViewsFilter("taxonomy_index_tid")
  */
-class TaxonomyIndexTid extends ManyToOne {
-
-  // Stores the exposed input for this filter.
-  public $validated_exposed_input = NULL;
-
-  /**
-   * The vocabulary storage.
-   *
-   * @var \Drupal\taxonomy\VocabularyStorageInterface
-   */
-  protected $vocabularyStorage;
-
-  /**
-   * The term storage.
-   *
-   * @var \Drupal\taxonomy\TermStorageInterface
-   */
-  protected $termStorage;
-
-  /**
-   * Constructs a TaxonomyIndexTid object.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\taxonomy\VocabularyStorageInterface $vocabulary_storage
-   *   The vocabulary storage.
-   * @param \Drupal\taxonomy\TermStorageInterface $term_storage
-   *   The term storage.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, VocabularyStorageInterface $vocabulary_storage, TermStorageInterface $term_storage) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->vocabularyStorage = $vocabulary_storage;
-    $this->termStorage = $term_storage;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('entity_type.manager')->getStorage('taxonomy_vocabulary'),
-      $container->get('entity_type.manager')->getStorage('taxonomy_term')
-    );
-  }
+class TaxonomyIndexTid extends EntityReference {
 
   /**
    * {@inheritdoc}
@@ -77,28 +22,18 @@ public static function create(ContainerInterface $container, array $configuratio
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
     parent::init($view, $display, $options);
 
-    if (!empty($this->definition['vocabulary'])) {
-      $this->options['vid'] = $this->definition['vocabulary'];
-    }
-  }
-
-  public function hasExtraOptions() {
-    return TRUE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getValueOptions() {
-    return $this->valueOptions;
+    $this->definition['options callback'] = [$this, 'getValueOptionsCallback'];
+    $handler_settings = $this->options['handler_settings'] + [
+      'target_type' => $this->getReferencedEntityType()->id(),
+      'handler' => $this->options['handler'],
+      'hierarchy' => $this->options['hierarchy'],
+    ];
+    $this->definition['options arguments'] = [$this->selectionPluginManager->getInstance($handler_settings)];
   }
 
   protected function defineOptions() {
     $options = parent::defineOptions();
 
-    $options['type'] = ['default' => 'textfield'];
-    $options['limit'] = ['default' => TRUE];
-    $options['vid'] = ['default' => ''];
     $options['hierarchy'] = ['default' => FALSE];
     $options['error_message'] = ['default' => TRUE];
 
@@ -106,271 +41,19 @@ protected function defineOptions() {
   }
 
   public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {
-    $vocabularies = $this->vocabularyStorage->loadMultiple();
-    $options = [];
-    foreach ($vocabularies as $voc) {
-      $options[$voc->id()] = $voc->label();
-    }
-
-    if ($this->options['limit']) {
-      // We only do this when the form is displayed.
-      if (empty($this->options['vid'])) {
-        $first_vocabulary = reset($vocabularies);
-        $this->options['vid'] = $first_vocabulary->id();
-      }
-
-      if (empty($this->definition['vocabulary'])) {
-        $form['vid'] = [
-          '#type' => 'radios',
-          '#title' => $this->t('Vocabulary'),
-          '#options' => $options,
-          '#description' => $this->t('Select which vocabulary to show terms for in the regular options.'),
-          '#default_value' => $this->options['vid'],
-        ];
-      }
-    }
-
-    $form['type'] = [
-      '#type' => 'radios',
-      '#title' => $this->t('Selection type'),
-      '#options' => ['select' => $this->t('Dropdown'), 'textfield' => $this->t('Autocomplete')],
-      '#default_value' => $this->options['type'],
-    ];
-
+    parent::buildExtraOptionsForm($form, $form_state);
     $form['hierarchy'] = [
       '#type' => 'checkbox',
       '#title' => $this->t('Show hierarchy in dropdown'),
       '#default_value' => !empty($this->options['hierarchy']),
       '#states' => [
         'visible' => [
-          ':input[name="options[type]"]' => ['value' => 'select'],
+          ':input[name="options[widget]"]' => ['value' => self::WIDGET_SELECT],
         ],
       ],
     ];
   }
 
-  protected function valueForm(&$form, FormStateInterface $form_state) {
-    $vocabulary = $this->vocabularyStorage->load($this->options['vid']);
-    if (empty($vocabulary) && $this->options['limit']) {
-      $form['markup'] = [
-        '#markup' => '<div class="js-form-item form-item">' . $this->t('An invalid vocabulary is selected. Please change it in the options.') . '</div>',
-      ];
-      return;
-    }
-
-    if ($this->options['type'] == 'textfield') {
-      $terms = $this->value ? Term::loadMultiple(($this->value)) : [];
-      $form['value'] = [
-        '#title' => $this->options['limit'] ? $this->t('Select terms from vocabulary @voc', ['@voc' => $vocabulary->label()]) : $this->t('Select terms'),
-        '#type' => 'textfield',
-        '#default_value' => EntityAutocomplete::getEntityLabels($terms),
-      ];
-
-      if ($this->options['limit']) {
-        $form['value']['#type'] = 'entity_autocomplete';
-        $form['value']['#target_type'] = 'taxonomy_term';
-        $form['value']['#selection_settings']['target_bundles'] = [$vocabulary->id()];
-        $form['value']['#tags'] = TRUE;
-        $form['value']['#process_default_value'] = FALSE;
-      }
-    }
-    else {
-      if (!empty($this->options['hierarchy']) && $this->options['limit']) {
-        $tree = $this->termStorage->loadTree($vocabulary->id(), 0, NULL, TRUE);
-        $options = [];
-
-        if ($tree) {
-          foreach ($tree as $term) {
-            $choice = new \stdClass();
-            $choice->option = [$term->id() => str_repeat('-', $term->depth) . \Drupal::service('entity.repository')->getTranslationFromContext($term)->label()];
-            $options[] = $choice;
-          }
-        }
-      }
-      else {
-        $options = [];
-        $query = \Drupal::entityQuery('taxonomy_term')
-          // @todo Sorting on vocabulary properties -
-          //   https://www.drupal.org/node/1821274.
-          ->sort('weight')
-          ->sort('name')
-          ->addTag('taxonomy_term_access');
-        if ($this->options['limit']) {
-          $query->condition('vid', $vocabulary->id());
-        }
-        $terms = Term::loadMultiple($query->execute());
-        foreach ($terms as $term) {
-          $options[$term->id()] = \Drupal::service('entity.repository')->getTranslationFromContext($term)->label();
-        }
-      }
-
-      $default_value = (array) $this->value;
-
-      if ($exposed = $form_state->get('exposed')) {
-        $identifier = $this->options['expose']['identifier'];
-
-        if (!empty($this->options['expose']['reduce'])) {
-          $options = $this->reduceValueOptions($options);
-
-          if (!empty($this->options['expose']['multiple']) && empty($this->options['expose']['required'])) {
-            $default_value = [];
-          }
-        }
-
-        if (empty($this->options['expose']['multiple'])) {
-          if (empty($this->options['expose']['required']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
-            $default_value = 'All';
-          }
-          elseif (empty($default_value)) {
-            $keys = array_keys($options);
-            $default_value = array_shift($keys);
-          }
-          // Due to #1464174 there is a chance that array('') was saved in the admin ui.
-          // Let's choose a safe default value.
-          elseif ($default_value == ['']) {
-            $default_value = 'All';
-          }
-          else {
-            $copy = $default_value;
-            $default_value = array_shift($copy);
-          }
-        }
-      }
-      $form['value'] = [
-        '#type' => 'select',
-        '#title' => $this->options['limit'] ? $this->t('Select terms from vocabulary @voc', ['@voc' => $vocabulary->label()]) : $this->t('Select terms'),
-        '#multiple' => TRUE,
-        '#options' => $options,
-        '#size' => min(9, count($options)),
-        '#default_value' => $default_value,
-      ];
-
-      $user_input = $form_state->getUserInput();
-      if ($exposed && isset($identifier) && !isset($user_input[$identifier])) {
-        $user_input[$identifier] = $default_value;
-        $form_state->setUserInput($user_input);
-      }
-    }
-
-    if (!$form_state->get('exposed')) {
-      // Retain the helper option
-      $this->helper->buildOptionsForm($form, $form_state);
-
-      // Show help text if not exposed to end users.
-      $form['value']['#description'] = t('Leave blank for all. Otherwise, the first selected term will be the default instead of "Any".');
-    }
-  }
-
-  protected function valueValidate($form, FormStateInterface $form_state) {
-    // We only validate if they've chosen the text field style.
-    if ($this->options['type'] != 'textfield') {
-      return;
-    }
-
-    $tids = [];
-    if ($values = $form_state->getValue(['options', 'value'])) {
-      foreach ($values as $value) {
-        $tids[] = $value['target_id'];
-      }
-    }
-    $form_state->setValue(['options', 'value'], $tids);
-  }
-
-  public function acceptExposedInput($input) {
-    if (empty($this->options['exposed'])) {
-      return TRUE;
-    }
-    // We need to know the operator, which is normally set in
-    // \Drupal\views\Plugin\views\filter\FilterPluginBase::acceptExposedInput(),
-    // before we actually call the parent version of ourselves.
-    if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id']) && isset($input[$this->options['expose']['operator_id']])) {
-      $this->operator = $input[$this->options['expose']['operator_id']];
-    }
-
-    // If view is an attachment and is inheriting exposed filters, then assume
-    // exposed input has already been validated
-    if (!empty($this->view->is_attachment) && $this->view->display_handler->usesExposed()) {
-      $this->validated_exposed_input = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']];
-    }
-
-    // If we're checking for EMPTY or NOT, we don't need any input, and we can
-    // say that our input conditions are met by just having the right operator.
-    if ($this->operator == 'empty' || $this->operator == 'not empty') {
-      return TRUE;
-    }
-
-    // If it's non-required and there's no value don't bother filtering.
-    if (!$this->options['expose']['required'] && empty($this->validated_exposed_input)) {
-      return FALSE;
-    }
-
-    $rc = parent::acceptExposedInput($input);
-    if ($rc) {
-      // If we have previously validated input, override.
-      if (isset($this->validated_exposed_input)) {
-        $this->value = $this->validated_exposed_input;
-      }
-    }
-
-    return $rc;
-  }
-
-  public function validateExposed(&$form, FormStateInterface $form_state) {
-    if (empty($this->options['exposed'])) {
-      return;
-    }
-
-    $identifier = $this->options['expose']['identifier'];
-
-    // We only validate if they've chosen the text field style.
-    if ($this->options['type'] != 'textfield') {
-      if ($form_state->getValue($identifier) != 'All') {
-        $this->validated_exposed_input = (array) $form_state->getValue($identifier);
-      }
-      return;
-    }
-
-    if (empty($this->options['expose']['identifier'])) {
-      return;
-    }
-
-    if ($values = $form_state->getValue($identifier)) {
-      foreach ($values as $value) {
-        $this->validated_exposed_input[] = $value['target_id'];
-      }
-    }
-  }
-
-  protected function valueSubmit($form, FormStateInterface $form_state) {
-    // prevent array_filter from messing up our arrays in parent submit.
-  }
-
-  public function buildExposeForm(&$form, FormStateInterface $form_state) {
-    parent::buildExposeForm($form, $form_state);
-    if ($this->options['type'] != 'select') {
-      unset($form['expose']['reduce']);
-    }
-    $form['error_message'] = [
-      '#type' => 'checkbox',
-      '#title' => $this->t('Display error message'),
-      '#default_value' => !empty($this->options['error_message']),
-    ];
-  }
-
-  public function adminSummary() {
-    // set up $this->valueOptions for the parent summary
-    $this->valueOptions = [];
-
-    if ($this->value) {
-      $this->value = array_filter($this->value);
-      $terms = Term::loadMultiple($this->value);
-      foreach ($terms as $term) {
-        $this->valueOptions[$term->id()] = \Drupal::service('entity.repository')->getTranslationFromContext($term)->label();
-      }
-    }
-    return parent::adminSummary();
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -385,16 +68,29 @@ public function getCacheContexts() {
   }
 
   /**
+   * Gets the target referenced entity type by this field.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface
+   *   Entity type.
+   */
+  protected function getReferencedEntityType() {
+    return $this->entityTypeManager->getDefinition('taxonomy_term');
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function calculateDependencies() {
     $dependencies = parent::calculateDependencies();
 
-    $vocabulary = $this->vocabularyStorage->load($this->options['vid']);
-    $dependencies[$vocabulary->getConfigDependencyKey()][] = $vocabulary->getConfigDependencyName();
-
-    foreach ($this->termStorage->loadMultiple($this->options['value']) as $term) {
-      $dependencies[$term->getConfigDependencyKey()][] = $term->getConfigDependencyName();
+    $handler_settings = $this->options['handler_settings'];
+    if (isset($handler_settings['target_bundles'])) {
+      $bundle_entity_type_id = $this->getReferencedEntityType()->getBundleEntityType();
+      $target_bundles = $this->entityTypeManager->getStorage($bundle_entity_type_id)->loadMultiple($handler_settings['target_bundles']);
+      /** @var \Drupal\Core\Entity\EntityInterface $bundle */
+      foreach ($target_bundles as $bundle) {
+        $dependencies[$bundle->getConfigDependencyKey()][] = $bundle->getConfigDependencyName();
+      }
     }
 
     return $dependencies;
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml
index ab5e1d8..acd265b 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml
@@ -164,9 +164,17 @@ display:
             default_group_multiple: {  }
             group_items: {  }
           reduce_duplicates: false
-          type: select
-          limit: true
-          vid: tags
+          handler: 'default:taxonomy_term'
+          handler_settings:
+            target_bundles:
+              tags: tags
+            sort:
+              field: name
+              direction: asc
+            auto_create: false
+            auto_create_bundle: ''
+          widget: select
+          list_max: 100
           hierarchy: true
           error_message: true
           plugin_id: taxonomy_index_tid
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid__non_existing_dependency.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid__non_existing_dependency.yml
index a5c5828..857291a 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid__non_existing_dependency.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid__non_existing_dependency.yml
@@ -165,9 +165,17 @@ display:
             default_group_multiple: {  }
             group_items: {  }
           reduce_duplicates: false
-          type: select
-          limit: true
-          vid: tags
+          handler: 'default:taxonomy_term'
+          handler_settings:
+            target_bundles:
+              tags: tags
+            sort:
+              field: name
+              direction: asc
+            auto_create: false
+            auto_create_bundle: ''
+          widget: select
+          list_max: 100
           hierarchy: true
           error_message: true
           plugin_id: taxonomy_index_tid
@@ -177,6 +185,17 @@ display:
       empty: {  }
       relationships: {  }
       arguments: {  }
+      display_extenders: {  }
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url.query_args
+        - user
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }
   page_1:
     display_plugin: page
     id: page_1
@@ -185,3 +204,13 @@ display:
     display_options:
       display_extenders: {  }
       path: test-filter-taxonomy-index-tid
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url.query_args
+        - user
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_depth.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_depth.yml
index 856543a..67d441a 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_depth.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_depth.yml
@@ -163,9 +163,17 @@ display:
             default_group_multiple: {  }
             group_items: {  }
           reduce_duplicates: false
-          type: select
-          limit: true
-          vid: views_testing_tags
+          handler: 'default:taxonomy_term'
+          handler_settings:
+            target_bundles:
+              views_testing_tags: views_testing_tags
+            sort:
+              field: name
+              direction: asc
+            auto_create: false
+            auto_create_bundle: ''
+          widget: select
+          list_max: 100
           hierarchy: true
           depth: -2
           error_message: true
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
index a269c8f..b7ad97f 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
@@ -104,7 +104,7 @@ public function testFilterUI() {
     // type.
     $view = View::load('test_filter_taxonomy_index_tid');
     $display =& $view->getDisplay('default');
-    $display['display_options']['filters']['tid']['type'] = 'textfield';
+    $display['display_options']['filters']['tid']['widget'] = 'autocomplete';
     $view->save();
     $this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
     $this->assertFieldById('edit-options-value', NULL);
diff --git a/core/modules/views/config/schema/views.filter.schema.yml b/core/modules/views/config/schema/views.filter.schema.yml
index 1eb0900..9939916 100644
--- a/core/modules/views/config/schema/views.filter.schema.yml
+++ b/core/modules/views/config/schema/views.filter.schema.yml
@@ -128,6 +128,29 @@ views.filter.many_to_one:
       type: boolean
       label: 'Reduce duplicate'
 
+views.filter.entity_reference:
+  type: views.filter.many_to_one
+  label: 'Entity reference'
+  mapping:
+    handler:
+      type: string
+      label: 'Selection handler'
+      nullable: false
+      translatable: false
+    widget:
+      type: string
+      label: 'Selection type'
+      nullable: false
+      translatable: false
+    list_max:
+      type: string
+      label: 'Maximum entities in select list'
+      nullable: false
+      translatable: false
+    handler_settings:
+      type: entity_reference_selection.default
+      label: 'Selection handler settings'
+
 views.filter.standard:
   type: views_filter
   label: 'Standard'
diff --git a/core/modules/views/src/Plugin/views/filter/EntityReference.php b/core/modules/views/src/Plugin/views/filter/EntityReference.php
new file mode 100644
index 0000000..d598cf5
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/filter/EntityReference.php
@@ -0,0 +1,644 @@
+<?php
+
+namespace Drupal\views\Plugin\views\filter;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\Element\EntityAutocomplete;
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase;
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
+use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Drupal\Core\Render\Element;
+use Drupal\views\FieldAPIHandlerTrait;
+use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\ViewExecutable;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Filter handler which allows to search based on referenced entities.
+ *
+ * @ingroup views_filter_handlers
+ *
+ * @ViewsFilter("entity_reference")
+ */
+class EntityReference extends ManyToOne {
+
+  use FieldAPIHandlerTrait;
+
+  /**
+   * Type for the auto complete filter format.
+   */
+  const WIDGET_AUTOCOMPLETE = 'autocomplete';
+
+  /**
+   * Type for the select list filter format.
+   */
+  const WIDGET_SELECT = 'select';
+
+  /**
+   * Validated exposed input that will be set as value in case.
+   *
+   * @var array
+   */
+  protected $validatedExposedInput;
+
+  /**
+   * The selection plugin manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface
+   */
+  protected $selectionPluginManager;
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * @var \Psr\Log\LoggerInterface
+   */
+  protected $logger;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, SelectionPluginManagerInterface $selection_plugin_manager, EntityTypeManagerInterface $entity_type_manager, LoggerChannelFactoryInterface $loggerChannelFactory) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->selectionPluginManager = $selection_plugin_manager;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->logger = $loggerChannelFactory->get('views');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('plugin.manager.entity_reference_selection'),
+      $container->get('entity_type.manager'),
+      $container->get('logger.factory')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
+    parent::init($view, $display, $options);
+
+    if (empty($this->definition['field_name'])) {
+      $this->definition['field_name'] = $options['field'];
+    }
+
+    $this->definition['options callback'] = [$this, 'getValueOptionsCallback'];
+    $handler_settings = $this->options['handler_settings'] + [
+      'target_type' => $this->getReferencedEntityType()->id(),
+      'handler' => $this->options['handler'],
+    ];
+    $this->definition['options arguments'] = [$this->selectionPluginManager->getInstance($handler_settings)];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function defineOptions() {
+    $options = parent::defineOptions();
+
+    $options['handler'] = ['default' => 'default:' . $this->getReferencedEntityType()->id()];
+    $options['handler_settings'] = ['default' => []];
+    $options['widget'] = ['default' => self::WIDGET_AUTOCOMPLETE];
+
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasExtraOptions() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {
+    $entity_type = $this->getReferencedEntityType();
+
+    // Get all selection plugins for this entity type.
+    $selection_plugins = $this->selectionPluginManager->getSelectionGroups($entity_type->id());
+    $handlers_options = [];
+    foreach (array_keys($selection_plugins) as $selection_group_id) {
+      // We only display base plugins (e.g. 'default', 'views', ...) and not
+      // entity type specific plugins (e.g. 'default:node', 'default:user',
+      // ...).
+      if (array_key_exists($selection_group_id, $selection_plugins[$selection_group_id])) {
+        $handlers_options[$selection_group_id] = Html::escape($selection_plugins[$selection_group_id][$selection_group_id]['label']);
+      }
+      elseif (array_key_exists($selection_group_id . ':' . $entity_type->id(), $selection_plugins[$selection_group_id])) {
+        $selection_group_plugin = $selection_group_id . ':' . $entity_type->id();
+        $handlers_options[$selection_group_plugin] = Html::escape($selection_plugins[$selection_group_id][$selection_group_plugin]['base_plugin_label']);
+      }
+    }
+
+    // @todo When changing selection handler Ajax request doesn't get processed
+    // correctly so need to add this hack.
+    $input = $form_state->getUserInput();
+    if (isset($input['options']['handler']) && array_key_exists($input['options']['handler'], $handlers_options)) {
+      $this->options['handler'] = $input['options']['handler'];
+    }
+    if (isset($input['options']['handler_settings'])) {
+      $this->options['handler_settings'] = $input['options']['handler_settings'];
+    }
+
+    $form['#process'] = [[get_class($this), 'extraOptionsAjaxProcess']];
+
+    // @todo: We would actually prefer organizing the form elements according
+    // to the required structure of the value tree, and to rearrange the visual
+    // grouping using the #group key, in order to avoid messing with #parents.
+    // Currently, this however isn't possible. Revisit once Core issue
+    // https://www.drupal.org/project/drupal/issues/2854166 landed.
+    $form['reference'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Reference type'),
+      '#open' => TRUE,
+      '#parents' => ['options'],
+    ];
+
+    $form['reference']['handler'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Reference method'),
+      '#options' => $handlers_options,
+      '#default_value' => $this->options['handler'],
+      '#required' => TRUE,
+      '#ajax' => TRUE,
+      '#limit_validation_errors' => [['options', 'handler']],
+    ];
+
+    $form['reference']['handler_submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Change handler'),
+      '#limit_validation_errors' => [],
+      '#attributes' => [
+        'class' => ['js-hide'],
+      ],
+      '#submit' => [[get_class($this), 'settingsAjaxSubmit']],
+    ];
+
+    $form['reference']['handler_settings'] = [
+      '#type' => 'container',
+      '#attributes' => ['class' => ['entity_reference-settings']],
+      '#process' => [[get_class($this), 'fixSubmitParents']],
+    ];
+
+    $options = $this->options['handler_settings'];
+    $options += [
+      'target_type' => $entity_type->id(),
+      'handler' => $this->options['handler'],
+    ];
+
+    /** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase $selection_handler */
+    $selection_handler = $this->selectionPluginManager->getInstance($options);
+    $subform_state = SubformState::createForSubform($form['reference'], $form, $form_state);
+    $form['reference']['handler_settings'] += $selection_handler->buildConfigurationForm([], $subform_state);
+    // There is no need in polluting filter config form.
+    $form['reference']['handler_settings']['target_bundles']['#ajax'] = FALSE;
+    $form['reference']['handler_settings']['target_bundles_update']['#access'] = FALSE;
+    $form['reference']['handler_settings']['auto_create']['#access'] = FALSE;
+    $form['reference']['handler_settings']['auto_create_bundle']['#access'] = FALSE;
+
+    $form['widget'] = [
+      '#type' => 'radios',
+      '#title' => $this->t('Selection type'),
+      '#default_value' => $this->options['widget'],
+      '#options' => [
+        self::WIDGET_SELECT => $this->t('Select list'),
+        self::WIDGET_AUTOCOMPLETE => $this->t('Autocomplete'),
+      ],
+    ];
+
+    $form['list_max'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Maximum entities in select list'),
+      '#default_value' => $this->options['list_max'] ?? 30,
+      '#min' => 1,
+      '#states' => [
+        'visible' => [
+          ':input[name="options[widget]"]' => ['value' => static::WIDGET_SELECT],
+        ],
+      ],
+    ];
+  }
+
+  /**
+   * Render API callback.
+   *
+   * @see static::buildExtraOptionsForm()
+   */
+  public static function fixSubmitParents($form, FormStateInterface $form_state) {
+    static::fixSubmitParentsElement($form, 'root');
+    return $form;
+  }
+
+  /**
+   * Process element callback.
+   *
+   * @see static::fixSubmitParents()
+   */
+  public static function fixSubmitParentsElement(&$element, $key) {
+    if (isset($element['#type']) && in_array($element['#type'], ['button', 'submit']) && $key !== 'root') {
+      $element['#parents'] = [$key];
+    }
+
+    foreach (Element::children($element) as $key) {
+      static::fixSubmitParentsElement($element[$key], $key);
+    }
+  }
+
+  /**
+   * Render API callback.
+   *
+   * Processes the extra options form and allows access to the form state.
+   *
+   * @see static::buildExtraOptionsForm()
+   * @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::fieldSettingsAjaxProcess()
+   */
+  public static function extraOptionsAjaxProcess($form, FormStateInterface $form_state) {
+    static::extraOptionsAjaxProcessElement($form, $form);
+    return $form;
+  }
+
+  /**
+   * Process element callback.
+   *
+   * Adds entity_reference specific properties to AJAX form elements from the
+   * extra options form.
+   *
+   * @see static::extraOptionsAjaxProcess()
+   * @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::fieldSettingsAjaxProcessElement()
+   */
+  public static function extraOptionsAjaxProcessElement(&$element, $main_form) {
+    if (!empty($element['#ajax'])) {
+      $element['#ajax'] = [
+        'callback' => [get_called_class(), 'settingsAjax'],
+        'wrapper' => $main_form['#id'],
+        'element' => $main_form['#array_parents'],
+      ];
+    }
+
+    foreach (Element::children($element) as $key) {
+      static::extraOptionsAjaxProcessElement($element[$key], $main_form);
+    }
+  }
+
+  /**
+   * Ajax callback for the handler settings form.
+   *
+   * @see static::extraOptionsAjaxProcessElement()
+   * @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::settingsAjax()
+   */
+  public static function settingsAjax($form, FormStateInterface $form_state) {
+    return NestedArray::getValue($form, $form_state->getTriggeringElement()['#ajax']['element']);
+  }
+
+  /**
+   * Submit handler for the non-JS case.
+   *
+   * @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::settingsAjaxSubmit()
+   */
+  public static function settingsAjaxSubmit($form, FormStateInterface $form_state) {
+    $form_state->setRebuild();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateExtraOptionsForm($form, FormStateInterface $form_state) {
+    $subform_state = SubformState::createForSubform($form['reference'], $form, $form_state);
+
+    // Copy handler_settings from options to settings to be compatible with
+    // selection plugins.
+    $subform_state->setValue(['settings', 'handler_settings'], $form_state->getValue(['options', 'handler_settings']));
+
+    $options = $this->options['handler_settings'];
+    $options += [
+      'target_type' => $this->getReferencedEntityType()->id(),
+      'handler' => $this->options['handler'],
+    ];
+
+    /** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase $selection_handler */
+    $selection_handler = $this->selectionPluginManager->getInstance($options);
+    $selection_handler->validateConfigurationForm($form, $subform_state);
+
+    // Copy handler_settings back into options.
+    // Necessary because DefaultSelection::validateConfigurationForm()
+    // manipulates the form state values.
+    $form_state->setValue(['options', 'handler_settings'], $subform_state->getValue(['settings', 'handler_settings']));
+    $form_state->setValue(['settings', 'handler_settings'], NULL);
+
+    parent::validateExtraOptionsForm($form, $form_state);
+
+    // This is required for proper validation rendering on AJAX.
+    if ($form_state->getErrors()) {
+      $form_state->set('rerender', TRUE);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function valueForm(&$form, FormStateInterface $form_state) {
+    $referenced_type = $this->getReferencedEntityType();
+    $is_exposed = $form_state->get('exposed');
+
+    if ($this->options['widget'] == self::WIDGET_AUTOCOMPLETE) {
+      $form['value'] = [
+        '#title' => $this->t('Select %entity_types', ['%entity_types' => $referenced_type->getPluralLabel()]),
+        '#type' => 'entity_autocomplete',
+        '#default_value' => EntityAutocomplete::getEntityLabels($this->getDefaultSelectedEntities()),
+        '#tags' => TRUE,
+        '#process_default_value' => TRUE,
+        '#target_type' => $this->getReferencedEntityType()->id(),
+        '#selection_handler' => $this->options['handler'],
+        '#selection_settings' => $this->options['handler_settings'],
+        '#validate_reference' => FALSE,
+        '#process_default_value' => FALSE,
+      ];
+    }
+    else {
+      $options = $this->getValueOptions();
+      $default_value = (array) $this->value;
+
+      if ($is_exposed) {
+        $identifier = $this->options['expose']['identifier'];
+
+        if (!empty($this->options['expose']['reduce'])) {
+          $options = $this->reduceValueOptions($options);
+
+          if (!empty($this->options['expose']['multiple']) && empty($this->options['expose']['required'])) {
+            $default_value = [];
+          }
+        }
+
+        if (empty($this->options['expose']['multiple'])) {
+          if (empty($this->options['expose']['required']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
+            $default_value = 'All';
+          }
+          elseif (empty($default_value)) {
+            $keys = array_keys($options);
+            $default_value = array_shift($keys);
+          }
+          // Due to https://www.drupal.org/node/1464174 there is a chance that
+          // [''] was saved in the admin ui. Let's choose a safe default value.
+          elseif ($default_value == ['']) {
+            $default_value = 'All';
+          }
+          else {
+            $copy = $default_value;
+            $default_value = array_shift($copy);
+          }
+        }
+      }
+
+      $form['value'] = [
+        '#type' => 'select',
+        '#title' => $this->t('Select @entity_types', ['@entity_types' => $referenced_type->getPluralLabel()]),
+        '#multiple' => TRUE,
+        '#options' => $options,
+        '#size' => min(9, count($options)),
+        '#default_value' => $default_value,
+      ];
+
+      $user_input = $form_state->getUserInput();
+      if ($is_exposed && isset($identifier) && !isset($user_input[$identifier])) {
+        $user_input[$identifier] = $default_value;
+        $form_state->setUserInput($user_input);
+      }
+    }
+
+    // Show or hide the value field depending on the operator field.
+    $visible = [];
+    if ($is_exposed) {
+      $operator_field = ($this->options['expose']['use_operator'] && $this->options['expose']['operator_id']) ? $this->options['expose']['operator_id'] : NULL;
+    }
+    else {
+      $operator_field = 'options[operator]';
+      $visible[] = [
+          ':input[name="options[expose_button][checkbox][checkbox]"]' => ['checked' => TRUE],
+          ':input[name="options[expose][use_operator]"]' => ['checked' => TRUE],
+          ':input[name="options[expose][operator_id]"]' => ['empty' => FALSE],
+      ];
+    }
+    if ($operator_field) {
+      foreach ($this->operatorValues(1) as $operator) {
+        $visible[] = [
+          ':input[name="' . $operator_field . '"]' => ['value' => $operator],
+        ];
+      }
+      $form['value']['#states'] = ['visible' => $visible];
+    }
+
+    if (!$is_exposed) {
+      // Retain the helper option.
+      $this->helper->buildOptionsForm($form, $form_state);
+
+      // Show help text if not exposed to end users.
+      $form['value']['#description'] = $this->t('Leave blank for all. Otherwise, the first selected item will be the default instead of "Any".');
+    }
+  }
+
+  /**
+   * Gets all entities selected by default.
+   *
+   * @return string
+   *   The auto-complete value.
+   */
+  protected function getDefaultSelectedEntities() {
+    $referenced_type_id = $this->getReferencedEntityType()->id();
+    /** @var \Drupal\Core\Entity\EntityStorageInterface $entity_storage */
+    $entity_storage = $this->entityTypeManager->getStorage($referenced_type_id);
+
+    return $this->value && !isset($this->value['all']) ? $entity_storage->loadMultiple($this->value) : [];
+  }
+
+  /**
+   * Value options callback.
+   *
+   * @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase $selection_handler
+   *   The selection handler.
+   *
+   * @return array
+   *   The options.
+   *
+   * @see \Drupal\views\Plugin\views\filter\InOperator::getValueOptions()
+   */
+  protected function getValueOptionsCallback(SelectionPluginBase $selection_handler) {
+    $entities = $this->getReferencableEntities($selection_handler);
+    $options = [];
+    foreach ($entities as $bundle) {
+      foreach ($bundle as $id => $entity_label) {
+        $options[$id] = $entity_label;
+      }
+    }
+    return $options;
+  }
+
+  protected function getReferencableEntities(SelectionInterface $handler) {
+    if (isset($this->options['list_max'])) {
+      // We attempt to query max + 1 entities so that if we get that one more additional
+      // then we can alert the user that the view filter is not 100% functional.
+      $entities = $handler->getReferenceableEntities(NULL, NULL, $this->options['list_max'] + 1);
+      $count = 0;
+
+      foreach ($entities as $bundles) {
+        $count += count($bundles);
+      }
+
+      if ($count > $this->options['list_max']) {
+        array_pop($entities[array_reverse(array_keys($entities))[0]]);
+        $this->logger->warning($this->t('Entity count for reference filter "%name" on view "%view" and display "%display" is overflowing. We recommend changing the display mode to autocomplete widget.'), [
+          '%name' => $this->definition['title'],
+          '%view' => $this->view->getTitle(),
+          '%display' => $this->view->getDisplay()->definition['title'],
+        ]);
+      }
+    } else {
+      $entities = $handler->getReferenceableEntities();
+    }
+
+    return $entities;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function valueValidate($form, FormStateInterface $form_state) {
+    // We only validate if they've chosen the text field style.
+    if ($this->options['widget'] != self::WIDGET_AUTOCOMPLETE) {
+      return;
+    }
+
+    $ids = [];
+    if ($values = $form_state->getValue(['options', 'value'])) {
+      foreach ($values as $value) {
+        $ids[] = $value['target_id'];
+      }
+    }
+
+    $form_state->setValue(['options', 'value'], $ids);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function acceptExposedInput($input) {
+    if (empty($this->options['exposed'])) {
+      return TRUE;
+    }
+    // We need to know the operator, which is normally set in
+    // \Drupal\views\Plugin\views\filter\FilterPluginBase::acceptExposedInput(),
+    // before we actually call the parent version of ourselves.
+    if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id']) && isset($input[$this->options['expose']['operator_id']])) {
+      $this->operator = $input[$this->options['expose']['operator_id']];
+    }
+
+    // If view is an attachment and is inheriting exposed filters, then assume
+    // exposed input has already been validated.
+    if (!empty($this->view->is_attachment) && $this->view->display_handler->usesExposed()) {
+      $this->validatedExposedInput = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']];
+    }
+
+    // If we're checking for EMPTY or NOT, we don't need any input, and we can
+    // say that our input conditions are met by just having the right operator.
+    if ($this->operator == 'empty' || $this->operator == 'not empty') {
+      return TRUE;
+    }
+
+    // If it's non-required and there's no value don't bother filtering.
+    if (!$this->options['expose']['required'] && empty($this->validatedExposedInput)) {
+      return FALSE;
+    }
+
+    $rc = parent::acceptExposedInput($input);
+    if ($rc) {
+      // If we have previously validated input, override.
+      if (isset($this->validatedExposedInput)) {
+        $this->value = $this->validatedExposedInput;
+      }
+    }
+
+    return $rc;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateExposed(&$form, FormStateInterface $form_state) {
+    if (empty($this->options['exposed'])) {
+      return;
+    }
+
+    $identifier = $this->options['expose']['identifier'];
+
+    // We only validate if they've chosen the select field style.
+    if ($this->options['widget'] != self::WIDGET_AUTOCOMPLETE) {
+
+      if ($form_state->getValue($identifier) != 'All') {
+        $this->validatedExposedInput = (array) $form_state->getValue($identifier);
+      }
+      return;
+    }
+
+    if (empty($identifier)) {
+      return;
+    }
+
+    if ($values = $form_state->getValue($identifier)) {
+      foreach ($values as $value) {
+        $this->validatedExposedInput[] = $value['target_id'];
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function valueSubmit($form, FormStateInterface $form_state) {
+    // Prevent array_filter from messing up our arrays in parent submit.
+  }
+
+  /**
+   * Gets the target entity type ID referenced by this field.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface
+   *   Entity type.
+   */
+  protected function getReferencedEntityType() {
+    $field_def = $this->getFieldDefinition();
+    $entity_type_id = $field_def->getItemDefinition()->getSetting('target_type');
+    return $this->entityTypeManager->getDefinition($entity_type_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    $dependencies = parent::calculateDependencies();
+
+    /** @var \Drupal\Core\Entity\EntityInterface $entity */
+    foreach ($this->getDefaultSelectedEntities() as $entity) {
+      $dependencies[$entity->getConfigDependencyKey()][] = $entity->getConfigDependencyName();
+    }
+
+    return $dependencies;
+  }
+
+}
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_checkboxes.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_checkboxes.yml
index 26b1d32..b9fb66a 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_checkboxes.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_form_checkboxes.yml
@@ -106,9 +106,16 @@ display:
             default_group_multiple: {  }
             group_items: {  }
           reduce_duplicates: false
-          type: select
-          limit: true
-          vid: test_exposed_checkboxes
+          handler: 'default:taxonomy_term'
+          handler_settings:
+            target_bundles:
+              test_exposed_checkboxes: test_exposed_checkboxes
+            sort:
+              field: name
+              direction: asc
+            auto_create: false
+            auto_create_bundle: ''
+          widget: select
           hierarchy: false
           error_message: true
           plugin_id: taxonomy_index_tid
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_entity_reference.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_entity_reference.yml
new file mode 100644
index 0000000..249d6fd
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_entity_reference.yml
@@ -0,0 +1,222 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - node.type.page
+  module:
+    - node
+    - user
+id: test_filter_entity_reference
+label: test_filter_entity_reference
+module: views
+description: ''
+tag: ''
+base_table: node_field_data
+base_field: nid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: perm
+        options:
+          perm: 'access content'
+      cache:
+        type: none
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: true
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: mini
+        options:
+          items_per_page: 10
+          offset: 0
+          id: 0
+          total_pages: null
+          expose:
+            items_per_page: false
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 25, 50'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: â€¹â€¹
+            next: â€ºâ€º
+      style:
+        type: default
+        options:
+          grouping: {  }
+          row_class: ''
+          default_row_class: true
+          uses_fields: false
+      row:
+        type: fields
+        options:
+          inline: {  }
+          separator: ''
+          hide_empty: false
+          default_field_elements: true
+      fields:
+        title:
+          id: title
+          table: node_field_data
+          field: title
+          entity_type: node
+          entity_field: title
+          label: ''
+          alter:
+            alter_text: false
+            make_link: false
+            absolute: false
+            trim: false
+            word_boundary: false
+            ellipsis: false
+            strip_tags: false
+            html: false
+          hide_empty: false
+          empty_zero: false
+          settings:
+            link_to_entity: true
+          plugin_id: field
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exclude: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_alter_empty: true
+          click_sort_column: value
+          type: string
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+      filters:
+        status:
+          value: '1'
+          table: node_field_data
+          field: status
+          plugin_id: boolean
+          entity_type: node
+          entity_field: status
+          id: status
+          expose:
+            operator: ''
+          group: 1
+        type:
+          id: type
+          table: node_field_data
+          field: type
+          value:
+            page: page
+          entity_type: node
+          entity_field: type
+          plugin_id: bundle
+        field_test_target_id:
+          id: field_test_target_id
+          table: node__field_test
+          field: field_test_target_id
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: or
+          value: {  }
+          group: 1
+          exposed: true
+          expose:
+            operator_id: field_test_target_id_op
+            label: 'Test (field_test)'
+            description: ''
+            use_operator: false
+            operator: field_test_target_id_op
+            identifier: field_test_target_id
+            required: false
+            remember: false
+            multiple: true
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator: '0'
+            reduce: false
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          reduce_duplicates: false
+          handler: 'default:node'
+          handler_settings:
+            target_bundles:
+              article: article
+            sort:
+              field: title
+              direction: ASC
+            auto_create: false
+            auto_create_bundle: ''
+          widget: select
+          list_max: 100
+          plugin_id: entity_reference
+      sorts:
+        created:
+          id: created
+          table: node_field_data
+          field: created
+          order: DESC
+          entity_type: node
+          entity_field: created
+          plugin_id: date
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exposed: false
+          expose:
+            label: ''
+          granularity: second
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      arguments: {  }
+      display_extenders: {  }
diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc
index 39a8f72..3545714 100644
--- a/core/modules/views/views.views.inc
+++ b/core/modules/views/views.views.inc
@@ -808,6 +808,13 @@ function core_field_views_data(FieldStorageConfigInterface $field_storage) {
       'relationship field' => $field_name . '_target_id',
     ];
 
+    // Render filters as select lists.
+    foreach ($table_data as $table_field_name => $table_field_data) {
+      if (isset($table_field_data['filter']) && $table_field_name != 'delta') {
+        $data[$table_name][$table_field_name]['filter']['id'] = 'entity_reference';
+      }
+    }
+
     // Provide a reverse relationship for the entity type that is referenced by
     // the field.
     $args['@entity'] = $entity_type->getLabel();
diff --git a/core/modules/views_ui/tests/src/Functional/FilterEntityReferenceWebTest.php b/core/modules/views_ui/tests/src/Functional/FilterEntityReferenceWebTest.php
new file mode 100644
index 0000000..3200e8c
--- /dev/null
+++ b/core/modules/views_ui/tests/src/Functional/FilterEntityReferenceWebTest.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace Drupal\Tests\views_ui\Functional;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\node\NodeInterface;
+use Drupal\views\Entity\View;
+
+/**
+ * Test the entity reference filter UI.
+ *
+ * @group views_ui
+ * @see \Drupal\views\Plugin\views\filter\EntityReference
+ */
+class FilterEntityReferenceWebTest extends UITestBase {
+
+  /**
+   * Entity type and referenceable type.
+   *
+   * @var \Drupal\node\NodeTypeInterface
+   */
+  protected $entityType;
+
+  /**
+   * Referenceable entity type.
+   *
+   * @var \Drupal\node\NodeTypeInterface
+   */
+  protected $referenceableType;
+
+  /**
+   * Referenceable content.
+   *
+   * @var \Drupal\node\NodeInterface[]
+   */
+  protected $nodes;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $testViews = ['test_filter_entity_reference'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp($import_test_views = TRUE) {
+    parent::setUp($import_test_views);
+
+    // Create an entity type, and a referenceable type. Since these are coded
+    // into the test view, they are not randomly named.
+    $this->entityType = $this->drupalCreateContentType(['type' => 'page']);
+    $this->referenceableType = $this->drupalCreateContentType(['type' => 'article']);
+
+    $field_storage = FieldStorageConfig::create([
+      'entity_type' => 'node',
+      'field_name' => 'field_test',
+      'type' => 'entity_reference',
+      'settings' => [
+        'target_type' => 'node',
+      ],
+      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+    ]);
+    $field_storage->save();
+
+    $field = FieldConfig::create([
+      'entity_type' => 'node',
+      'field_name' => 'field_test',
+      'bundle' => $this->entityType->id(),
+      'settings' => [
+        'handler' => 'default',
+        'handler_settings' => [
+          // Note, this has no impact on Views at this time.
+          'target_bundles' => [
+            $this->referenceableType->id() => $this->referenceableType->label(),
+          ],
+        ],
+      ],
+    ]);
+    $field->save();
+
+    // Create 10 referenceable nodes.
+    for ($i = 0; $i < 10; $i++) {
+      $node = $this->drupalCreateNode(['type' => $this->referenceableType->id()]);
+      $this->nodes[$node->id()] = $node;
+    }
+  }
+
+  /**
+   * Tests the filter UI.
+   */
+  public function testFilterUi() {
+    $this->drupalGet('admin/structure/views/nojs/handler/test_filter_entity_reference/default/filter/field_test_target_id');
+
+    $options = $this->getUiOptions();
+    // Should be sorted by title ASC.
+    uasort($this->nodes, function (NodeInterface $a, NodeInterface $b) {
+      return strnatcasecmp($a->getTitle(), $b->getTitle());
+    });
+    $found_all = TRUE;
+    $i = 0;
+    foreach ($this->nodes as $nid => $node) {
+      $option = $options[$i];
+      $label = $option['label'];
+      $found_all = $found_all && $label == $node->label() && $nid == $option['nid'];
+      $this->assertEqual($label, $node->label(), new FormattableMarkup('Expected referenceable label found for option :option', [':option' => $i]));
+      $i++;
+    }
+    $this->assertTrue($found_all, 'All referenceable nodes were available as a select list properly ordered.');
+
+    // Change the sort field and direction.
+    $view = View::load('test_filter_entity_reference');
+    $display = & $view->getDisplay('default');
+    $display['display_options']['filters']['field_test_target_id']['handler_settings']['sort']['field'] = 'nid';
+    $display['display_options']['filters']['field_test_target_id']['handler_settings']['sort']['direction'] = 'DESC';
+    $view->save();
+
+    $this->drupalGet('admin/structure/views/nojs/handler/test_filter_entity_reference/default/filter/field_test_target_id');
+    // Items should now be in reverse nid order.
+    krsort($this->nodes);
+    $options = $this->getUiOptions();
+    $found_all = TRUE;
+    $i = 0;
+    foreach ($this->nodes as $nid => $node) {
+      $option = $options[$i];
+      $label = $option['label'];
+      $found_all = $found_all && $label == $node->label() && $nid == $option['nid'];
+      $this->assertEqual($label, $node->label(), new FormattableMarkup('Expected referenceable label found for option :option', [':option' => $i]));
+      $i++;
+    }
+    $this->assertTrue($found_all, 'All referenceable nodes were available as a select list properly ordered.');
+  }
+
+  /**
+   * Helper method to parse options from the UI.
+   *
+   * @return array
+   *   Array of keyed arrays containing `nid` and `label` of each option.
+   */
+  protected function getUiOptions() {
+    /** @var \Behat\Mink\Element\NodeElement[] $result */
+    $result = $this->xpath('//select[@name="options[value][]"]/option');
+    $this->assertNotEmpty($result, 'Options found');
+
+    $options = [];
+    foreach ($result as $option) {
+      $nid = (int) $option->getValue();
+      $options[] = [
+        'nid' => $nid,
+        'label' => (string) $this->getSession()->getDriver()->getText($option->getXpath()),
+      ];
+    }
+
+    return $options;
+  }
+
+}
diff --git a/core/themes/stable/templates/admin/views-ui-expose-filter-form.html.twig b/core/themes/stable/templates/admin/views-ui-expose-filter-form.html.twig
index a23b6e2..2a18712 100644
--- a/core/themes/stable/templates/admin/views-ui-expose-filter-form.html.twig
+++ b/core/themes/stable/templates/admin/views-ui-expose-filter-form.html.twig
@@ -27,12 +27,6 @@
 {{ form.operator }}
 {{ form.value }}
 
-{% if form.use_operator %}
-  <div class="views-left-40">
-  {{ form.use_operator }}
-  </div>
-{% endif %}
-
 {#
   Collect a list of elements printed to exclude when printing the
   remaining elements.
@@ -47,6 +41,7 @@
   'operator',
   'value',
   'use_operator',
+  'operator_id',
   'more'
   )
 %}
@@ -61,5 +56,15 @@
 {% else %}
   {{ remaining_form }}
 {% endif %}
+{% if form.use_operator %}
+  <div class="views-left-40">
+  {{ form.use_operator }}
+  </div>
+{% endif %}
+{% if form.operator_id %}
+  <div class="views-left-40">
+  {{ form.operator_id }}
+  </div>
+{% endif %}
 
 {{ form.more }}
