diff --git a/core/modules/node/src/Plugin/views/wizard/Node.php b/core/modules/node/src/Plugin/views/wizard/Node.php
index b29396fcbb..71c1c2faf6 100644
--- a/core/modules/node/src/Plugin/views/wizard/Node.php
+++ b/core/modules/node/src/Plugin/views/wizard/Node.php
@@ -172,7 +172,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..9b4ed296c1 100644
--- a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
+++ b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml
@@ -119,10 +119,17 @@ views.field.taxonomy_index_tid:
 views.filter.taxonomy_index_tid:
   type: views.filter.many_to_one
   label: 'Taxonomy term ID'
+  deprecated: "The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414"
   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 77c7966090..09d98aff76 100644
--- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
@@ -5,8 +5,9 @@
 use Drupal\Core\Entity\Element\EntityAutocomplete;
 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\Attribute\ViewsFilter;
 use Drupal\views\ViewExecutable;
@@ -95,7 +96,7 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, ?array &$
     parent::init($view, $display, $options);
 
     if (!empty($this->definition['vocabulary'])) {
-      $this->options['vid'] = $this->definition['vocabulary'];
+      $this->options['vids'] = [$this->definition['vocabulary']];
     }
   }
 
@@ -115,34 +116,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,
         ];
       }
     }
@@ -166,19 +161,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. 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),
       ];
@@ -186,30 +194,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->access('view label');
             }
-            $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.
@@ -220,15 +224,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());
       }
 
-      $default_value = (array) $this->value;
+      /** @var \Drupal\taxonomy\TermInterface[] $terms */
+      foreach ($terms as $term) {
+        $this->addOption($options, $term, $vocabularies);
+      }
+
+      $default_value = $this->value;
 
       if ($exposed = $form_state->get('exposed')) {
         $identifier = $this->options['expose']['identifier'];
@@ -260,12 +266,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,
       ];
 
@@ -393,7 +398,7 @@ 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();
       }
@@ -407,8 +412,10 @@ public function adminSummary() {
   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();
@@ -417,4 +424,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 = \Drupal::service('entity.repository')->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 f80db2876d..aa7915edf0 100644
--- a/core/modules/taxonomy/taxonomy.post_update.php
+++ b/core/modules/taxonomy/taxonomy.post_update.php
@@ -7,6 +7,8 @@
 
 use Drupal\Core\Config\Entity\ConfigEntityUpdater;
 use Drupal\taxonomy\VocabularyInterface;
+use Drupal\views\ViewEntityInterface;
+use Drupal\views\ViewsConfigUpdater;
 
 /**
  * Implements hook_removed_post_updates().
@@ -23,6 +25,23 @@ function taxonomy_removed_post_updates() {
   ];
 }
 
+/**
+ * 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);
+  });
+}
+
 /**
  * Re-save Taxonomy configurations with new_revision config.
  */
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
index e69de29bb2..ae8611c1a8 100644
--- a/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyTermIdFilterUpdateTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyTermIdFilterUpdateTest.php
@@ -0,0 +1,69 @@
+<?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 static $modules = ['taxonomy', 'views'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConfigSchemaExclusions() {
+    return ['views.view.test_filter_taxonomy_index_tid'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles(): void {
+    $this->databaseDumpFiles = [
+      dirname(__DIR__, 5) . '/system/tests/fixtures/update/drupal-9.4.0.bare.standard.php.gz',
+    ];
+  }
+
+  /**
+   * Tests that filter handlers are updated properly.
+   */
+  public function testPostUpdateTaxonomyIndexFilterMultipleVocabularies(): void {
+    $config_factory = \Drupal::configFactory();
+    // Prepare the test view with the old schema.
+    $view_as_array = Yaml::decode(file_get_contents(dirname(__DIR__, 3) . '/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml'));
+    $view_as_array['uuid'] = \Drupal::service('uuid')->generate();
+    unset($view_as_array['display']['default']['display_options']['filters']['tid']['vids']);
+    $view_as_array['display']['default']['display_options']['filters']['tid']['vid'] = 'tags';
+    $config_factory->getEditable('views.view.test_filter_taxonomy_index_tid')
+      ->setData($view_as_array)
+      ->save();
+
+    $path = 'display.default.display_options.filters.tid';
+    $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 386bce016b..4a0f0c7a39 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
@@ -70,11 +70,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
@@ -82,41 +105,30 @@ 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']);
-
-    // Extra taxonomy and terms.
-    Vocabulary::create([
-      'vid' => 'other_tags',
-      'name' => 'Other tags',
-    ])->save();
-
-    $this->terms[3][0] = $term = Term::create([
-      'vid' => 'tags',
-      'name' => "Term 3.0",
-    ]);
-    $term->save();
-
-    Vocabulary::create([
-      'vid' => 'empty_vocabulary',
-      'name' => 'Empty Vocabulary',
-    ])->save();
+    return $terms;
   }
 
   /**
    * Tests the filter UI.
+   *
+   * @group legacy
    */
   public function testFilterUI(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
     $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');
 
@@ -159,10 +171,90 @@ public function testFilterUI(): void {
     $this->assertSame($expected, $view->calculateDependencies()->getDependencies());
   }
 
+  /**
+   * Tests the filter UI with multiple vocabularies.
+   *
+   * @group legacy
+   */
+  public function testFilterUIWithMultipleVocabularies() {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
+    $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.
+   *
+   * @group legacy
    */
   public function testExposedFilter(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
     $node_type = $this->drupalCreateContentType(['type' => 'page']);
 
     // Create the tag field itself.
@@ -264,8 +356,12 @@ public function testExposedFilter(): void {
     $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');
@@ -292,8 +388,11 @@ public function testExposedFilter(): void {
 
   /**
    * Tests exposed grouped taxonomy filters.
+   *
+   * @group legacy
    */
   public function testExposedGroupedFilter(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
     // Create a content type with a taxonomy field.
     $this->drupalCreateContentType(['type' => 'article']);
     $field_name = 'field_views_testing_tags';
@@ -336,8 +435,12 @@ public function testExposedGroupedFilter(): void {
 
   /**
    * Tests that an exposed taxonomy filter doesn't show unpublished terms.
+   *
+   * @group legacy
    */
   public function testExposedUnpublishedFilterOptions(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
+
     $this->terms[1][0]->setUnpublished()->save();
     // Expose the filter.
     $this->drupalGet('admin/structure/views/nojs/handler/test_filter_taxonomy_index_tid/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 0e8324f82e..e4abf7399a 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyIndexTidFilterTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyIndexTidFilterTest.php
@@ -12,8 +12,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 {
@@ -60,8 +59,12 @@ protected function setUp($import_test_views = TRUE): void {
 
   /**
    * Tests dependencies are not added for terms that do not exist.
+   *
+   * @covers ::calculateDependencies
+   * @group legacy
    */
   public function testConfigDependency(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
     /** @var \Drupal\views\Entity\View $view */
     $view = View::load('test_filter_taxonomy_index_tid__non_existing_dependency');
 
@@ -101,4 +104,29 @@ public function testConfigDependency(): void {
     ], $view->calculateDependencies()->getDependencies());
   }
 
+  /**
+   * Tests 'taxonomy_index_tid' filter handler vocabulary dependencies.
+   *
+   * @covers ::calculateDependencies
+   * @group legacy
+   */
+  public function testMultipleVocabularies(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
+    /** @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/taxonomy/tests/src/Kernel/Views/TaxonomyTermFilterDepthTest.php b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyTermFilterDepthTest.php
index 411379bd41..9da2530639 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyTermFilterDepthTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyTermFilterDepthTest.php
@@ -85,8 +85,11 @@ protected function setUp($import_test_views = TRUE): void {
 
   /**
    * Tests the terms with depth filter.
+   *
+   * @group legacy
    */
   public function testTermWithDepthFilter(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
     // Default view has an empty value for this filter, so all nodes should be
     // returned.
     $expected = [
diff --git a/core/modules/views/src/ViewsConfigUpdater.php b/core/modules/views/src/ViewsConfigUpdater.php
index 8f9dfcfbf0..89b2d053e2 100644
--- a/core/modules/views/src/ViewsConfigUpdater.php
+++ b/core/modules/views/src/ViewsConfigUpdater.php
@@ -159,6 +159,9 @@ public function updateAll(ViewEntityInterface $view) {
       if ($this->addLabelIfMissing($view)) {
         $changed = TRUE;
       }
+      if ($this->processTidFilterWithMultipleVocabulariesHandler($handler, $handler_type)) {
+        $changed = TRUE;
+      }
       return $changed;
     });
   }
@@ -689,4 +692,40 @@ protected function processRenderedEntityFieldHandler(array &$handler, string $ha
     return $handler_type === 'field' && $plugin_id === 'rendered_entity';
   }
 
+  /**
+  * 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, FALSE, 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['plugin_id'] = 'taxonomy_index_tid_vids';
+      $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 a61c0adb62..0383893a59 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
diff --git a/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php b/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php
index a6e39524c2..67ab7bdb53 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php
@@ -83,8 +83,11 @@ protected function setUp($import_test_views = TRUE, $modules = []): void {
 
   /**
    * Tests overriding the default render option with checkboxes.
+   *
+   * @group legacy
    */
   public function testExposedFormRenderCheckboxes(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
     // Use a test theme to convert multi-select elements into checkboxes.
     \Drupal::service('theme_installer')->install(['views_test_checkboxes_theme']);
     $this->config('system.theme')
@@ -116,8 +119,11 @@ public function testExposedFormRenderCheckboxes(): void {
 
   /**
    * Tests that "is all of" filters work with checkboxes.
+   *
+   * @group legacy
    */
   public function testExposedIsAllOfFilter(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
     foreach (['Term 1', 'Term 2', 'Term 3'] as $term_name) {
       // Add a few terms to the new vocabulary.
       $term = Term::create([
diff --git a/core/modules/views/tests/src/Functional/Wizard/TaggedWithTest.php b/core/modules/views/tests/src/Functional/Wizard/TaggedWithTest.php
index eefa980b46..59860157e7 100644
--- a/core/modules/views/tests/src/Functional/Wizard/TaggedWithTest.php
+++ b/core/modules/views/tests/src/Functional/Wizard/TaggedWithTest.php
@@ -126,8 +126,11 @@ protected function setUp($import_test_views = TRUE, $modules = []): void {
 
   /**
    * Tests the "tagged with" functionality.
+   *
+   * @group legacy
    */
   public function testTaggedWith(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
     // In this test we will only create nodes that have an instance of the tag
     // field.
     $node_add_path = 'node/add/' . $this->nodeTypeWithTags->id();
diff --git a/core/modules/views/tests/src/Kernel/TestViewsTest.php b/core/modules/views/tests/src/Kernel/TestViewsTest.php
index cd03bdd44e..83e66e78fb 100644
--- a/core/modules/views/tests/src/Kernel/TestViewsTest.php
+++ b/core/modules/views/tests/src/Kernel/TestViewsTest.php
@@ -199,8 +199,11 @@ protected function setUp(): void {
 
   /**
    * Tests default configuration data type.
+   *
+   * @group legacy
    */
   public function testDefaultConfig(): void {
+    $this->expectDeprecation("The 'vid' key in 'views.filter.taxonomy_index_tid' config schema is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Update your view to use the 'vids' key instead. See https://www.drupal.org/node/3162414");
     // Create a typed config manager with access to configuration schema in
     // every module, profile and theme.
     $typed_config = new TypedConfigManager(
