diff --git a/app/assets/javascripts/admin/users/components/admin_users_filter_app.vue b/app/assets/javascripts/admin/users/components/admin_users_filter_app.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b569c6d23fb9727eac1dd4a3e198ccd2d1206679
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/admin_users_filter_app.vue
@@ -0,0 +1,65 @@
+<script>
+import { GlFilteredSearch } from '@gitlab/ui';
+import { setUrlParams, visitUrl } from '~/lib/utils/url_utility';
+import { TOKENS } from '../constants';
+import { initializeValuesFromQuery } from '../utils';
+
+const TOKEN_TYPES = TOKENS.map(({ type }) => type);
+
+export default {
+  name: 'AdminUsersFilterApp',
+  components: {
+    GlFilteredSearch,
+  },
+  data() {
+    const { tokens, sort } = initializeValuesFromQuery();
+
+    return {
+      tokens,
+      sort,
+    };
+  },
+  computed: {
+    availableTokens() {
+      // Once a token is selected, discard the rest
+      const token = this.tokens.find(({ type }) => TOKEN_TYPES.includes(type));
+
+      if (token) {
+        return TOKENS.filter(({ type }) => type === token.type);
+      }
+
+      return TOKENS;
+    },
+  },
+  methods: {
+    search(tokens) {
+      const newParams = {};
+
+      tokens?.forEach((token) => {
+        if (typeof token === 'string') {
+          newParams.search_query = token;
+        } else {
+          newParams.filter = token.value.data;
+        }
+      });
+
+      if (this.sort) {
+        newParams.sort = this.sort;
+      }
+
+      const newUrl = setUrlParams(newParams, window.location.href, true);
+      visitUrl(newUrl);
+    },
+  },
+};
+</script>
+
+<template>
+  <gl-filtered-search
+    v-model="tokens"
+    :placeholder="s__('AdminUsers|Search by name, email, or username')"
+    :available-tokens="availableTokens"
+    terms-as-tokens
+    @submit="search"
+  />
+</template>
diff --git a/app/assets/javascripts/admin/users/constants.js b/app/assets/javascripts/admin/users/constants.js
index 73383623aa2d310a87f95ff6c83fd1f3bdbe7e7f..8ac77739ad9ee0692fbf67505006261d1c2b5709 100644
--- a/app/assets/javascripts/admin/users/constants.js
+++ b/app/assets/javascripts/admin/users/constants.js
@@ -1,3 +1,6 @@
+import { GlFilteredSearchToken } from '@gitlab/ui';
+
+import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
 import { s__, __ } from '~/locale';
 
 export const I18N_USER_ACTIONS = {
@@ -18,3 +21,46 @@ export const I18N_USER_ACTIONS = {
   trust: s__('AdminUsers|Trust user'),
   untrust: s__('AdminUsers|Untrust user'),
 };
+
+export const TOKENS = [
+  {
+    title: s__('AdminUsers|Access level'),
+    type: 'access_level',
+    token: GlFilteredSearchToken,
+    operators: OPERATORS_IS,
+    unique: true,
+    options: [
+      { value: 'admins', title: s__('AdminUsers|Administrator') },
+      { value: 'external', title: s__('AdminUsers|External') },
+    ],
+  },
+  {
+    title: __('State'),
+    type: 'state',
+    token: GlFilteredSearchToken,
+    operators: OPERATORS_IS,
+    unique: true,
+    options: [
+      { value: 'banned', title: s__('AdminUsers|Banned') },
+      { value: 'blocked', title: s__('AdminUsers|Blocked') },
+      { value: 'deactivated', title: s__('AdminUsers|Deactivated') },
+      {
+        value: 'blocked_pending_approval',
+        title: s__('AdminUsers|Pending approval'),
+      },
+      { value: 'trusted', title: s__('AdminUsers|Trusted') },
+      { value: 'wop', title: s__('AdminUsers|Without projects') },
+    ],
+  },
+  {
+    title: s__('AdminUsers|Two-factor authentication'),
+    type: '2fa',
+    token: GlFilteredSearchToken,
+    operators: OPERATORS_IS,
+    unique: true,
+    options: [
+      { value: 'two_factor_enabled', title: __('On') },
+      { value: 'two_factor_disabled', title: __('Off') },
+    ],
+  },
+];
diff --git a/app/assets/javascripts/admin/users/index.js b/app/assets/javascripts/admin/users/index.js
index 2bd37d3fffe6f4de09bc9ca6fdee0cb56d2f9abd..ae9fe207eff95a549188f743113f72da3dabf394 100644
--- a/app/assets/javascripts/admin/users/index.js
+++ b/app/assets/javascripts/admin/users/index.js
@@ -4,6 +4,7 @@ import createDefaultClient from '~/lib/graphql';
 import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
 import csrf from '~/lib/utils/csrf';
 import AdminUsersApp from './components/app.vue';
+import AdminUsersFilterApp from './components/admin_users_filter_app.vue';
 import DeleteUserModal from './components/modals/delete_user_modal.vue';
 import UserActions from './components/user_actions.vue';
 
@@ -34,12 +35,19 @@ const initApp = (el, component, userPropKey, props = {}) => {
   });
 };
 
-export const initAdminUsersApp = (el = document.querySelector('#js-admin-users-app')) =>
-  initApp(el, AdminUsersApp, 'users');
+export const initAdminUsersFilterApp = () => {
+  return new Vue({
+    el: document.querySelector('#js-admin-users-filter-app'),
+    render: (createElement) => createElement(AdminUsersFilterApp),
+  });
+};
 
 export const initAdminUserActions = (el = document.querySelector('#js-admin-user-actions')) =>
   initApp(el, UserActions, 'user', { showButtonLabels: true });
 
+export const initAdminUsersApp = (el = document.querySelector('#js-admin-users-app')) =>
+  initApp(el, AdminUsersApp, 'users');
+
 export const initDeleteUserModals = () => {
   return new Vue({
     functional: true,
diff --git a/app/assets/javascripts/admin/users/utils.js b/app/assets/javascripts/admin/users/utils.js
index f6c1091ba27640b725ef1677f2d3ee5f2f95a09b..f40d634321124f0d723c622fc16532e7df65d243 100644
--- a/app/assets/javascripts/admin/users/utils.js
+++ b/app/assets/javascripts/admin/users/utils.js
@@ -1,3 +1,6 @@
+import { queryToObject } from '~/lib/utils/url_utility';
+import { TOKENS } from './constants';
+
 export const generateUserPaths = (paths, id) => {
   return Object.fromEntries(
     Object.entries(paths).map(([action, genericPath]) => {
@@ -5,3 +8,39 @@ export const generateUserPaths = (paths, id) => {
     }),
   );
 };
+
+/**
+ * @typedef {{type: string, value: {data: string, operator: string}}} Token
+ */
+
+/**
+ * Initialize token values based on the URL parameters
+ * @param {string} query - document.location.searchd
+ *
+ * @returns {{tokens: Array<string|Token>, sort: string}}
+ */
+export function initializeValuesFromQuery(query = document.location.search) {
+  const tokens = [];
+
+  const { filter, search_query: searchQuery, sort } = queryToObject(query);
+
+  if (filter) {
+    const token = TOKENS.find(({ options }) => options.some(({ value }) => value === filter));
+
+    if (token) {
+      tokens.push({
+        type: token.type,
+        value: {
+          data: filter,
+          operator: token.operators[0].value,
+        },
+      });
+    }
+  }
+
+  if (searchQuery) {
+    tokens.push(searchQuery);
+  }
+
+  return { tokens, sort };
+}
diff --git a/app/assets/javascripts/pages/admin/users/index.js b/app/assets/javascripts/pages/admin/users/index.js
index 41e99a3baf5415f708ffa157390399eb2276a177..0add5f24e2c382b230ae754269a7ce510bb87ad8 100644
--- a/app/assets/javascripts/pages/admin/users/index.js
+++ b/app/assets/javascripts/pages/admin/users/index.js
@@ -1,7 +1,13 @@
-import { initAdminUsersApp, initDeleteUserModals, initAdminUserActions } from '~/admin/users';
+import {
+  initAdminUsersFilterApp,
+  initAdminUserActions,
+  initAdminUsersApp,
+  initDeleteUserModals,
+} from '~/admin/users';
 import initConfirmModal from '~/confirm_modal';
 
-initAdminUsersApp();
+initAdminUsersFilterApp();
 initAdminUserActions();
+initAdminUsersApp();
 initDeleteUserModals();
 initConfirmModal();
diff --git a/app/views/admin/cohorts/_cohorts_table.html.haml b/app/views/admin/cohorts/_cohorts_table.html.haml
index ef24bd9e2e8132f50272d0b565643aad097232a9..361824e428899e5f99a85dc0b120dccb1ee4fa83 100644
--- a/app/views/admin/cohorts/_cohorts_table.html.haml
+++ b/app/views/admin/cohorts/_cohorts_table.html.haml
@@ -1,6 +1,6 @@
 - number_of_data_columns = @cohorts[:months_included] - 1
 
-.table-holder.d-xl-table.gl-mt-5
+.table-holder.d-xl-table
   %table.table
     %thead
       %tr
diff --git a/app/views/admin/users/_tabs.html.haml b/app/views/admin/users/_tabs.html.haml
index 6c14e1189fe91e613d99c153204c80435dd0d0be..6bdfaf0cf12dbae449390c76f2d7d65104776e56 100644
--- a/app/views/admin/users/_tabs.html.haml
+++ b/app/views/admin/users/_tabs.html.haml
@@ -1,3 +1,3 @@
-= gl_tabs_nav({ class: 'js-users-tabs' }) do
+= gl_tabs_nav({ class: 'js-users-tabs gl-flex-grow-1 gl-border-0' }) do
   = gl_tab_link_to s_('AdminUsers|Users'), admin_users_path
   = gl_tab_link_to s_('AdminUsers|Cohorts'), admin_cohorts_path
diff --git a/app/views/admin/users/_users.html.haml b/app/views/admin/users/_users.html.haml
index 3daf3fe19227663e809457f122161cf9290d2fe5..55067bb046fb3bf9cdd08520f45ea3b8cee7f76f 100644
--- a/app/views/admin/users/_users.html.haml
+++ b/app/views/admin/users/_users.html.haml
@@ -7,7 +7,13 @@
     - c.with_body do
       = render 'shared/registration_features_discovery_message', feature_title: s_('RegistrationFeatures|send emails to users')
 
-.top-area
+- if Feature.enabled?(:admin_user_filtered_nav)
+  .gl-display-flex.gl-align-items-flex-start.gl-flex-wrap.gl-md-flex-nowrap.gl-gap-4.row-content-block.gl-border-0{ data: { testid: "filtered-search-block" } }
+    #js-admin-users-filter-app
+    .gl-flex-shrink-0
+      = label_tag s_('AdminUsers|Sort by')
+      = gl_redirect_listbox_tag admin_users_sort_options(filter: params[:filter], search_query: params[:search_query]), @sort,  data: { placement: 'right' }
+- else
   .scrolling-tabs-container.inner-page-scroll-tabs.gl-flex-grow-1.gl-min-w-0.gl-w-full
     %button.fade-left{ type: 'button', title: _('Scroll left'), 'aria-label': _('Scroll left') }
       = sprite_icon('chevron-lg-left', size: 12)
@@ -17,56 +23,51 @@
       = gl_tab_link_to admin_users_path, { item_active: active_when(params[:filter].nil?), class: 'gl-border-0!' } do
         = s_('AdminUsers|Active')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.active_without_ghosts))
-      = gl_tab_link_to admin_users_path(filter: "admins"), { item_active: active_when(params[:filter] == 'admins'), class: 'gl-border-0!' } do
+      = gl_tab_link_to admin_users_path(filter: "admins"), { item_active: active_when(params[:filter] == 'admins'), class:  'gl-border-0!' } do
         = s_('AdminUsers|Admins')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.admins))
-      = gl_tab_link_to admin_users_path(filter: 'two_factor_enabled'), { item_active: active_when(params[:filter] == 'two_factor_enabled'), class: 'filter-two-factor-enabled gl-border-0!' } do
+      = gl_tab_link_to admin_users_path(filter: 'two_factor_enabled'), { item_active: active_when(params[:filter] ==  'two_factor_enabled'), class: 'filter-two-factor-enabled gl-border-0!' } do
         = s_('AdminUsers|2FA Enabled')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.with_two_factor))
-      = gl_tab_link_to admin_users_path(filter: 'two_factor_disabled'), { item_active: active_when(params[:filter] == 'two_factor_disabled'), class: 'filter-two-factor-disabled gl-border-0!' } do
+      = gl_tab_link_to admin_users_path(filter: 'two_factor_disabled'), { item_active: active_when(params[:filter] ==   'two_factor_disabled'), class: 'filter-two-factor-disabled gl-border-0!' } do
         = s_('AdminUsers|2FA Disabled')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.without_two_factor))
-      = gl_tab_link_to admin_users_path(filter: 'external'), { item_active: active_when(params[:filter] == 'external'), class: 'gl-border-0!' } do
+      = gl_tab_link_to admin_users_path(filter: 'external'), { item_active: active_when(params[:filter] == 'external'), class:  'gl-border-0!' } do
         = s_('AdminUsers|External')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.external))
-      = gl_tab_link_to admin_users_path(filter: "blocked"), { item_active: active_when(params[:filter] == 'blocked'), class: 'gl-border-0!' } do
+      = gl_tab_link_to admin_users_path(filter: "blocked"), { item_active: active_when(params[:filter] == 'blocked'), class:  'gl-border-0!' } do
         = s_('AdminUsers|Blocked')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.blocked))
-      = gl_tab_link_to admin_users_path(filter: "banned"), { item_active: active_when(params[:filter] == 'banned'), class: 'gl-border-0!' } do
+      = gl_tab_link_to admin_users_path(filter: "banned"), { item_active: active_when(params[:filter] == 'banned'), class:  'gl-border-0!' } do
         = s_('AdminUsers|Banned')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.banned))
-      = gl_tab_link_to admin_users_path(filter: "blocked_pending_approval"), { item_active: active_when(params[:filter] == 'blocked_pending_approval'), class: 'filter-blocked-pending-approval gl-border-0!', data: { testid: 'pending-approval-tab' } } do
+      = gl_tab_link_to admin_users_path(filter: "blocked_pending_approval"), { item_active: active_when(params[:filter] ==  'blocked_pending_approval'), class: 'filter-blocked-pending-approval gl-border-0!', data: { testid:  'pending-approval-tab' } } do
         = s_('AdminUsers|Pending approval')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.blocked_pending_approval))
-      = gl_tab_link_to admin_users_path(filter: "deactivated"), { item_active: active_when(params[:filter] == 'deactivated'), class: 'gl-border-0!' } do
+      = gl_tab_link_to admin_users_path(filter: "deactivated"), { item_active: active_when(params[:filter] == 'deactivated'),   class: 'gl-border-0!' } do
         = s_('AdminUsers|Deactivated')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.deactivated))
-      = gl_tab_link_to admin_users_path(filter: "wop"), { item_active: active_when(params[:filter] == 'wop'), class: 'gl-border-0!' } do
+      = gl_tab_link_to admin_users_path(filter: "wop"), { item_active: active_when(params[:filter] == 'wop'), class:  'gl-border-0!' } do
         = s_('AdminUsers|Without projects')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.without_projects))
-      = gl_tab_link_to admin_users_path(filter: "trusted"), { item_active: active_when(params[:filter] == 'trusted'), class: 'gl-border-0!' } do
+      = gl_tab_link_to admin_users_path(filter: "trusted"), { item_active: active_when(params[:filter] == 'trusted'), class:  'gl-border-0!' } do
         = s_('AdminUsers|Trusted')
         = gl_tab_counter_badge(limited_counter_with_delimiter(User.trusted))
-  .nav-controls
-    = render_if_exists 'admin/users/admin_email_users'
-    = render_if_exists 'admin/users/admin_export_user_permissions'
-    = render Pajamas::ButtonComponent.new(variant: :confirm, href: new_admin_user_path) do
-      = s_('AdminUsers|New user')
 
-.row-content-block.gl-border-0{ data: { testid: "filtered-search-block" } }
-  = form_tag admin_users_path, method: :get do
-    - if params[:filter].present?
-      = hidden_field_tag "filter", h(params[:filter])
-    .search-holder
-      .search-field-holder
-        = search_field_tag :search_query, params[:search_query], placeholder: s_('AdminUsers|Search by name, email, or username'), class: 'form-control search-text-input js-search-input', spellcheck: false, data: { testid: 'user-search-field' }
-        - if @sort.present?
-          = hidden_field_tag :sort, @sort
-        = sprite_icon('search', css_class: 'search-icon')
-        = button_tag s_('AdminUsers|Search users') if Rails.env.test?
-      .dropdown.gl-sm-ml-3
-        = label_tag s_('AdminUsers|Sort by')
-        = gl_redirect_listbox_tag admin_users_sort_options(filter: params[:filter], search_query: params[:search_query]), @sort, data: { placement: 'right' }
+  .row-content-block.gl-border-0{ data: { testid: "filtered-search-block" } }
+    = form_tag admin_users_path, method: :get do
+      - if params[:filter].present?
+        = hidden_field_tag "filter", h(params[:filter])
+      .search-holder
+        .search-field-holder
+          = search_field_tag :search_query, params[:search_query], placeholder: s_('AdminUsers|Search by name, email, or username'), class: 'form-control search-text-input js-search-input', spellcheck: false, data: { testid:  'user-search-field' }
+          - if @sort.present?
+            = hidden_field_tag :sort, @sort
+          = sprite_icon('search', css_class: 'search-icon')
+          = button_tag s_('AdminUsers|Search users') if Rails.env.test?
+        .dropdown.gl-sm-ml-3
+          = label_tag s_('AdminUsers|Sort by')
+          = gl_redirect_listbox_tag admin_users_sort_options(filter: params[:filter], search_query: params[:search_query]), @sort,  data: { placement: 'right' }
 
 #js-admin-users-app{ data: admin_users_data_attributes(@users) }
   = render Pajamas::SpinnerComponent.new(size: :lg, class: 'gl-my-7')
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 86b777d8458bbf01f1c4b4af356ce401256e2056..3e14cd8bf490b10482468037719023245edfb2c0 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -1,6 +1,12 @@
 - page_title _("Users")
 
-= render 'tabs'
+.top-area
+  = render 'tabs'
+  .nav-controls
+    = render_if_exists 'admin/users/admin_email_users'
+    = render_if_exists 'admin/users/admin_export_user_permissions'
+    = render Pajamas::ButtonComponent.new(variant: :confirm, href: new_admin_user_path) do
+      = s_('AdminUsers|New user')
 
 .tab-content
   .tab-pane.active
diff --git a/config/feature_flags/gitlab_com_derisk/admin_user_filtered_nav.yml b/config/feature_flags/gitlab_com_derisk/admin_user_filtered_nav.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f639f56c090e635fb99231cae6aa5a6c8e3ddc3c
--- /dev/null
+++ b/config/feature_flags/gitlab_com_derisk/admin_user_filtered_nav.yml
@@ -0,0 +1,9 @@
+---
+name: admin_user_filtered_nav
+feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/238183
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149471
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/455962
+milestone: '17.0'
+group: group::authentication
+type: gitlab_com_derisk
+default_enabled: false
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1817711dda22a872f4a2b50305919637bedfc282..a7e50657f8265cc4da75cc3744011e90d773c6c6 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -4366,6 +4366,9 @@ msgstr ""
 msgid "AdminUsers|Trusted"
 msgstr ""
 
+msgid "AdminUsers|Two-factor authentication"
+msgstr ""
+
 msgid "AdminUsers|Unban user"
 msgstr ""
 
@@ -49632,6 +49635,9 @@ msgstr ""
 msgid "Starts: %{startsAt}"
 msgstr ""
 
+msgid "State"
+msgstr ""
+
 msgid "State your message to activate"
 msgstr ""
 
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index d3fe47605174353cb083f2a66d0a7ef604b67ee3..a3c7383097b089295eb7fa0772e35361416125b7 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -8,11 +8,11 @@
   include ListboxHelpers
 
   let_it_be(:user, reload: true) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
-  let_it_be(:current_user) { create(:admin) }
+  let_it_be(:admin) { create(:admin) }
 
   before do
-    sign_in(current_user)
-    enable_admin_mode!(current_user)
+    sign_in(admin)
+    enable_admin_mode!(admin)
   end
 
   describe 'GET /admin/users', :js do
@@ -25,11 +25,10 @@
     end
 
     it "has users list" do
-      current_user.reload
+      admin.reload
 
-      expect(page).to have_content(current_user.email)
-      expect(page).to have_content(current_user.name)
-      expect(page).to have_content(current_user.created_at.strftime('%b %d, %Y'))
+      expect(page).to have_content(admin.name)
+      expect(page).to have_content(admin.created_at.strftime('%b %d, %Y'))
       expect(page).to have_content(user.email)
       expect(page).to have_content(user.name)
       expect(page).to have_content('Projects')
@@ -65,36 +64,35 @@
 
     context 'user project count' do
       before do
-        project = create(:project)
-        project.add_maintainer(current_user)
+        create(:project, maintainers: admin)
       end
 
       it 'displays count of users projects' do
         visit admin_users_path
 
-        expect(find_by_testid("user-project-count-#{current_user.id}").text).to eq("1")
+        expect(find_by_testid("user-project-count-#{admin.id}").text).to eq("1")
       end
     end
 
-    describe 'tabs' do
-      it 'has multiple tabs to filter users' do
-        expect(page).to have_link('Active', href: admin_users_path)
-        expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins'))
-        expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled'))
-        expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
-        expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
-        expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
-        expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
-        expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
+    context 'when :admin_user_filtered_nav feature flag is disabled' do
+      before do
+        stub_feature_flags(admin_user_filtered_nav: false)
+        visit admin_users_path
       end
 
-      context '`Pending approval` tab' do
-        before do
-          visit admin_users_path
-        end
-
-        it 'shows the `Pending approval` tab' do
+      describe 'tabs' do
+        it 'has multiple tabs to filter users' do
+          expect(page).to have_link('Active', href: admin_users_path)
+          expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins'))
+          expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled'))
+          expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
+          expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
+          expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
+          expect(page).to have_link('Banned', href: admin_users_path(filter: 'banned'))
           expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval'))
+          expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
+          expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
+          expect(page).to have_link('Trusted', href: admin_users_path(filter: 'trusted'))
         end
       end
     end
@@ -133,10 +131,7 @@
       end
 
       it 'searches with respect of sorting' do
-        visit admin_users_path(sort: 'name_asc')
-
-        fill_in :search_query, with: 'Foo'
-        click_button('Search users')
+        visit admin_users_path(sort: 'name_asc', search_query: 'Foo')
 
         expect(first_row.text).to include('Foo Bar')
         expect(second_row.text).to include('Foo Baz')
@@ -162,38 +157,21 @@
     end
 
     describe 'Two-factor Authentication filters' do
-      it 'counts users who have enabled 2FA' do
-        create(:user, :two_factor)
-
-        visit admin_users_path
-
-        page.within('.filter-two-factor-enabled .gl-tab-counter-badge') do
-          expect(page).to have_content('1')
-        end
-      end
-
       it 'filters by users who have enabled 2FA' do
-        user = create(:user, :two_factor)
-
-        visit admin_users_path
-        click_link '2FA Enabled'
-
-        expect(page).to have_content(user.email)
-      end
+        user_2fa = create(:user, :two_factor)
 
-      it 'counts users who have not enabled 2FA' do
-        visit admin_users_path
+        visit admin_users_path(filter: 'two_factor_enabled')
 
-        page.within('.filter-two-factor-disabled .gl-tab-counter-badge') do
-          expect(page).to have_content('2') # Including admin
-        end
+        expect(page).to have_content(user_2fa.email)
+        expect(all_users.length).to be(1)
       end
 
-      it 'filters by users who have not enabled 2FA' do
-        visit admin_users_path
-        click_link '2FA Disabled'
+      it 'filters users who have not enabled 2FA' do
+        visit admin_users_path(filter: 'two_factor_disabled')
 
         expect(page).to have_content(user.email)
+        expect(page).to have_content(admin.email)
+        expect(all_users.length).to be(2)
       end
     end
 
@@ -201,20 +179,18 @@
       it 'counts users who are pending approval' do
         create_list(:user, 2, :blocked_pending_approval)
 
-        visit admin_users_path
+        visit admin_users_path(filter: 'blocked_pending_approval')
 
-        page.within('.filter-blocked-pending-approval .gl-tab-counter-badge') do
-          expect(page).to have_content('2')
-        end
+        expect(all_users.length).to be(2)
       end
 
       it 'filters by users who are pending approval' do
-        user = create(:user, :blocked_pending_approval)
+        blocked_user = create(:user, :blocked_pending_approval)
 
-        visit admin_users_path
-        click_link 'Pending approval'
+        visit admin_users_path(filter: 'blocked_pending_approval')
 
-        expect(page).to have_content(user.email)
+        expect(page).to have_content(blocked_user.email)
+        expect(all_users.length).to be(1)
       end
     end
 
@@ -238,7 +214,7 @@
         expect(page).to have_content('Successfully blocked')
         expect(page).not_to have_content(user.email)
 
-        click_link 'Blocked'
+        visit admin_users_path(filter: 'blocked')
 
         wait_for_requests
 
@@ -276,7 +252,7 @@
         expect(page).to have_content('Successfully deactivated')
         expect(page).not_to have_content(user.email)
 
-        click_link 'Deactivated'
+        visit admin_users_path(filter: 'deactivated')
 
         wait_for_requests
 
@@ -314,11 +290,9 @@
 
     describe 'users pending approval' do
       it 'sends a welcome email and a password reset email to the user upon admin approval', :sidekiq_inline do
-        user = create(:user, :blocked_pending_approval, created_by_id: current_user.id)
-
-        visit admin_users_path
+        user = create(:user, :blocked_pending_approval, created_by_id: admin.id)
 
-        click_link 'Pending approval'
+        visit admin_users_path(filter: 'blocked_pending_approval')
 
         click_user_dropdown_toggle(user.id)
 
@@ -368,10 +342,8 @@
 
     context 'user group count', :js do
       before do
-        group = create(:group)
-        group.add_developer(current_user)
-        project = create(:project, group: create(:group))
-        project.add_reporter(current_user)
+        create(:group, developers: admin)
+        create(:project, group: create(:group), reporters: admin)
       end
 
       it 'displays count of the users authorized groups' do
@@ -379,7 +351,7 @@
 
         wait_for_requests
 
-        within_testid("user-group-count-#{current_user.id}") do
+        within_testid("user-group-count-#{admin.id}") do
           expect(page).to have_content('2')
         end
       end
@@ -389,8 +361,7 @@
       it 'shows user info', :aggregate_failures do
         user = create(:user, :blocked_pending_approval)
 
-        visit admin_users_path
-        click_link 'Pending approval'
+        visit admin_users_path(filter: 'blocked_pending_approval')
         click_link user.name
 
         expect(page).to have_content(user.name)
@@ -531,12 +502,10 @@ def expects_warning_to_be_shown
   end
 
   describe 'GET /admin/users/:id/projects' do
-    let_it_be(:group) { create(:group) }
+    let_it_be(:group) { create(:group, developers: user) }
     let_it_be(:project) { create(:project, group: group) }
 
     before do
-      group.add_developer(user)
-
       visit projects_admin_user_path(user)
     end
 
@@ -657,11 +626,15 @@ def check_breadcrumb(content)
   end
 
   def first_row
-    page.all('[role="row"]')[1]
+    all_users[0]
   end
 
   def second_row
-    page.all('[role="row"]')[2]
+    all_users[1]
+  end
+
+  def all_users
+    page.all('tbody[role="rowgroup"] > tr')
   end
 
   def sort_by(option)
diff --git a/spec/frontend/admin/users/components/admin_users_filter_app_spec.js b/spec/frontend/admin/users/components/admin_users_filter_app_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..1c4dd899564ffcfdb9544469929848d8f9072d45
--- /dev/null
+++ b/spec/frontend/admin/users/components/admin_users_filter_app_spec.js
@@ -0,0 +1,109 @@
+import { GlFilteredSearch } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { visitUrl, getBaseURL } from '~/lib/utils/url_utility';
+import AdminUsersFilterApp from '~/admin/users/components/admin_users_filter_app.vue';
+import { TOKENS } from '~/admin/users/constants';
+
+const mockToken = [
+  {
+    type: 'access_level',
+    value: { data: 'admins', operator: '=' },
+  },
+];
+
+const accessLevelToken = TOKENS.filter(({ type }) => type === 'access_level');
+
+jest.mock('~/lib/utils/url_utility', () => {
+  return {
+    ...jest.requireActual('~/lib/utils/url_utility'),
+    visitUrl: jest.fn(),
+  };
+});
+
+describe('AdminUsersFilterApp', () => {
+  let wrapper;
+
+  const createComponent = () => {
+    wrapper = shallowMount(AdminUsersFilterApp);
+  };
+
+  const findFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
+  const findAvailableTokens = () => findFilteredSearch().props('availableTokens');
+
+  it('includes all the tokens', () => {
+    createComponent();
+    const actualOptions = findAvailableTokens()
+      .flatMap(({ options }) => options.map(({ value }) => value))
+      .sort();
+    const expectedOptions = TOKENS.flatMap(({ options }) =>
+      options.map(({ value }) => value),
+    ).sort();
+
+    expect(actualOptions).toMatchObject(expectedOptions);
+  });
+
+  describe('when a token is selected', () => {
+    /**
+     * Currently BE support only one filter at the time
+     * https://gitlab.com/gitlab-org/gitlab/-/issues/254377
+     */
+    it('discard all other tokens', async () => {
+      createComponent();
+      findFilteredSearch().vm.$emit('input', mockToken);
+      await nextTick();
+
+      expect(findAvailableTokens()).toEqual(accessLevelToken);
+    });
+  });
+
+  describe('when a text token is selected', () => {
+    it('includes all the tokens', async () => {
+      createComponent();
+      findFilteredSearch().vm.$emit('input', [
+        {
+          type: 'filtered-search-term',
+          value: { data: 'mytext' },
+        },
+      ]);
+      await nextTick();
+
+      expect(findAvailableTokens()).toEqual(TOKENS);
+    });
+  });
+
+  describe('initialize tokens based on query search parameters', () => {
+    /**
+     * Currently BE support only one filter at the time
+     * https://gitlab.com/gitlab-org/gitlab/-/issues/254377
+     */
+    it('includes only one token if `filter` query parameter the TOKENS', () => {
+      window.history.replaceState({}, '', '/?filter=admins');
+      createComponent();
+
+      expect(findAvailableTokens()).toEqual(accessLevelToken);
+    });
+
+    it('replace the initial token when another token is selected', async () => {
+      window.history.replaceState({}, '', '/?filter=banned');
+      createComponent();
+      findFilteredSearch().vm.$emit('input', mockToken);
+      await nextTick();
+
+      expect(findAvailableTokens()).toEqual(accessLevelToken);
+    });
+  });
+
+  describe('when user submit a search', () => {
+    it('keeps `sort` and adds new `search_query` and `filter` query parameter and visit page', async () => {
+      window.history.replaceState({}, '', '/?filter=banned&sort=oldest_sign_in');
+      createComponent();
+      findFilteredSearch().vm.$emit('submit', [...mockToken, 'mytext']);
+      await nextTick();
+
+      expect(visitUrl).toHaveBeenCalledWith(
+        `${getBaseURL()}/?filter=admins&search_query=mytext&sort=oldest_sign_in`,
+      );
+    });
+  });
+});
diff --git a/spec/frontend/admin/users/utils_spec.js b/spec/frontend/admin/users/utils_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..77bc70476a5964b3d08fdc65caf3e3c9caee205d
--- /dev/null
+++ b/spec/frontend/admin/users/utils_spec.js
@@ -0,0 +1,43 @@
+import { TOKENS } from '~/admin/users/constants';
+import { initializeValuesFromQuery } from '~/admin/users/utils';
+
+const allFilters = TOKENS.flatMap(({ type, options, operators }) =>
+  options.map(({ value }) => ({ value, type, operator: operators[0].value })),
+);
+
+describe('initializeValuesFromQuery', () => {
+  it('parses `search_query` query parameter correctly', () => {
+    expect(initializeValuesFromQuery('?search_query=dummy')).toMatchObject({
+      tokens: ['dummy'],
+      sort: undefined,
+    });
+  });
+
+  it.each(allFilters)('parses `filter` query parameter `$value`', ({ value, type, operator }) => {
+    expect(initializeValuesFromQuery(`?search_query=dummy&filter=${value}`)).toMatchObject({
+      tokens: [{ type, value: { data: value, operator } }, 'dummy'],
+      sort: undefined,
+    });
+  });
+
+  it('parses `sort` query parameter correctly', () => {
+    expect(initializeValuesFromQuery('?sort=last_activity_on_asc')).toMatchObject({
+      tokens: [],
+      sort: 'last_activity_on_asc',
+    });
+  });
+
+  it('ignores `filter` query parameter not found in the TOKEN options', () => {
+    expect(initializeValuesFromQuery('?filter=unknown')).toMatchObject({
+      tokens: [],
+      sort: undefined,
+    });
+  });
+
+  it('ignores other query parameters other than `filter` and `search_query` and `sort`', () => {
+    expect(initializeValuesFromQuery('?other=value')).toMatchObject({
+      tokens: [],
+      sort: undefined,
+    });
+  });
+});