diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 2a8a1f05b352ff274568dd3d40ffa9eb05f84827..2a7bf0bc3061cf850fd70a33c6bbce6092c02cb4 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -58,7 +58,7 @@ class GitLabDropdownFilter
   filter: (search_text) ->
     data = @options.data()
 
-    if data?
+    if data? and not @options.filterByText
       results = data
 
       if search_text isnt ''
@@ -102,10 +102,11 @@ class GitLabDropdownFilter
           $el = $(@)
           matches = fuzzaldrinPlus.match($el.text().trim(), search_text)
 
-          if matches.length
-            $el.show()
-          else
-            $el.hide()
+          unless $el.is('.dropdown-header')
+            if matches.length
+              $el.show()
+            else
+              $el.hide()
       else
         elements.show()
 
@@ -191,6 +192,7 @@ class GitLabDropdown
     if @options.filterable
       @filter = new GitLabDropdownFilter @filterInput,
         filterInputBlur: @filterInputBlur
+        filterByText: @options.filterByText
         remote: @options.filterRemote
         query: @options.data
         keys: searchFields
diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee
index d12bad97a0517ebbf23b35cb2180a3651b76e45a..54c539d5f9b0ec259fcd0eacfb84d8372e8d6f12 100644
--- a/app/assets/javascripts/project.js.coffee
+++ b/app/assets/javascripts/project.js.coffee
@@ -19,6 +19,7 @@ class @Project
       $('.clone').text(url)
 
     # Ref switcher
+    @initRefSwitcher()
     $('.project-refs-select').on 'change', ->
       $(@).parents('form').submit()
 
@@ -50,3 +51,39 @@ class @Project
 
   changeProject: (url) ->
     window.location = url
+
+  initRefSwitcher: ->
+    $('.js-project-refs-dropdown').each ->
+      $dropdown = $(@)
+      selected = $dropdown.data('selected')
+
+      $dropdown.glDropdown(
+        data: (term, callback) ->
+          $.ajax(
+            url: $dropdown.data('refs-url')
+            data:
+              ref: $dropdown.data('ref')
+          ).done (refs) ->
+            callback(refs)
+        selectable: true
+        filterable: true
+        filterByText: true
+        fieldName: 'ref'
+        renderRow: (ref) ->
+          if ref.header?
+            "<li class='dropdown-header'>#{ref.header}</li>"
+          else
+            isActiveClass = if ref is selected then 'is-active' else ''
+
+            "<li>
+              <a href='#' data-ref='#{escape(ref)}' class='#{isActiveClass}'>
+                #{ref}
+              </a>
+            </li>"
+        id: (obj, $el) ->
+          $el.data('ref')
+        toggleLabel: (obj, $el) ->
+          $el.text().trim()
+        clicked: (e) ->
+          $dropdown.closest('form').submit()
+      )
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index ae7bdf14c40165fc8e21e9e95f4d2b64ef6a0dbb..874416e10074f779020fa1dcc1651c3dcbec6551 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -9,6 +9,10 @@
       margin-top: -2px;
       float: right;
     }
+
+    .dropdown-menu-toggle {
+      line-height: 20px;
+    }
   }
 
   .panel-body {
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index f242706ebe45ed4a0fa088bc242bc0067604a972..21d87cc9d341d6775b00be8e3299c811b50fd728 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -165,11 +165,6 @@
   background-size: 16px 16px !important;
 }
 
-/** Branch/tag selector **/
-.project-refs-form .select2-container {
-  width: 160px !important;
-}
-
 .select2-results .select2-no-results,
 .select2-results .select2-searching,
 .select2-results .select2-ajax-error,
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index f138a2f53878df1a80bcb81182c6edee457a028f..093d5e18516ca2cadfeaec4f4b2070a938f73f99 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -616,3 +616,9 @@ pre.light-well {
     color: $gl-text-green;
   }
 }
+
+.project-refs-form {
+  .dropdown-menu {
+    width: 300px;
+  }
+}
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 8044c6378256f4fd1a73839fa60f4b1c91b86ec1..78ceaf3237fd524d0a16572d4fc3f670e75d5082 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -1,7 +1,7 @@
 class ProjectsController < Projects::ApplicationController
   include ExtractsPath
 
-  before_action :authenticate_user!, except: [:show, :activity]
+  before_action :authenticate_user!, except: [:show, :activity, :refs]
   before_action :project, except: [:new, :create]
   before_action :repository, except: [:new, :create]
   before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
@@ -251,6 +251,24 @@ class ProjectsController < Projects::ApplicationController
     }
   end
 
+  def refs
+    options = {
+      'Branches' => @repository.branch_names,
+    }
+
+    unless @repository.tag_count.zero?
+      options['Tags'] = VersionSorter.rsort(@repository.tag_names)
+    end
+
+    # If reference is commit id - we should add it to branch/tag selectbox
+    ref = Addressable::URI.unescape(params[:ref])
+    if ref && options.flatten(2).exclude?(ref) && ref =~ /\A[0-9a-zA-Z]{6,52}\z/
+      options['Commits'] = [ref]
+    end
+
+    render json: options.to_json
+  end
+
   private
 
   def determine_layout
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 439b015b3b827462b86407e9422c7b7727b26414..82421d74de96be0ac03ad95029876602ddcc43ab 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -101,22 +101,6 @@ module ApplicationHelper
     'Never'
   end
 
-  def grouped_options_refs
-    repository = @project.repository
-
-    options = [
-      ['Branches', repository.branch_names],
-      ['Tags', VersionSorter.rsort(repository.tag_names)]
-    ]
-
-    # If reference is commit id - we should add it to branch/tag selectbox
-    if @ref && !options.flatten.include?(@ref) && @ref =~ /\A[0-9a-zA-Z]{6,52}\z/
-      options << ['Commit', [@ref]]
-    end
-
-    grouped_options_for_select(options, @ref || @project.default_branch)
-  end
-
   # Define whenever show last push event
   # with suggestion to create MR
   def show_last_push_widget?(event)
diff --git a/app/views/projects/badges/index.html.haml b/app/views/projects/badges/index.html.haml
index ee63bc55a303f622cfeaa633a9d1be94f005c56e..ac80951dd4fdb56c38393b8f8ca95a92f1c9dd7c 100644
--- a/app/views/projects/badges/index.html.haml
+++ b/app/views/projects/badges/index.html.haml
@@ -7,7 +7,7 @@
       %b Builds badge &middot;
       = @build_badge.to_html
       .pull-right
-        = render 'shared/ref_switcher', destination: 'badges'
+        = render 'shared/ref_switcher', destination: 'badges', align_right: true
     .panel-body
       .row
         .col-md-2.text-center
diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml
index eb2e1919e190541aae8b412ccab832b25dbfe79e..ea7162d4d63a54f1818ade14e298ff3fc9e85857 100644
--- a/app/views/shared/_ref_switcher.html.haml
+++ b/app/views/shared/_ref_switcher.html.haml
@@ -1,7 +1,14 @@
+- dropdown_toggle_text = @ref || @project.default_branch
 = form_tag switch_namespace_project_refs_path(@project.namespace, @project), method: :get, class: "project-refs-form" do
-  = select_tag "ref", grouped_options_refs, class: "project-refs-select select2 select2-sm"
   = hidden_field_tag :destination, destination
   - if defined?(path)
     = hidden_field_tag :path, path
   - @options && @options.each do |key, value|
     = hidden_field_tag key, value, id: nil
+  .dropdown
+    = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_namespace_project_path(@project.namespace, @project) }, { toggle_class: "js-project-refs-dropdown" }
+    .dropdown-menu.dropdown-menu-selectable{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) }
+      = dropdown_title "Switch branch/tag"
+      = dropdown_filter "Search branches and tags"
+      = dropdown_content
+      = dropdown_loading
diff --git a/config/routes.rb b/config/routes.rb
index de6094fa0edfdada440fd54816fe0fa2c33b68c2..a22559ebabca77dfc1dd5a0384eeea1bcae1c5d6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -479,6 +479,7 @@ Rails.application.routes.draw do
         get :download_export
         get :autocomplete_sources
         get :activity
+        get :refs
       end
 
       scope module: :projects do
diff --git a/features/steps/project/network_graph.rb b/features/steps/project/network_graph.rb
index 9b59b682676ad6da8114da2c01a60be4aff2fce9..019b3124a863d5b5d6de957750e01dd69c9dc5b3 100644
--- a/features/steps/project/network_graph.rb
+++ b/features/steps/project/network_graph.rb
@@ -20,11 +20,11 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
   end
 
   step 'page should select "master" in select box' do
-    expect(page).to have_selector '.select2-chosen', text: "master"
+    expect(page).to have_selector '.dropdown-menu-toggle', text: "master"
   end
 
   step 'page should select "v1.0.0" in select box' do
-    expect(page).to have_selector '.select2-chosen', text: "v1.0.0"
+    expect(page).to have_selector '.dropdown-menu-toggle', text: "v1.0.0"
   end
 
   step 'page should have "master" on graph' do
@@ -40,11 +40,19 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
   end
 
   When 'I switch ref to "feature"' do
-    select 'feature', from: 'ref'
+    first('.js-project-refs-dropdown').click
+
+    page.within '.project-refs-form' do
+      click_link 'feature'
+    end
   end
 
   When 'I switch ref to "v1.0.0"' do
-    select 'v1.0.0', from: 'ref'
+    first('.js-project-refs-dropdown').click
+
+    page.within '.project-refs-form' do
+      click_link 'v1.0.0'
+    end
   end
 
   When 'click "Show only selected branch" checkbox' do
@@ -68,11 +76,11 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
   end
 
   step 'page should select "feature" in select box' do
-    expect(page).to have_selector '.select2-chosen', text: "feature"
+    expect(page).to have_selector '.dropdown-menu-toggle', text: "feature"
   end
 
   step 'page should select "v1.0.0" in select box' do
-    expect(page).to have_selector '.select2-chosen', text: "v1.0.0"
+    expect(page).to have_selector '.dropdown-menu-toggle', text: "v1.0.0"
   end
 
   step 'page should have "feature" on graph' do
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index 79a3ed8197e46ec9cb44a17d4344038247354d6e..0fe046dcbf6616393e0e3793baa0d477266ac9da 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -290,15 +290,23 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
   end
 
   step "I switch ref to 'test'" do
-    select "'test'", from: 'ref'
+    first('.js-project-refs-dropdown').click
+
+    page.within '.project-refs-form' do
+      click_link 'test'
+    end
   end
 
   step "I switch ref to fix" do
-    select "fix", from: 'ref'
+    first('.js-project-refs-dropdown').click
+
+    page.within '.project-refs-form' do
+      click_link 'fix'
+    end
   end
 
   step "I see the ref 'test' has been selected" do
-    expect(page).to have_selector '.select2-chosen', text: "'test'"
+    expect(page).to have_selector '.dropdown-toggle-text', text: "'test'"
   end
 
   step "I visit the 'test' tree" do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index fba545560c7a53f9b3b7eb037ce5d3b1121b8d9a..146b2c2e131e822c43be6bb582a5da1a67f13381 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -237,4 +237,24 @@ describe ProjectsController do
       expect(response.status).to eq(401)
     end
   end
+
+  describe "GET refs" do
+    it "should get a list of branches and tags" do
+      get :refs, namespace_id: public_project.namespace.path, id: public_project.path
+
+      parsed_body = JSON.parse(response.body)
+      expect(parsed_body["Branches"]).to include("master")
+      expect(parsed_body["Tags"]).to include("v1.0.0")
+      expect(parsed_body["Commits"]).to be_nil
+    end
+
+    it "should get a list of branches, tags and commits" do
+      get :refs, namespace_id: public_project.namespace.path, id: public_project.path, ref: "123456"
+
+      parsed_body = JSON.parse(response.body)
+      expect(parsed_body["Branches"]).to include("master")
+      expect(parsed_body["Tags"]).to include("v1.0.0")
+      expect(parsed_body["Commits"]).to include("123456")
+    end
+  end
 end
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index 51be81d634c17e30119c1ae7b1328366ecc0d09e..01e90618a98cd68e233e6a4d655cb1bd1586fc53 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -1,8 +1,6 @@
 require 'spec_helper'
 
 feature 'list of badges' do
-  include Select2Helper
-
   background do
     user = create(:user)
     project = create(:project)
@@ -24,7 +22,11 @@ feature 'list of badges' do
   end
 
   scenario 'user changes current ref on badges list page', js: true do
-    select2('improve/awesome', from: '#ref')
+    first('.js-project-refs-dropdown').click
+
+    page.within '.project-refs-form' do
+      click_link 'improve/awesome'
+    end
 
     expect(page).to have_content 'badges/improve/awesome/build.svg'
   end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index f6c1005d2655971e5585000f02f797e60a9e17cc..bb28866f01009fca5dc0e0e6a081f6261750c215 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -174,51 +174,6 @@ describe ApplicationHelper do
     end
   end
 
-  describe 'grouped_options_refs' do
-    let(:options) { helper.grouped_options_refs }
-    let(:project) { create(:project) }
-
-    before do
-      assign(:project, project)
-
-      # Override Rails' grouped_options_for_select helper to just return the
-      # first argument (`options`), since it's easier to work with than the
-      # generated HTML.
-      allow(helper).to receive(:grouped_options_for_select).
-        and_wrap_original { |_, *args| args.first }
-    end
-
-    it 'includes a list of branch names' do
-      expect(options[0][0]).to eq('Branches')
-      expect(options[0][1]).to include('master', 'feature')
-    end
-
-    it 'includes a list of tag names' do
-      expect(options[1][0]).to eq('Tags')
-      expect(options[1][1]).to include('v1.0.0', 'v1.1.0')
-    end
-
-    it 'includes a specific commit ref if defined' do
-      # Must be an instance variable
-      ref = '2ed06dc41dbb5936af845b87d79e05bbf24c73b8'
-      assign(:ref, ref)
-
-      expect(options[2][0]).to eq('Commit')
-      expect(options[2][1]).to eq([ref])
-    end
-
-    it 'sorts tags in a natural order' do
-      # Stub repository.tag_names to make sure we get some valid testing data
-      expect(project.repository).to receive(:tag_names).
-        and_return(['v1.0.9', 'v1.0.10', 'v2.0', 'v3.1.4.2', 'v2.0rc1¿',
-                    'v1.0.9a', 'v2.0-rc1', 'v2.0rc2'])
-
-      expect(options[1][1]).
-        to eq(['v3.1.4.2', 'v2.0', 'v2.0rc2', 'v2.0rc1¿', 'v2.0-rc1', 'v1.0.10',
-               'v1.0.9', 'v1.0.9a'])
-    end
-  end
-
   describe 'simple_sanitize' do
     let(:a_tag) { '<a href="#">Foo</a>' }