diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
index 6f98331111941f5a2dc5a9381989a5ec46da396e..6bd71ae519c2702e06cb6dd8f273d2063836b441 100644
--- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js
+++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
@@ -1,13 +1,4 @@
-import Vue from 'vue';
-import { __ } from '~/locale';
-import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests';
-import { createFilteredSearchTokenKeys } from '~/filtered_search/issuable_filtered_search_token_keys';
-import { FILTERED_SEARCH } from '~/filtered_search/constants';
-import initFilteredSearch from '~/pages/search/init_filtered_search';
-import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown';
-import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants';
-import searchUserProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_projects_with_merge_requests_enabled.query.graphql';
-import ActionDropdown from '~/merge_request_dashboard/components/action_dropdown.vue';
+import { initMergeRequestsDashboard } from './page';
 
 const el = document.getElementById('js-merge-request-dashboard');
 
@@ -18,41 +9,5 @@ if (el) {
     initMergeRequestDashboard(el);
   });
 } else {
-  const actionDropdownEl = document.querySelector('.js-action-dropdown');
-
-  if (actionDropdownEl) {
-    // eslint-disable-next-line no-new
-    new Vue({
-      el: actionDropdownEl,
-      provide: {
-        switchDashboardPath: actionDropdownEl.dataset.switchdashboardpath,
-        dashboardLinkText: __('Switch to new dashboard'),
-        experimentEnabled: false,
-      },
-      render(createElement) {
-        return createElement(ActionDropdown);
-      },
-    });
-  }
-
-  const IssuableFilteredSearchTokenKeys = createFilteredSearchTokenKeys({
-    disableReleaseFilter: true,
-  });
-
-  addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys, {
-    disableBranchFilter: true,
-    disableReleaseFilter: true,
-    disableEnvironmentFilter: true,
-  });
-
-  initFilteredSearch({
-    page: FILTERED_SEARCH.MERGE_REQUESTS,
-    filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
-    useDefaultState: true,
-  });
-
-  initNewResourceDropdown({
-    resourceType: RESOURCE_TYPE_MERGE_REQUEST,
-    query: searchUserProjectsWithMergeRequestsEnabled,
-  });
+  initMergeRequestsDashboard();
 }
diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/page.js b/app/assets/javascripts/pages/dashboard/merge_requests/page.js
new file mode 100644
index 0000000000000000000000000000000000000000..16da7cd99a5590ab59bfe2f5e5d08997a10ec4e2
--- /dev/null
+++ b/app/assets/javascripts/pages/dashboard/merge_requests/page.js
@@ -0,0 +1,50 @@
+import Vue from 'vue';
+import { __ } from '~/locale';
+import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests';
+import { createFilteredSearchTokenKeys } from '~/filtered_search/issuable_filtered_search_token_keys';
+import { FILTERED_SEARCH } from '~/filtered_search/constants';
+import initFilteredSearch from '~/pages/search/init_filtered_search';
+import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown';
+import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants';
+import searchUserProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_projects_with_merge_requests_enabled.query.graphql';
+import ActionDropdown from '~/merge_request_dashboard/components/action_dropdown.vue';
+
+export const initMergeRequestsDashboard = () => {
+  const actionDropdownEl = document.querySelector('.js-action-dropdown');
+
+  if (actionDropdownEl) {
+    // eslint-disable-next-line no-new
+    new Vue({
+      el: actionDropdownEl,
+      provide: {
+        switchDashboardPath: actionDropdownEl.dataset.switchdashboardpath,
+        dashboardLinkText: __('Switch to new dashboard'),
+        experimentEnabled: false,
+      },
+      render(createElement) {
+        return createElement(ActionDropdown);
+      },
+    });
+  }
+
+  const IssuableFilteredSearchTokenKeys = createFilteredSearchTokenKeys({
+    disableReleaseFilter: true,
+  });
+
+  addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys, {
+    disableBranchFilter: true,
+    disableReleaseFilter: true,
+    disableEnvironmentFilter: true,
+  });
+
+  initFilteredSearch({
+    page: FILTERED_SEARCH.MERGE_REQUESTS,
+    filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
+    useDefaultState: true,
+  });
+
+  initNewResourceDropdown({
+    resourceType: RESOURCE_TYPE_MERGE_REQUEST,
+    query: searchUserProjectsWithMergeRequestsEnabled,
+  });
+};
diff --git a/app/assets/javascripts/pages/dashboard/search_merge_requests/index.js b/app/assets/javascripts/pages/dashboard/search_merge_requests/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..ce3ebcd046bdc7979f1692d170248bf93446bd05
--- /dev/null
+++ b/app/assets/javascripts/pages/dashboard/search_merge_requests/index.js
@@ -0,0 +1,3 @@
+import { initMergeRequestsDashboard } from '../merge_requests/page';
+
+initMergeRequestsDashboard();
diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb
index 86ad87cc2c15c08b9c40989c9c20400b489af3d2..c6137da7859e56eebaea53018e90a0067292c7d4 100644
--- a/app/controllers/concerns/issuable_collections_action.rb
+++ b/app/controllers/concerns/issuable_collections_action.rb
@@ -6,7 +6,7 @@ module IssuableCollectionsAction
   include IssuesCalendar
 
   included do
-    before_action :check_search_rate_limit!, only: [:issues, :merge_requests], if: -> {
+    before_action :check_search_rate_limit!, only: [:issues, :merge_requests, :search_merge_requests], if: -> {
       params[:search].present?
     }
   end
@@ -24,17 +24,11 @@ def issues
       format.atom { render layout: 'xml' }
     end
   end
+  # rubocop:enable Gitlab/ModuleWithInstanceVariables
 
   def merge_requests
-    @merge_requests = issuables_collection.page(params[:page])
-
-    @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @merge_requests).data
-  rescue ActiveRecord::QueryCanceled => exception # rubocop:disable Database/RescueQueryCanceled
-    log_exception(exception)
-
-    @search_timeout_occurred = true
+    render_merge_requests
   end
-  # rubocop:enable Gitlab/ModuleWithInstanceVariables
 
   def issues_calendar
     render_issues_calendar(issuables_collection)
@@ -46,7 +40,7 @@ def sorting_field
     case action_name
     when 'issues'
       Issue::SORTING_PREFERENCE_FIELD
-    when 'merge_requests'
+    when 'merge_requests', 'search_merge_requests'
       MergeRequest::SORTING_PREFERENCE_FIELD
     end
   end
@@ -55,7 +49,7 @@ def finder_type
     case action_name
     when 'issues', 'issues_calendar'
       IssuesFinder
-    when 'merge_requests'
+    when 'merge_requests', 'search_merge_requests'
       MergeRequestsFinder
     end
   end
@@ -68,4 +62,16 @@ def finder_options
       issue_types: issue_types
     )
   end
+
+  # rubocop:disable Gitlab/ModuleWithInstanceVariables
+  def render_merge_requests
+    @merge_requests = issuables_collection.page(params[:page])
+
+    @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @merge_requests).data
+  rescue ActiveRecord::QueryCanceled => exception # rubocop:disable Database/RescueQueryCanceled
+    log_exception(exception)
+
+    @search_timeout_occurred = true
+  end
+  # rubocop:enable Gitlab/ModuleWithInstanceVariables
 end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 188a8540a58d0e446c21ffc589064de38ecb7f6b..613d05206cb7a24ba866d1b40045749a122dc16b 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -8,15 +8,15 @@ class DashboardController < Dashboard::ApplicationController
   prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) }
 
   before_action :event_filter, only: :activity
-  before_action :projects, only: [:issues, :merge_requests]
-  before_action :set_show_full_reference, only: [:issues, :merge_requests]
-  before_action :check_filters_presence!, only: [:issues, :merge_requests]
+  before_action :projects, only: [:issues, :merge_requests, :search_merge_requests]
+  before_action :set_show_full_reference, only: [:issues, :merge_requests, :search_merge_requests]
+  before_action :check_filters_presence!, only: [:issues, :merge_requests, :search_merge_requests]
 
   before_action only: :issues do
     push_frontend_feature_flag(:frontend_caching)
   end
 
-  before_action only: :merge_requests do
+  before_action only: [:merge_requests, :search_merge_requests] do
     push_frontend_feature_flag(:mr_approved_filter, type: :ops)
   end
 
@@ -24,9 +24,9 @@ class DashboardController < Dashboard::ApplicationController
 
   feature_category :user_profile, [:activity]
   feature_category :team_planning, [:issues, :issues_calendar]
-  feature_category :code_review_workflow, [:merge_requests]
+  feature_category :code_review_workflow, [:merge_requests, :search_merge_requests]
 
-  urgency :low, [:merge_requests, :activity]
+  urgency :low, [:merge_requests, :activity, :search_merge_requests]
   urgency :low, [:issues, :issues_calendar]
 
   def activity
@@ -40,6 +40,10 @@ def activity
     end
   end
 
+  def search_merge_requests
+    render_merge_requests
+  end
+
   protected
 
   def load_events
diff --git a/app/views/dashboard/search_merge_requests.html.haml b/app/views/dashboard/search_merge_requests.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..5717726d32cf89be64b0125abb7046d18b7f382b
--- /dev/null
+++ b/app/views/dashboard/search_merge_requests.html.haml
@@ -0,0 +1 @@
+= render template: 'dashboard/merge_requests'
diff --git a/config/routes/dashboard.rb b/config/routes/dashboard.rb
index c7db3a64408c40ed9b6f99d2f7e64e87e46cb49a..556a1618ee87f8b4b306b2e2f4434f6f32545c46 100644
--- a/config/routes/dashboard.rb
+++ b/config/routes/dashboard.rb
@@ -5,7 +5,7 @@
   get :issues
   get :merge_requests
   get :activity
-  get 'merge_requests/search', to: 'dashboard#merge_requests'
+  get 'merge_requests/search', to: 'dashboard#search_merge_requests'
 
   scope module: :dashboard do
     resources :milestones, only: [:index]
diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb
index 52f6f08e17af5b161bd33a6dd87bff2dbb2d16aa..32c574934662cde989bd131aeb4e8d599b224521 100644
--- a/spec/controllers/dashboard_controller_spec.rb
+++ b/spec/controllers/dashboard_controller_spec.rb
@@ -90,6 +90,68 @@
         end
       end
     end
+
+    describe 'GET merge requests search' do
+      it_behaves_like 'issuables requiring filter', :search_merge_requests
+
+      context 'when an ActiveRecord::QueryCanceled is raised' do
+        before do
+          allow_next_instance_of(Gitlab::IssuableMetadata) do |instance|
+            allow(instance).to receive(:data).and_raise(ActiveRecord::QueryCanceled)
+          end
+        end
+
+        it 'sets :search_timeout_occurred' do
+          get :search_merge_requests, params: { author_id: user.id }
+
+          expect(response).to have_gitlab_http_status(:ok)
+          expect(assigns(:search_timeout_occurred)).to eq(true)
+        end
+
+        context 'rendering views' do
+          render_views
+
+          it 'shows error message' do
+            get :search_merge_requests, params: { author_id: user.id }
+
+            expect(response.body).to have_content('Too many results to display. Edit your search or add a filter.')
+          end
+
+          it 'does not display MR counts in nav' do
+            get :search_merge_requests, params: { author_id: user.id }
+
+            expect(response.body).to have_content('Open Merged Closed All')
+            expect(response.body).not_to have_content('Open 0 Merged 0 Closed 0 All 0')
+          end
+        end
+
+        it 'logs the exception' do
+          expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original
+
+          get :search_merge_requests, params: { author_id: user.id }
+        end
+      end
+
+      context 'when an ActiveRecord::QueryCanceled is not raised' do
+        it 'does not set :search_timeout_occurred' do
+          get :search_merge_requests, params: { author_id: user.id }
+
+          expect(response).to have_gitlab_http_status(:ok)
+          expect(assigns(:search_timeout_occurred)).to eq(nil)
+        end
+
+        context 'rendering views' do
+          render_views
+
+          it 'displays MR counts in nav' do
+            get :search_merge_requests, params: { author_id: user.id }
+
+            expect(response.body).to have_content('Open 0 Merged 0 Closed 0 All 0')
+            expect(response.body).not_to have_content('Open Merged Closed All')
+          end
+        end
+      end
+    end
   end
 
   describe "GET activity as JSON" do
diff --git a/spec/requests/dashboard_controller_spec.rb b/spec/requests/dashboard_controller_spec.rb
index d7f01b8a7ab7b4ffbb7b460fa13cee5ed44afb81..8377caa58d68580b441eb570653bad8c6294af62 100644
--- a/spec/requests/dashboard_controller_spec.rb
+++ b/spec/requests/dashboard_controller_spec.rb
@@ -40,4 +40,18 @@ def request
       end
     end
   end
+
+  context 'search merge requests dashboard' do
+    it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do
+      let_it_be(:current_user) { create(:user) }
+
+      before do
+        sign_in current_user
+      end
+
+      def request
+        get merge_requests_search_dashboard_path, params: { scope: 'all', search: 'test' }
+      end
+    end
+  end
 end