diff --git a/core/modules/node/src/Plugin/views/wizard/Node.php b/core/modules/node/src/Plugin/views/wizard/Node.php
index ea00810862..2781818948 100644
--- a/core/modules/node/src/Plugin/views/wizard/Node.php
+++ b/core/modules/node/src/Plugin/views/wizard/Node.php
@@ -171,7 +171,7 @@ protected function defaultDisplayFiltersUser(array $form, FormStateInterface $fo
         'table' => 'taxonomy_index',
         'field' => 'tid',
         'value' => $tids,
-        'vid' => $vid,
+        'vids' => [$vid],
         'plugin_id' => 'taxonomy_index_tid',
       ];
       // If the user entered more than one valid term in the autocomplete
diff --git a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
index 9ec9758185..98be95b22d 100644
--- a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
+++ b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
@@ -120,9 +120,12 @@ views.filter.taxonomy_index_tid:
   type: views.filter.many_to_one
   label: 'Taxonomy term ID'
   mapping:
-    vid:
-      type: string
-      label: 'Vocabulary'
+    vids:
+      type: sequence
+      label: 'Vocabularies'
+      sequence:
+        type: string
+        label: 'Vocabulary'
     type:
       type: string
       label: 'Selection type'
diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
index e9c4c9368d..58c0cfd15c 100644
--- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
@@ -3,10 +3,12 @@
 namespace Drupal\taxonomy\Plugin\views\filter;
 
 use Drupal\Core\Entity\Element\EntityAutocomplete;
+use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
-use Drupal\taxonomy\Entity\Term;
+use Drupal\taxonomy\TermInterface;
 use Drupal\taxonomy\TermStorageInterface;
+use Drupal\taxonomy\VocabularyInterface;
 use Drupal\taxonomy\VocabularyStorageInterface;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
@@ -50,6 +52,13 @@ class TaxonomyIndexTid extends ManyToOne {
    */
   protected $currentUser;
 
+  /**
+   * The entity repository service.
+   *
+   * @var \Drupal\Core\Entity\EntityRepositoryInterface
+   */
+  protected $entityRepository;
+
   /**
    * Constructs a TaxonomyIndexTid object.
    *
@@ -83,7 +92,8 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_definition,
       $container->get('entity_type.manager')->getStorage('taxonomy_vocabulary'),
       $container->get('entity_type.manager')->getStorage('taxonomy_term'),
-      $container->get('current_user')
+      $container->get('current_user'),
+      $container->get('entity.repository')
     );
   }
 
@@ -94,7 +104,7 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o
     parent::init($view, $display, $options);
 
     if (!empty($this->definition['vocabulary'])) {
-      $this->options['vid'] = $this->definition['vocabulary'];
+      $this->options['vids'] = [$this->definition['vocabulary']];
     }
   }
 
@@ -114,34 +124,28 @@ protected function defineOptions() {
 
     $options['type'] = ['default' => 'textfield'];
     $options['limit'] = ['default' => TRUE];
-    $options['vid'] = ['default' => ''];
+    $options['vids'] = ['default' => []];
     $options['hierarchy'] = ['default' => FALSE];
     $options['error_message'] = ['default' => TRUE];
 
     return $options;
   }
 
+  /**
+   * {@inheritdoc}
+   */
   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();
-      }
-
+      $vocabularies = $this->vocabularyStorage->loadMultiple();
       if (empty($this->definition['vocabulary'])) {
-        $form['vid'] = [
-          '#type' => 'radios',
+        $form['vids'] = [
+          '#type' => 'checkboxes',
           '#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'],
+          '#options' => $this->getVocabularyLabels($vocabularies),
+          '#description' => $this->t('Select which vocabularies to show terms for in the regular options.'),
+          '#default_value' => $this->options['vids'],
+          '#required' => TRUE,
         ];
       }
     }
@@ -165,19 +169,32 @@ public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {
     ];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function submitExtraOptionsForm($form, FormStateInterface $form_state) {
+    $vids = $form_state->getValue(['options', 'vids']);
+    $form_state->setValue(['options', 'vids'], array_keys(array_filter($vids)));
+  }
+
   protected function valueForm(&$form, FormStateInterface $form_state) {
-    $vocabulary = $this->vocabularyStorage->load($this->options['vid']);
-    if (empty($vocabulary) && $this->options['limit']) {
+    $vocabularies = $this->vocabularyStorage->loadMultiple($this->options['vids']);
+    if (empty($vocabularies) && $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>',
+        '#markup' => '<div class="js-form-item form-item">' . $this->t('Invalid or no vocabularies are selected. Please select valid vocabularies in filter settings.') . '</div>',
       ];
       return;
     }
 
+    $form['value'] = [
+      '#title' => $this->options['limit'] ? $this->formatPlural(count($vocabularies), 'Select terms from vocabulary @vocabs', 'Select terms from vocabularies @vocabs', [
+        '@vocabs' => "'" . implode("', '", $this->getVocabularyLabels($vocabularies)) . "'",
+      ]) : $this->t('Select terms'),
+    ];
+
     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'),
+      $terms = $this->value ? $this->termStorage->loadMultiple($this->value) : [];
+      $form['value'] += [
         '#type' => 'textfield',
         '#default_value' => EntityAutocomplete::getEntityLabels($terms),
       ];
@@ -185,30 +202,26 @@ protected function valueForm(&$form, FormStateInterface $form_state) {
       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']['#selection_settings']['target_bundles'] = array_keys($vocabularies);
         $form['value']['#tags'] = TRUE;
         $form['value']['#process_default_value'] = FALSE;
       }
     }
     else {
+      $options = [];
       if (!empty($this->options['hierarchy']) && $this->options['limit']) {
-        $tree = $this->termStorage->loadTree($vocabulary->id(), 0, NULL, TRUE);
-        $options = [];
-
-        if ($tree) {
-          foreach ($tree as $term) {
-            if (!$term->isPublished() && !$this->currentUser->hasPermission('administer taxonomy')) {
-              continue;
+        $terms = [];
+        foreach ($vocabularies as $vocabulary) {
+          $terms = array_merge($terms, array_filter(
+            $this->termStorage->loadTree($vocabulary->id(), 0, NULL, TRUE), function (TermInterface $term): bool {
+              return $term->isPublished() || $this->currentUser->hasPermission('administer taxonomy');
             }
-            $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')
+        $query = $this->termStorage->getQuery()
           ->accessCheck(TRUE)
           // @todo Sorting on vocabulary properties -
           //   https://www.drupal.org/node/1821274.
@@ -219,15 +232,17 @@ protected function valueForm(&$form, FormStateInterface $form_state) {
           $query->condition('status', 1);
         }
         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();
+          $query->condition('vid', $this->options['vids'], 'IN');
         }
+        $terms = $this->termStorage->loadMultiple($query->execute());
+      }
+
+      /** @var \Drupal\taxonomy\TermInterface[] $terms */
+      foreach ($terms as $term) {
+        $this->addOption($options, $term, $vocabularies);
       }
 
-      $default_value = (array) $this->value;
+      $default_value = $this->value;
 
       if ($exposed = $form_state->get('exposed')) {
         $identifier = $this->options['expose']['identifier'];
@@ -259,12 +274,11 @@ protected function valueForm(&$form, FormStateInterface $form_state) {
           }
         }
       }
-      $form['value'] = [
+      $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)),
+        '#size' => min(9, count($options, COUNT_RECURSIVE)),
         '#default_value' => $default_value,
       ];
 
@@ -392,9 +406,11 @@ public function adminSummary() {
 
     if ($this->value) {
       $this->value = array_filter($this->value);
-      $terms = Term::loadMultiple($this->value);
+      $terms = $this->termStorage->loadMultiple($this->value);
       foreach ($terms as $term) {
-        $this->valueOptions[$term->id()] = \Drupal::service('entity.repository')->getTranslationFromContext($term)->label();
+        $this->valueOptions[$term->id()] = $this->entityRepository
+          ->getTranslationFromContext($term)
+          ->label();
       }
     }
     return parent::adminSummary();
@@ -419,8 +435,10 @@ public function getCacheContexts() {
   public function calculateDependencies() {
     $dependencies = parent::calculateDependencies();
 
-    $vocabulary = $this->vocabularyStorage->load($this->options['vid']);
-    $dependencies[$vocabulary->getConfigDependencyKey()][] = $vocabulary->getConfigDependencyName();
+    $vocabularies = $this->vocabularyStorage->loadMultiple($this->options['vids']);
+    foreach ($vocabularies as $vocabulary) {
+      $dependencies[$vocabulary->getConfigDependencyKey()][] = $vocabulary->getConfigDependencyName();
+    }
 
     foreach ($this->termStorage->loadMultiple($this->options['value']) as $term) {
       $dependencies[$term->getConfigDependencyKey()][] = $term->getConfigDependencyName();
@@ -429,4 +447,45 @@ public function calculateDependencies() {
     return $dependencies;
   }
 
+  /**
+   * Returns a list of vocabulary labels keyed by vocabulary ID.
+   *
+   * @param array $vocabularies
+   *   An associative array of vocabulary entities, keyed by vocabulary ID.
+   *
+   * @return array
+   *   Associative array of vocabulary labels keyed by vocabulary ID.
+   */
+  protected function getVocabularyLabels(array $vocabularies): array {
+    return array_map(function (VocabularyInterface $vocabulary): string {
+      return $vocabulary->label();
+    }, $vocabularies);
+  }
+
+  /**
+   * Adds an option to the filter settings select.
+   *
+   * @param array $options
+   *   The list of select options passed by reference.
+   * @param \Drupal\taxonomy\TermInterface $term
+   *   The term to be added as option.
+   * @param array $vocabularies
+   *   The list of vocabularies.
+   */
+  protected function addOption(array &$options, TermInterface $term, array $vocabularies): void {
+    $option = $this->entityRepository->getTranslationFromContext($term)->label();
+    if (!empty($this->options['hierarchy']) && $this->options['limit']) {
+      $option = str_repeat('-', $term->depth) . $option;
+    }
+
+    /** @var \Drupal\taxonomy\VocabularyInterface[] $vocabularies */
+    if (count($vocabularies) > 1) {
+      $vocabulary_label = $vocabularies[$term->get('vid')->target_id]->label();
+      $options[$vocabulary_label][$term->id()] = $option;
+    }
+    else {
+      $options[$term->id()] = $option;
+    }
+  }
+
 }
diff --git a/core/modules/taxonomy/taxonomy.post_update.php b/core/modules/taxonomy/taxonomy.post_update.php
index 6b5fefe6d0..ede376d8e4 100644
--- a/core/modules/taxonomy/taxonomy.post_update.php
+++ b/core/modules/taxonomy/taxonomy.post_update.php
@@ -5,6 +5,10 @@
  * Post update functions for Taxonomy.
  */
 
+use Drupal\Core\Config\Entity\ConfigEntityUpdater;
+use Drupal\views\ViewEntityInterface;
+use Drupal\views\ViewsConfigUpdater;
+
 /**
  * Implements hook_removed_post_updates().
  */
@@ -19,3 +23,21 @@ function taxonomy_removed_post_updates() {
     'taxonomy_post_update_clear_views_argument_validator_plugins_cache' => '10.0.0',
   ];
 }
+
+
+/**
+ * Allow multiple vocabularies for views using the taxonomy term ID filter.
+ */
+function taxonomy_post_update_multiple_vocabularies_filter(?array &$sandbox = NULL): void {
+   // If Views is not installed, there is nothing to do.
+   if (!\Drupal::moduleHandler()->moduleExists('views')) {
+       return;
+  }
+
+  /** @var \Drupal\views\ViewsConfigUpdater $view_config_updater */
+  $view_config_updater = \Drupal::classResolver(ViewsConfigUpdater::class);
+  $view_config_updater->setDeprecationsEnabled(FALSE);
+  \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function (ViewEntityInterface $view) use ($view_config_updater): bool {
+   return $view_config_updater->needsTidFilterWithMultipleVocabulariesUpdate($view);
+  });
+}
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 be15a8f3e7..a86492817a 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
@@ -165,7 +165,8 @@ display:
           reduce_duplicates: false
           type: select
           limit: true
-          vid: tags
+          vids:
+            - tags
           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 ea9f640813..ad91187217 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
@@ -166,7 +166,8 @@ display:
           reduce_duplicates: false
           type: select
           limit: true
-          vid: tags
+          vids:
+            - tags
           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_depth.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid_depth.yml
index 9b4c7847e0..2755a0a822 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
@@ -164,7 +164,8 @@ display:
           reduce_duplicates: false
           type: select
           limit: true
-          vid: views_testing_tags
+          vids:
+            - views_testing_tags
           hierarchy: true
           depth: -2
           error_message: true
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_exposed_grouped_filter.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_exposed_grouped_filter.yml
index cf92bcce3e..648fa93703 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_exposed_grouped_filter.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_exposed_grouped_filter.yml
@@ -212,7 +212,8 @@ display:
           reduce_duplicates: false
           type: select
           limit: true
-          vid: tags
+          vids:
+            - tags
           hierarchy: false
           error_message: true
           plugin_id: taxonomy_index_tid
diff --git a/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyTermIdFilterUpdateTest.php b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyTermIdFilterUpdateTest.php
new file mode 100644
index 0000000000..faf590aeae
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyTermIdFilterUpdateTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Drupal\Tests\taxonomy\Functional\Update;
+
+use Drupal\Core\Serialization\Yaml;
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * Tests update of term ID filter handlers to allow multiple vocabularies.
+ *
+ * @group taxonomy
+ * @group legacy
+ *
+ * @see taxonomy_post_update_multiple_vocabularies_filter()
+ */
+class TaxonomyTermIdFilterUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles(): void {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.8.0.bare.standard.php.gz',
+    ];
+  }
+
+  /**
+   * Tests that filter handlers are updated properly.
+   */
+  public function testPostUpdateTaxonomyIndexFilterMultipleVocabularies(): void {
+    // Prepare the test view with the old schema.
+    $view_as_array = Yaml::decode(file_get_contents(__DIR__ . '/../../../modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml'));
+    $view_as_array['uuid'] = \Drupal::service('uuid')->generate();
+    $view_as_array['display']['default']['display_options']['filters']['tid']['vid'] = 'tags';
+    unset($view_as_array['display']['default']['display_options']['filters']['tid']['vids']);
+    $db = \Drupal::database();
+    $db->insert('config')
+      ->fields([
+        'collection' => '',
+        'name' => 'views.view.test_filter_taxonomy_index_tid',
+        'data' => serialize($view_as_array),
+      ])->execute();
+
+    $path = 'display.default.display_options.filters.tid';
+    $config_factory = \Drupal::configFactory();
+    $view_as_config = $config_factory->get('views.view.test_filter_taxonomy_index_tid');
+
+    // Check that, before running updates, only the legacy 'vid' key exists.
+    $this->assertSame('tags', $view_as_config->get("{$path}.vid"));
+    $this->assertArrayNotHasKey('vids', $view_as_config->get($path));
+
+    $this->runUpdates();
+
+    $view_as_config = $config_factory->get('views.view.test_filter_taxonomy_index_tid');
+
+    // Check that, after running updates, only the new 'vids' key exists.
+    $this->assertSame(['tags'], $view_as_config->get("{$path}.vids"));
+    $this->assertArrayNotHasKey('vid', $view_as_config->get($path));
+  }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
index 89c93a465a..f7c3bab9aa 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
@@ -67,11 +67,34 @@ protected function setUp($import_test_views = TRUE, $modules = []): void {
     ]);
     $this->drupalLogin($this->adminUser);
 
+    $this->terms = $this->createVocabularyAndTerms('tags');
+    ViewTestData::createTestViews(get_class($this), ['taxonomy_test_views']);
+
     Vocabulary::create([
-      'vid' => 'tags',
-      'name' => 'Tags',
+      'vid' => 'empty_vocabulary',
+      'name' => 'Empty Vocabulary',
     ])->save();
+  }
 
+  /**
+   * Creates a vocabulary and terms for it.
+   *
+   * @param string $vocab_id
+   *   The vocabulary ID.
+   *
+   * @return \Drupal\taxonomy\TermInterface[][]
+   *   The terms.
+   *
+   * @throws \Drupal\Core\Entity\EntityStorageException
+   */
+  protected function createVocabularyAndTerms(string $vocab_id): array {
+    $vocab = Vocabulary::load($vocab_id);
+    if (!$vocab) {
+      Vocabulary::create([
+        'vid' => $vocab_id,
+        'name' => 'Test Vocabulary ' . $vocab_id,
+      ])->save();
+    }
     // Setup a hierarchy which looks like this:
     // term 0.0
     // term 1.0
@@ -79,22 +102,19 @@ protected function setUp($import_test_views = TRUE, $modules = []): void {
     // term 2.0
     // - term 2.1
     // - term 2.2
+    $terms = [];
     for ($i = 0; $i < 3; $i++) {
       for ($j = 0; $j <= $i; $j++) {
-        $this->terms[$i][$j] = $term = Term::create([
-          'vid' => 'tags',
+        $term = Term::create([
+          'vid' => $vocab_id,
           'name' => "Term $i.$j",
           'parent' => isset($this->terms[$i][0]) ? $this->terms[$i][0]->id() : 0,
         ]);
         $term->save();
+        $terms[$i][$j] = $term;
       }
     }
-    ViewTestData::createTestViews(static::class, ['taxonomy_test_views']);
-
-    Vocabulary::create([
-      'vid' => 'empty_vocabulary',
-      'name' => 'Empty Vocabulary',
-    ])->save();
+    return $terms;
   }
 
   /**
@@ -102,6 +122,7 @@ protected function setUp($import_test_views = TRUE, $modules = []): void {
    */
   public function testFilterUI() {
     $this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
+    $this->assertSession()->fieldExists("Select terms from vocabulary 'Test Vocabulary tags'");
 
     $result = $this->assertSession()->selectExists('edit-options-value')->findAll('css', 'option');
 
@@ -144,6 +165,80 @@ public function testFilterUI() {
     $this->assertSame($expected, $view->calculateDependencies()->getDependencies());
   }
 
+  /**
+   * Test filter UI with multiple vocabularies.
+   */
+  public function testFilterUIWithMultipleVocabularies() {
+    $terms2 = $this->createVocabularyAndTerms('tags2');
+    $node_type = $this->drupalCreateContentType(['type' => 'page']);
+    // Create the tag field itself.
+    $field_name = 'taxonomy_tags';
+    $this->createEntityReferenceField('node', $node_type->id(), $field_name, NULL, 'taxonomy_term');
+    $node0 = $this->drupalCreateNode([
+      'type' => 'page',
+      'taxonomy_tags' => $this->terms[0][0]->id(),
+    ]);
+    $node0->save();
+    $node1 = $this->drupalCreateNode([
+      'type' => 'page',
+      'taxonomy_tags' => $terms2[0][0]->id(),
+    ]);
+    $node1->save();
+    $node2 = $this->drupalCreateNode([
+      'type' => 'page',
+      'taxonomy_tags' => [$this->terms[0][0]->id(), $terms2[0][0]->id()],
+    ]);
+    $node2->save();
+    $edit = [
+      'options[vids][tags]' => TRUE,
+      'options[vids][tags2]' => TRUE,
+      'options[type]' => 'textfield',
+    ];
+    $this->drupalGet('admin/structure/views/nojs/handler-extra/test_filter_taxonomy_index_tid/default/filter/tid');
+    $this->submitForm($edit, 'Apply');
+    $this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/default/filter/tid');
+    $this->submitForm([], 'Expose filter');
+    $edit = [
+      'options[operator]' => 'and',
+      'options[value]' => '',
+      'options[reduce_duplicates]' => TRUE,
+    ];
+    $this->submitForm($edit, 'Apply');
+    $this->submitForm([], 'Save');
+    $this->drupalGet('test-filter-taxonomy-index-tid', ['query' => ['tid' => '']]);
+    $xpath = $this->xpath('//div[@class="views-row"]//a');
+    $this->assertCount(3, $xpath);
+    $this->drupalGet('test-filter-taxonomy-index-tid', ['query' => ['tid' => "t1 ({$this->terms[0][0]->id()})"]]);
+    $xpath = $this->xpath('//div[@class="views-row"]//a');
+    $this->assertCount(2, $xpath);
+    $xpath = $this->xpath('//div[@class="views-row"]//a[@href=:href]', [
+      ':href' => $node0->toUrl()->toString(),
+    ]);
+    $this->assertCount(1, $xpath);
+    $xpath = $this->xpath('//div[@class="views-row"]//a[@href=:href]', [
+      ':href' => $node2->toUrl()->toString(),
+    ]);
+    $this->assertCount(1, $xpath);
+    $this->drupalGet('test-filter-taxonomy-index-tid', ['query' => ['tid' => "t2 ({$terms2[0][0]->id()})"]]);
+    $xpath = $this->xpath('//div[@class="views-row"]//a');
+    $this->assertCount(2, $xpath);
+    $xpath = $this->xpath('//div[@class="views-row"]//a[@href=:href]', [
+      ':href' => $node1->toUrl()->toString(),
+    ]);
+    $this->assertCount(1, $xpath);
+    $xpath = $this->xpath('//div[@class="views-row"]//a[@href=:href]', [
+      ':href' => $node2->toUrl()->toString(),
+    ]);
+    $this->assertCount(1, $xpath);
+    $this->drupalGet('test-filter-taxonomy-index-tid', ['query' => ['tid' => "t1 ({$this->terms[0][0]->id()}), t2 ({$terms2[0][0]->id()})"]]);
+    $xpath = $this->xpath('//div[@class="views-row"]//a');
+    $this->assertCount(1, $xpath);
+    $xpath = $this->xpath('//div[@class="views-row"]//a[@href=:href]', [
+      ':href' => $node2->toUrl()->toString(),
+    ]);
+    $this->assertCount(1, $xpath);
+  }
+
   /**
    * Tests exposed taxonomy filters.
    */
@@ -249,8 +344,12 @@ public function testExposedFilter() {
     $this->drupalGet('admin/structure/views/nojs/add-handler/test_taxonomy_term_name/default/filter');
     $this->submitForm($edit, 'Add and configure filter criteria');
     // Select 'Empty Vocabulary' and 'Autocomplete' from the list of options.
+    $edit = [
+      'options[vids][empty_vocabulary]' => TRUE,
+      'options[type]' => 'textfield',
+    ];
     $this->drupalGet('admin/structure/views/nojs/handler-extra/test_taxonomy_term_name/default/filter/tid');
-    $this->submitForm([], 'Apply and continue');
+    $this->submitForm($edit, 'Apply and continue');
     // Expose the filter.
     $edit = ['options[expose_button][checkbox][checkbox]' => TRUE];
     $this->drupalGet('admin/structure/views/nojs/handler/test_taxonomy_term_name/default/filter/tid');
diff --git a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyIndexTidFilterTest.php b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyIndexTidFilterTest.php
index e58429f64d..9f8def62af 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyIndexTidFilterTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyIndexTidFilterTest.php
@@ -10,8 +10,7 @@
 /**
  * Test the taxonomy term index filter.
  *
- * @see \Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTid
- *
+ * @coversDefaultClass \Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTid
  * @group taxonomy
  */
 class TaxonomyIndexTidFilterTest extends TaxonomyTestBase {
@@ -58,6 +57,8 @@ protected function setUp($import_test_views = TRUE): void {
 
   /**
    * Tests dependencies are not added for terms that do not exist.
+   *
+   * @covers ::calculateDependencies
    */
   public function testConfigDependency() {
     /** @var \Drupal\views\Entity\View $view */
@@ -99,4 +100,27 @@ public function testConfigDependency() {
     ], $view->calculateDependencies()->getDependencies());
   }
 
+  /**
+   * Tests 'taxonomy_index_tid' filter handler vocabulary dependencies.
+   *
+   * @covers ::calculateDependencies
+   */
+  public function testMultipleVocabularies(): void {
+    /** @var \Drupal\views\Entity\View $view */
+    $view = View::load('test_filter_taxonomy_index_tid__non_existing_dependency');
+
+    // Add a second vocabulary to the view 'tid' filter handler.
+    Vocabulary::create(['vid' => 'second_vocab'])->save();
+    $displays = $view->get('display');
+    $displays['default']['display_options']['filters']['tid']['vids'][] = 'second_vocab';
+    $view->set('display', $displays);
+    $view->save();
+
+    // Check that the dependencies were updated.
+    $this->assertSame([
+      'taxonomy.vocabulary.second_vocab',
+      'taxonomy.vocabulary.tags',
+    ], $view->getDependencies()['config']);
+  }
+
 }
diff --git a/core/modules/views/src/ViewsConfigUpdater.php b/core/modules/views/src/ViewsConfigUpdater.php
index 11b3375228..80e2cb5e3d 100644
--- a/core/modules/views/src/ViewsConfigUpdater.php
+++ b/core/modules/views/src/ViewsConfigUpdater.php
@@ -260,8 +260,45 @@ protected function processOembedEagerLoadFieldHandler(array &$handler, string $h
       $deprecations_triggered = TRUE;
       @trigger_error(sprintf('The oEmbed loading attribute update for view "%s" is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Profile, module and theme provided configuration should be updated to accommodate the changes described at https://www.drupal.org/node/3275103.', $view->id()), E_USER_DEPRECATED);
     }
-
+    if ($this->processTidFilterWithMultipleVocabulariesHandler($handler, $handler_type)) {
+      $changed = TRUE;
+    }
     return $changed;
   }
 
+  /**
+   * Update taxonomy term ID filter handlers to allow multiple vocabularies.
+   *
+   * @param \Drupal\views\ViewEntityInterface $view
+   *   The View to update.
+   *
+   * @return bool
+   *   Whether the view was updated.
+   */
+  public function needsTidFilterWithMultipleVocabulariesUpdate(ViewEntityInterface $view): bool {
+    return $this->processDisplayHandlers($view, TRUE, function (array &$handler, string $handler_type): bool {
+      return $this->processTidFilterWithMultipleVocabulariesHandler($handler, $handler_type);
+    });
+  }
+
+  /**
+   * Processes taxonomy term ID filter handlers to allow multiple vocabularies.
+   *
+   * @param array $handler
+   *   A display handler.
+   * @param string $handler_type
+   *   The handler type.
+   *
+   * @return bool
+   *   Whether the handler was updated.
+   */
+  protected function processTidFilterWithMultipleVocabulariesHandler(array &$handler, string $handler_type): bool {
+    if ($handler_type === 'filter' && isset($handler['plugin_id']) && $handler['plugin_id'] === 'taxonomy_index_tid' && empty($handler['vids']) && !empty($handler['vid'])) {
+      $handler['vids'] = [$handler['vid']];
+      unset($handler['vid']);
+      return TRUE;
+    }
+    return FALSE;
+  }
+
 }
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 923c335cd1..73249e113c 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
@@ -107,7 +107,8 @@ display:
           reduce_duplicates: false
           type: select
           limit: true
-          vid: test_exposed_checkboxes
+          vids:
+            - test_exposed_checkboxes
           hierarchy: false
           error_message: true
           plugin_id: taxonomy_index_tid
