diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index fc25122aedc8242f9d3eb3159e52c215565bfa9a..ef5785b55320217ceacf9eda8922df3bbcea0cd8 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -36,6 +36,7 @@
 /* global Shortcuts */
 
 import GroupsList from './groups_list';
+import ProjectsList from './projects_list';
 
 const ShortcutsBlob = require('./shortcuts_blob');
 const UserCallout = require('./user_callout');
@@ -98,6 +99,14 @@ const UserCallout = require('./user_callout');
         case 'dashboard:todos:index':
           new gl.Todos();
           break;
+        case 'dashboard:projects:index':
+        case 'dashboard:projects:starred':
+        case 'explore:projects:index':
+        case 'explore:projects:trending':
+        case 'explore:projects:starred':
+        case 'admin:projects:index':
+          new ProjectsList();
+          break;
         case 'dashboard:groups:index':
         case 'explore:groups:index':
           new GroupsList();
@@ -163,9 +172,6 @@ const UserCallout = require('./user_callout');
         case 'dashboard:activity':
           new gl.Activities();
           break;
-        case 'dashboard:projects:starred':
-          new gl.Activities();
-          break;
         case 'projects:commit:show':
           new Commit();
           new gl.Diff();
@@ -208,6 +214,7 @@ const UserCallout = require('./user_callout');
           shortcut_handler = new ShortcutsNavigation();
           new NotificationsForm();
           new NotificationsDropdown();
+          new ProjectsList();
           break;
         case 'groups:group_members:index':
           new gl.MemberExpirationDate();
diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js
new file mode 100644
index 0000000000000000000000000000000000000000..f498c3ea9739cc9fb3206396b97729e75ea50928
--- /dev/null
+++ b/app/assets/javascripts/filterable_list.js
@@ -0,0 +1,47 @@
+/**
+ * Makes search request for content when user types a value in the search input.
+ * Updates the html content of the page with the received one.
+ */
+export default class FilterableList {
+  constructor(form, filter, holder) {
+    this.filterForm = form;
+    this.listFilterElement = filter;
+    this.listHolderElement = holder;
+
+    this.initSearch();
+  }
+
+  initSearch() {
+    this.debounceFilter = _.debounce(this.filterResults.bind(this), 500);
+
+    this.listFilterElement.removeEventListener('input', this.debounceFilter);
+    this.listFilterElement.addEventListener('input', this.debounceFilter);
+  }
+
+  filterResults() {
+    const form = this.filterForm;
+    const filterUrl = `${form.getAttribute('action')}?${$(form).serialize()}`;
+
+    $(this.listHolderElement).fadeTo(250, 0.5);
+
+    return $.ajax({
+      url: form.getAttribute('action'),
+      data: $(form).serialize(),
+      type: 'GET',
+      dataType: 'json',
+      context: this,
+      complete() {
+        $(this.listHolderElement).fadeTo(250, 1);
+      },
+      success(data) {
+        this.listHolderElement.innerHTML = data.html;
+
+       // Change url so if user reload a page - search results are saved
+        return window.history.replaceState({
+          page: filterUrl,
+
+        }, document.title, filterUrl);
+      },
+    });
+  }
+}
diff --git a/app/assets/javascripts/groups_list.js b/app/assets/javascripts/groups_list.js
index 0ef81e494449ed0ff4aba5dcc710451e0c06248b..49b29affaa58626fe290964a31632242fb1e8786 100644
--- a/app/assets/javascripts/groups_list.js
+++ b/app/assets/javascripts/groups_list.js
@@ -1,47 +1,15 @@
+import FilterableList from './filterable_list';
+
 /**
- * Based on project list search.
  * Makes search request for groups when user types a value in the search input.
  * Updates the html content of the page with the received one.
  */
 export default class GroupsList {
   constructor() {
-    this.groupsListFilterElement = document.querySelector('.js-groups-list-filter');
-    this.groupsListHolderElement = document.querySelector('.js-groups-list-holder');
-
-    this.initSearch();
-  }
-
-  initSearch() {
-    this.debounceFilter = _.debounce(this.filterResults.bind(this), 500);
-
-    this.groupsListFilterElement.removeEventListener('input', this.debounceFilter);
-    this.groupsListFilterElement.addEventListener('input', this.debounceFilter);
-  }
-
-  filterResults() {
-    const form = document.querySelector('form#group-filter-form');
-    const groupFilterUrl = `${form.getAttribute('action')}?${$(form).serialize()}`;
-
-    $(this.groupsListHolderElement).fadeTo(250, 0.5);
-
-    return $.ajax({
-      url: form.getAttribute('action'),
-      data: $(form).serialize(),
-      type: 'GET',
-      dataType: 'json',
-      context: this,
-      complete() {
-        $(this.groupsListHolderElement).fadeTo(250, 1);
-      },
-      success(data) {
-        this.groupsListHolderElement.innerHTML = data.html;
-
-       // Change url so if user reload a page - search results are saved
-        return window.history.replaceState({
-          page: groupFilterUrl,
+    var form = document.querySelector('form#group-filter-form');
+    var filter = document.querySelector('.js-groups-list-filter');
+    var holder = document.querySelector('.js-groups-list-holder');
 
-        }, document.title, groupFilterUrl);
-      },
-    });
+    new FilterableList(form, filter, holder);
   }
 }
diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js
index acdf9b7eb5a5b9f201a7eae6e3a0878d30c61752..383c28154574198a641aeaa3f3f710c0f9b73a67 100644
--- a/app/assets/javascripts/projects_list.js
+++ b/app/assets/javascripts/projects_list.js
@@ -1,50 +1,15 @@
-/* eslint-disable func-names, space-before-function-paren, object-shorthand, quotes, no-var, one-var, one-var-declaration-per-line, prefer-arrow-callback, consistent-return, no-unused-vars, camelcase, prefer-template, comma-dangle, max-len */
+import FilterableList from './filterable_list';
 
-(function() {
-  window.ProjectsList = {
-    init: function() {
-      $(".projects-list-filter").off('keyup');
-      this.initSearch();
-      return this.initPagination();
-    },
-    initSearch: function() {
-      var debounceFilter, projectsListFilter;
-      projectsListFilter = $('.projects-list-filter');
-      debounceFilter = _.debounce(window.ProjectsList.filterResults, 500);
-      return projectsListFilter.on('keyup', function(e) {
-        if (projectsListFilter.val() !== '') {
-          return debounceFilter();
-        }
-      });
-    },
-    filterResults: function() {
-      var form, project_filter_url, search;
-      $('.projects-list-holder').fadeTo(250, 0.5);
-      form = null;
-      form = $("form#project-filter-form");
-      search = $(".projects-list-filter").val();
-      project_filter_url = form.attr('action') + '?' + form.serialize();
-      return $.ajax({
-        type: "GET",
-        url: form.attr('action'),
-        data: form.serialize(),
-        complete: function() {
-          return $('.projects-list-holder').fadeTo(250, 1);
-        },
-        success: function(data) {
-          $('.projects-list-holder').replaceWith(data.html);
-          return history.replaceState({
-            page: project_filter_url
-          // Change url so if user reload a page - search results are saved
-          }, document.title, project_filter_url);
-        },
-        dataType: "json"
-      });
-    },
-    initPagination: function() {
-      return $('.projects-list-holder .pagination').on('ajax:success', function(e, data) {
-        return $('.projects-list-holder').replaceWith(data.html);
-      });
-    }
-  };
-}).call(window);
+/**
+ * Makes search request for projects when user types a value in the search input.
+ * Updates the html content of the page with the received one.
+ */
+export default class ProjectsList {
+  constructor() {
+    var form = document.querySelector('form#project-filter-form');
+    var filter = document.querySelector('.js-projects-list-filter');
+    var holder = document.querySelector('.js-projects-list-holder');
+
+    new FilterableList(form, filter, holder);
+  }
+}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 88ea92c5afb1e7e7d8184d984426cc3cb5a1cdfd..543d2ece3df54763f5082ee31071d650e1eab8d7 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -182,7 +182,8 @@ input[type="checkbox"]:hover {
     display: flex;
   }
 
-  .search-field-holder {
+  .search-field-holder,
+  .project-filter-form {
     -webkit-flex: 1 0 auto;
     flex: 1 0 auto;
     position: relative;
@@ -201,7 +202,8 @@ input[type="checkbox"]:hover {
     pointer-events: none;
   }
 
-  .search-text-input {
+  .search-text-input,
+  .project-filter-form-field {
     padding-left: $gl-padding + 15px;
     padding-right: $gl-padding + 15px;
   }
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 39c8c6d8a0c0c216fbc2bf776dd03f1debdd4642..daecfc832bf7d82ddbfec3fe3972577881f73c55 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -14,6 +14,15 @@ class Admin::ProjectsController < Admin::ApplicationController
     @projects = @projects.search(params[:name]) if params[:name].present?
     @projects = @projects.sort(@sort = params[:sort])
     @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
+
+    respond_to do |format|
+      format.html
+      format.json do
+        render json: {
+          html: view_to_html_string("admin/projects/_projects", locals: { projects: @projects })
+        }
+      end
+    end
   end
 
   def show
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
index bbcb52f7eafc9abb78bc326f12bc1b682d00d60e..cb9bb33f4923f10a1c64e2e58ffe95dbf911da38 100644
--- a/app/helpers/explore_helper.rb
+++ b/app/helpers/explore_helper.rb
@@ -1,11 +1,16 @@
 module ExploreHelper
   def filter_projects_path(options = {})
     exist_opts = {
-      sort: params[:sort],
+      sort: params[:sort] || @sort,
       scope: params[:scope],
       group: params[:group],
       tag: params[:tag],
       visibility_level: params[:visibility_level],
+      name: params[:name],
+      personal: params[:personal],
+      archived: params[:archived],
+      shared: params[:shared],
+      namespace_id: params[:namespace_id],
     }
 
     options = exist_opts.merge(options)
diff --git a/app/views/admin/projects/_projects.html.haml b/app/views/admin/projects/_projects.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..c1a9f8d6ddd49d8c19d5d745a972f9f9a934263b
--- /dev/null
+++ b/app/views/admin/projects/_projects.html.haml
@@ -0,0 +1,32 @@
+.js-projects-list-holder
+  - if @projects.any?
+    %ul.projects-list.content-list
+      - @projects.each_with_index do |project|
+        %li.project-row
+          .controls
+            - if project.archived
+              %span.label.label-warning archived
+            %span.badge
+              = storage_counter(project.statistics.storage_size)
+            = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn"
+            = link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove"
+          .title
+            = link_to [:admin, project.namespace.becomes(Namespace), project] do
+              .dash-project-avatar
+                .avatar-container.s40
+                  = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+              %span.project-full-name
+                %span.namespace-name
+                  - if project.namespace
+                    = project.namespace.human_name
+                    \/
+                %span.project-name.filter-title
+                  = project.name
+
+          - if project.description.present?
+            .description
+              = markdown_field(project, :description)
+
+    = paginate @projects, theme: 'gitlab'
+  - else
+    .nothing-here-block No projects found
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 121662a2ea6424403ce1bb59372785ea5aab767b..3301f55b8a8b89891dc55ba3ba84c0d2089eb55e 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -7,33 +7,24 @@
 %div{ class: container_class }
   .top-area
     .prepend-top-default
-      = form_tag admin_projects_path, method: :get do |f|
-        = render "shared/projects/filter_fields"
-        .search-holder
-          .search-field-holder
-            = search_field_tag :name, params[:name], class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false, placeholder: 'Search by name'
-
-            - if params[:visibility_level].present?
-              = hidden_field_tag 'visibility_level', params[:visibility_level]
-
-            = icon("search", class: "search-icon")
-
-          .dropdown
-            - toggle_text = 'Namespace'
-            - if params[:namespace_id].present?
-              = hidden_field_tag :namespace_id, params[:namespace_id]
-              - namespace = Namespace.find(params[:namespace_id])
-              - toggle_text = "#{namespace.kind}: #{namespace.full_path}"
-            = dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { toggle_class: 'js-namespace-select large' })
-            .dropdown-menu.dropdown-select.dropdown-menu-align-right
-              = dropdown_title('Namespaces')
-              = dropdown_filter("Search for Namespace")
-              = dropdown_content
-              = dropdown_loading
-          = render 'shared/projects/dropdown'
-          = link_to new_project_path, class: 'btn btn-new' do
-            New Project
-          = button_tag "Search", class: "btn btn-primary btn-search hide"
+      .search-holder
+        = render 'shared/projects/search_form', autofocus: true, icon: true
+        .dropdown
+          - toggle_text = 'Namespace'
+          - if params[:namespace_id].present?
+            = hidden_field_tag :namespace_id, params[:namespace_id]
+            - namespace = Namespace.find(params[:namespace_id])
+            - toggle_text = "#{namespace.kind}: #{namespace.full_path}"
+          = dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { toggle_class: 'js-namespace-select large' })
+          .dropdown-menu.dropdown-select.dropdown-menu-align-right
+            = dropdown_title('Namespaces')
+            = dropdown_filter("Search for Namespace")
+            = dropdown_content
+            = dropdown_loading
+        = render 'shared/projects/dropdown'
+        = link_to new_project_path, class: 'btn btn-new' do
+          New Project
+        = button_tag "Search", class: "btn btn-primary btn-search hide"
 
     %ul.nav-links
       - opts = params[:visibility_level].present? ? {} : { page: admin_projects_path }
@@ -51,35 +42,4 @@
         = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do
           Public
 
-  .projects-list-holder
-    - if @projects.any?
-      %ul.projects-list.content-list
-        - @projects.each_with_index do |project|
-          %li.project-row
-            .controls
-              - if project.archived
-                %span.label.label-warning archived
-              %span.badge
-                = storage_counter(project.statistics.storage_size)
-              = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn"
-              = link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove"
-            .title
-              = link_to [:admin, project.namespace.becomes(Namespace), project] do
-                .dash-project-avatar
-                  .avatar-container.s40
-                    = project_icon(project, alt: '', class: 'avatar project-avatar s40')
-                %span.project-full-name
-                  %span.namespace-name
-                    - if project.namespace
-                      = project.namespace.human_name
-                      \/
-                  %span.project-name.filter-title
-                    = project.name
-
-            - if project.description.present?
-              .description
-                = markdown_field(project, :description)
-
-      = paginate @projects, theme: 'gitlab'
-    - else
-      .nothing-here-block No projects found
+  = render 'projects'
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index c6d5937a3c3367495d45eebbffba9868c3674281..13eaba41f4cefb014548e446c4330b94c421e7c3 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -7,8 +7,7 @@
       = link_to explore_groups_path, title: 'Explore groups' do
         Explore Groups
   .nav-controls
-    = form_tag request.path, method: :get, class: 'group-filter-form', id: 'group-filter-form' do |f|
-      = search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name...', class: 'group-filter-form-field form-control input-short js-groups-list-filter', spellcheck: false, id: 'group-filter-form-field', tabindex: "2"
+    = render 'shared/groups/search_form'
     = render 'shared/groups/dropdown'
     - if current_user.can_create_group?
       = link_to new_group_path, class: "btn btn-new" do
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index 795ad9f33c1440435d52bbbf33482207755e6437..600ee63a5c09f31624cc68c7ee889350e7791cb0 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -13,9 +13,7 @@
         Explore projects
 
   .nav-controls
-    = form_tag request.fullpath, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
-      = render "shared/projects/filter_fields"
-      = search_field_tag :name, params[:name], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2"
+    = render 'shared/projects/search_form'
     = render 'shared/projects/dropdown'
     - if current_user.can_create_project?
       = link_to new_project_path, class: 'btn btn-new' do
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 4b095f5264361c3f2d71bf71bee4537e251b7e8b..eef794dbd51540cc144f1582ea85faa97926748f 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -5,7 +5,6 @@
 - header_title  "Projects", dashboard_projects_path
 
 .user-callout{ 'callout-svg' => custom_icon('icon_customization') }
-
 - if @projects.any? || params[:name]
   = render 'dashboard/projects_head'
 
diff --git a/app/views/explore/groups/_nav.html.haml b/app/views/explore/groups/_nav.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..c8d95b52156333b47c90bc5c268bd2c9ed27ec9c
--- /dev/null
+++ b/app/views/explore/groups/_nav.html.haml
@@ -0,0 +1,8 @@
+.top-area
+  %ul.nav-links
+    = nav_link(page: explore_groups_path) do
+      = link_to explore_groups_path do
+        Explore Groups
+  .nav-controls
+    = render 'shared/groups/search_form'
+    = render 'shared/groups/dropdown'
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 7f1bacc91cb9ad2f711739f078b651d469706e80..8374f5a009fd3fe9f719c3f3e8ef18d694ad0776 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -5,7 +5,7 @@
   = render 'dashboard/groups_head'
 - else
   = render 'explore/head'
-
+  = render 'nav'
 
 - if @groups.present?
   = render 'groups'
diff --git a/app/views/explore/projects/_nav.html.haml b/app/views/explore/projects/_nav.html.haml
index 614b5431779351042cf94fbe407de1c689206aef..e0a2a1e9c96daba3af2fbce1ede9a9585bb8d7b5 100644
--- a/app/views/explore/projects/_nav.html.haml
+++ b/app/views/explore/projects/_nav.html.haml
@@ -1,10 +1,17 @@
-%ul.nav-links
-  = nav_link(page: [trending_explore_projects_path, explore_root_path]) do
-    = link_to trending_explore_projects_path do
-      Trending
-  = nav_link(page: starred_explore_projects_path) do
-    = link_to starred_explore_projects_path do
-      Most stars
-  = nav_link(page: explore_projects_path) do
-    = link_to explore_projects_path do
-      All
+.top-area
+  %ul.nav-links
+    = nav_link(page: [trending_explore_projects_path, explore_root_path]) do
+      = link_to trending_explore_projects_path do
+        Trending
+    = nav_link(page: starred_explore_projects_path) do
+      = link_to starred_explore_projects_path do
+        Most stars
+    = nav_link(page: explore_projects_path) do
+      = link_to explore_projects_path do
+        All
+
+  .nav-controls
+    - unless current_user
+      = render 'shared/projects/search_form'
+      = render 'shared/projects/dropdown'
+    = render 'filter'
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index 42b50481b9d3d59f26680024e332d7866f5706ec..ec4617551030505ad0bce37237e5d9b70d4102f2 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -6,10 +6,5 @@
 - else
   = render 'explore/head'
 
-.top-area
-  = render 'explore/projects/nav'
-
-  .nav-controls
-    = render 'filter'
-
+= render 'explore/projects/nav'
 = render 'projects', projects: @projects
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 5a7b0c1534bba5e7ade32e16e3e934fc618bd388..18997baa998908e5cf484b66e978706102f16d1c 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -11,9 +11,7 @@
   .top-area
     = render 'groups/show_nav'
     .nav-controls
-      = form_tag request.fullpath, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
-        = render "shared/projects/filter_fields"
-        = search_field_tag :name, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
+      = render 'shared/projects/search_form'
       = render 'shared/projects/dropdown'
       - if can? current_user, :create_projects, @group
         = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do
diff --git a/app/views/shared/groups/_search_form.html.haml b/app/views/shared/groups/_search_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..ad7a7faedf1d0443a69d03ae3fbcf6916f3692ea
--- /dev/null
+++ b/app/views/shared/groups/_search_form.html.haml
@@ -0,0 +1,2 @@
+= form_tag request.path, method: :get, class: 'group-filter-form', id: 'group-filter-form' do |f|
+  = search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name...', class: 'group-filter-form-field form-control input-short js-groups-list-filter', spellcheck: false, id: 'group-filter-form-field', tabindex: "2"
diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml
index 7e5c00e560811a439b71564b092fa94661f9deac..2d25b8aad62c69ee32a9223eb44c2e3eae09b2aa 100644
--- a/app/views/shared/projects/_dropdown.html.haml
+++ b/app/views/shared/projects/_dropdown.html.haml
@@ -1,9 +1,4 @@
 - @sort ||= sort_value_recently_updated
-- name = params[:name]
-- personal = params[:personal]
-- archived = params[:archived]
-- shared = params[:shared]
-- namespace_id = params[:namespace_id]
 .dropdown
   - toggle_text = projects_sort_options_hash[@sort]
   = dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { id: 'sort-projects-dropdown' })
@@ -12,32 +7,32 @@
       Sort by
     - projects_sort_options_hash.each do |value, title|
       %li
-        = link_to filter_projects_path(namespace_id: namespace_id, sort: value, archived: archived, personal: personal, name: name), class: ("is-active" if @sort == value) do
+        = link_to filter_projects_path(sort: value), class: ("is-active" if @sort == value) do
           = title
 
     %li.divider
     %li
-      = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: nil, name: name), class: ("is-active" unless params[:archived].present?) do
+      = link_to filter_projects_path(archived: nil), class: ("is-active" unless params[:archived].present?) do
         Hide archived projects
     %li
-      = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: true, name: name), class: ("is-active" if params[:archived].present?) do
+      = link_to filter_projects_path(archived: true), class: ("is-active" if params[:archived].present?) do
         Show archived projects
     - if current_user
       %li.divider
       %li
-        = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: nil, name: name), class: ("is-active" unless personal.present?) do
+        = link_to filter_projects_path(personal: nil), class: ("is-active" unless params[:personal].present?) do
           Owned by anyone
       %li
-        = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: true, name: name), class: ("is-active" if personal.present?) do
+        = link_to filter_projects_path(personal: true), class: ("is-active" if params[:personal].present?) do
           Owned by me
       - if @group && @group.shared_projects.present?
         %li.divider
         %li
-          = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: nil, name: name), class: ("is-active" unless shared.present?) do
+          = link_to filter_projects_path(shared: nil), class: ("is-active" unless params[:shared].present?) do
             All projects
         %li
-          = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 0, name: name), class: ("is-active" if shared == '0') do
+          = link_to filter_projects_path(shared: 0), class: ("is-active" if params[:shared] == '0') do
             Hide shared projects
         %li
-          = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 1, name: name), class: ("is-active" if shared == '1') do
+          = link_to filter_projects_path(shared: 1), class: ("is-active" if params[:shared] == '1') do
             Hide group projects
diff --git a/app/views/shared/projects/_filter_fields.html.haml b/app/views/shared/projects/_filter_fields.html.haml
deleted file mode 100644
index 26362ad1eb7dc1b2e35a0e828d63726b0a6e1896..0000000000000000000000000000000000000000
--- a/app/views/shared/projects/_filter_fields.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-- if params[:sort].present?
-  = hidden_field_tag :sort, params[:sort]
-
-- if params[:personal].present?
-  = hidden_field_tag :personal, params[:personal]
-
-- if params[:archived].present?
-  = hidden_field_tag :archived, params[:archived]
-
-- if params[:visibility_level].present?
-  = hidden_field_tag :visibility_level, params[:visibility_level]
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 3a9dd37dc7d8a53c4b875d7db7658c71f7a48523..c57282c57424b05ccbfaa48f4d49cb8a225a785c 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -8,7 +8,7 @@
 - show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true
 - remote = false unless local_assigns[:remote] == true
 
-.projects-list-holder
+.js-projects-list-holder
   - if projects.any?
     %ul.projects-list.content-list
       - projects.each_with_index do |project, i|
@@ -25,6 +25,3 @@
     = paginate(projects, remote: remote, theme: "gitlab") if projects.respond_to? :total_pages
   - else
     .nothing-here-block No projects found
-
-:javascript
-  ProjectsList.init();
diff --git a/app/views/shared/projects/_search_form.html.haml b/app/views/shared/projects/_search_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..b89194bcc677c0b6bc5bfcfbbc8c73ce63e6804d
--- /dev/null
+++ b/app/views/shared/projects/_search_form.html.haml
@@ -0,0 +1,23 @@
+= form_tag filter_projects_path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
+  = search_field_tag :name, params[:name],
+    placeholder: 'Filter by name...',
+    class: 'project-filter-form-field form-control input-short js-projects-list-filter',
+    spellcheck: false,
+    id: 'project-filter-form-field',
+    tabindex: "2",
+    autofocus: local_assigns[:autofocus]
+
+  - if local_assigns[:icon]
+    = icon("search", class: "search-icon")
+
+  - if params[:sort].present?
+    = hidden_field_tag :sort, params[:sort]
+
+  - if params[:personal].present?
+    = hidden_field_tag :personal, params[:personal]
+
+  - if params[:archived].present?
+    = hidden_field_tag :archived, params[:archived]
+
+  - if params[:visibility_level].present?
+    = hidden_field_tag :visibility_level, params[:visibility_level]
diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb
index 9a6c04fba7ad4740ee718b1d42686df861da4d13..79db9728227699925df29b7265b93a072591dc96 100644
--- a/features/steps/project/fork.rb
+++ b/features/steps/project/fork.rb
@@ -56,7 +56,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
   end
 
   step 'I should see my fork on the list' do
-    page.within('.projects-list-holder') do
+    page.within('.js-projects-list-holder') do
       project = @user.fork_of(@project)
       expect(page).to have_content("#{project.namespace.human_name} / #{project.name}")
     end