diff --git a/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue b/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue
index f4ce8f900ec4fbb919dda5c93e971961d0a7fc11..62420b0a278b0dc3c6e17fb5a5ccfe3e3f20981a 100644
--- a/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue
+++ b/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue
@@ -29,6 +29,7 @@ import createPipelineMutation from '../graphql/mutations/create_pipeline.mutatio
 import ciConfigVariablesQuery from '../graphql/queries/ci_config_variables.graphql';
 import filterVariables from '../utils/filter_variables';
 import RefsDropdown from './refs_dropdown.vue';
+import VariableValuesListbox from './variable_values_listbox.vue';
 
 let pollTimeout;
 export const POLLING_INTERVAL = 2000;
@@ -67,6 +68,7 @@ export default {
     GlSprintf,
     GlLoadingIcon,
     RefsDropdown,
+    VariableValuesListbox,
     CcValidationRequiredAlert: () =>
       import('ee_component/billings/components/cc_validation_required_alert.vue'),
   },
@@ -471,12 +473,10 @@ export default {
             data-testid="pipeline-form-ci-variable-key-field"
             @change="addEmptyVariable(refFullName)"
           />
-          <gl-collapsible-listbox
+          <variable-values-listbox
             v-if="shouldShowValuesDropdown(variable.key)"
             :items="createListItemsFromVariableOptions(variable.key)"
             :selected="variable.value"
-            block
-            fluid-width
             :class="$options.formElementClasses"
             class="gl-flex-grow-1 gl-mr-0!"
             data-testid="pipeline-form-ci-variable-value-dropdown"
diff --git a/app/assets/javascripts/ci/pipeline_new/components/variable_values_listbox.vue b/app/assets/javascripts/ci/pipeline_new/components/variable_values_listbox.vue
new file mode 100644
index 0000000000000000000000000000000000000000..2c660afbeb9753e298efa94a3337439d7b965b0e
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_new/components/variable_values_listbox.vue
@@ -0,0 +1,67 @@
+<script>
+import { GlCollapsibleListbox } from '@gitlab/ui';
+import fuzzaldrinPlus from 'fuzzaldrin-plus';
+import { n__ } from '~/locale';
+
+export default {
+  name: 'VariableValuesListbox',
+  components: {
+    GlCollapsibleListbox,
+  },
+  props: {
+    selected: {
+      type: String,
+      required: true,
+    },
+    items: {
+      type: Array,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      searchTerm: '',
+    };
+  },
+  computed: {
+    searchSummary() {
+      return n__(
+        'CiVariables|%d value found',
+        'CiVariables|%d values found',
+        this.filteredItems.length,
+      );
+    },
+    filteredItems() {
+      if (this.searchTerm) {
+        return fuzzaldrinPlus.filter(this.items, this.searchTerm, {
+          key: ['text'],
+        });
+      }
+      return this.items;
+    },
+  },
+  methods: {
+    onSearch(searchTerm) {
+      this.searchTerm = searchTerm.trim().toLowerCase();
+    },
+  },
+};
+</script>
+<template>
+  <gl-collapsible-listbox
+    :items="filteredItems"
+    :toggle-text="selected"
+    :selected="selected"
+    :search-placeholder="s__('CiVariables|Search values')"
+    :no-results-text="s__('CiVariables|No matching values')"
+    searchable
+    block
+    fluid-width
+    @search="onSearch"
+    @select="$emit('select', $event)"
+  >
+    <template #search-summary-sr-only>
+      {{ searchSummary }}
+    </template>
+  </gl-collapsible-listbox>
+</template>
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0c8e45053b45835bd80e0407324c3aae27f49f13..c580954af7d7895a7320ce0fb7690ab6dab1f985 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -10728,6 +10728,11 @@ msgstr ""
 msgid "CiStatusText|Warning"
 msgstr ""
 
+msgid "CiVariables|%d value found"
+msgid_plural "CiVariables|%d values found"
+msgstr[0] ""
+msgstr[1] ""
+
 msgid "CiVariables|%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable."
 msgstr ""
 
@@ -10800,6 +10805,9 @@ msgstr ""
 msgid "CiVariables|Maximum number of variables reached."
 msgstr ""
 
+msgid "CiVariables|No matching values"
+msgstr ""
+
 msgid "CiVariables|Protect variable"
 msgstr ""
 
@@ -10821,6 +10829,9 @@ msgstr ""
 msgid "CiVariables|Scope"
 msgstr ""
 
+msgid "CiVariables|Search values"
+msgstr ""
+
 msgid "CiVariables|Specify variable values to be used in this run. The variables specified in the configuration file and %{linkStart}CI/CD settings%{linkEnd} are used by default."
 msgstr ""
 
diff --git a/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js b/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js
index 369c8794fa9d592454d98e7fe6be38995b7b5047..b0a1232c2be0642ba74878bc54dc445de7caaecd 100644
--- a/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js
+++ b/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js
@@ -20,6 +20,7 @@ import PipelineNewForm, {
 import ciConfigVariablesQuery from '~/ci/pipeline_new/graphql/queries/ci_config_variables.graphql';
 import { resolvers } from '~/ci/pipeline_new/graphql/resolvers';
 import RefsDropdown from '~/ci/pipeline_new/components/refs_dropdown.vue';
+import VariableValuesListbox from '~/ci/pipeline_new/components/variable_values_listbox.vue';
 import {
   mockCreditCardValidationRequiredError,
   mockCiConfigVariablesResponse,
@@ -62,8 +63,7 @@ describe('Pipeline New Form', () => {
     wrapper.findAllByTestId('pipeline-form-ci-variable-type');
   const findKeyInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-key-field');
   const findValueInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-value-field');
-  const findCollapsableListWithVariableOptions = () =>
-    wrapper.findAllByTestId('pipeline-form-ci-variable-value-dropdown').at(0);
+  const findCollapsableListWithVariableOptions = () => wrapper.findComponent(VariableValuesListbox);
   const findErrorAlert = () => wrapper.findByTestId('run-pipeline-error-alert');
   const findPipelineConfigButton = () => wrapper.findByTestId('ci-cd-pipeline-configuration');
   const findWarningAlert = () => wrapper.findByTestId('run-pipeline-warning-alert');
diff --git a/spec/frontend/ci/pipeline_new/components/variable_values_listbox_spec.js b/spec/frontend/ci/pipeline_new/components/variable_values_listbox_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..5226d962f6196f8a5a97c59e0bbe55d0f1c2a0eb
--- /dev/null
+++ b/spec/frontend/ci/pipeline_new/components/variable_values_listbox_spec.js
@@ -0,0 +1,72 @@
+import { GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import VariableValuesListbox from '~/ci/pipeline_new/components/variable_values_listbox.vue';
+import { mockYamlVariables } from '../mock_data';
+
+const { value, valueOptions } = mockYamlVariables[2];
+
+describe('Variable values listbox', () => {
+  let wrapper;
+
+  const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
+  const findListboxItems = () => wrapper.findAllComponents(GlListboxItem);
+  const search = (searchString) => findListbox().vm.$emit('search', searchString);
+
+  const createComponent = () => {
+    wrapper = shallowMountExtended(VariableValuesListbox, {
+      propsData: {
+        selected: value,
+        items: valueOptions.map((option) => ({
+          text: option,
+          value: option,
+        })),
+      },
+      stubs: {
+        GlCollapsibleListbox,
+      },
+    });
+  };
+
+  beforeEach(() => {
+    createComponent();
+  });
+
+  it('multiple predefined values are rendered as a dropdown', () => {
+    for (let i = 0; i < valueOptions.length; i += 1) {
+      expect(findListboxItems().at(i).text()).toBe(valueOptions[i]);
+    }
+  });
+
+  it('variable with multiple predefined values sets value as the default', () => {
+    expect(findListbox().props('selected')).toBe(value);
+  });
+
+  it('filters options based on search', async () => {
+    const searchString = 'prod';
+
+    search(searchString);
+
+    await nextTick();
+
+    expect(findListboxItems().length).toBe(1);
+    expect(findListboxItems().at(0).text()).toContain(searchString);
+
+    search('');
+
+    await nextTick();
+
+    expect(findListboxItems().length).toBe(3);
+  });
+
+  it('filters options with fuzzy filtering', async () => {
+    const searchString = 'poduct';
+
+    search(searchString);
+
+    await nextTick();
+
+    expect(findListboxItems().length).toBe(1);
+    expect(findListboxItems().at(0).text()).toBe('production');
+  });
+});