diff --git a/CHANGELOG b/CHANGELOG
index 3394a6bdf05803e0f2f16495643f383d603b508c..1ebed18ebb49f2e8935f30eace1aeddacebdda18 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -87,6 +87,7 @@ v 8.12.0
   - Fix markdown anchor icon interaction (ClemMakesApps)
   - Test migration paths from 8.5 until current release !4874
   - Replace animateEmoji timeout with eventListener (ClemMakesApps)
+  - Show badges in Milestone tabs. !5946 (Dan Rowden)
   - Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
   - Require confirmation when not logged in for unsubscribe links !6223 (Maximiliano Perez Coto)
   - Add `wiki_page_events` to project hook APIs (Ben Boeckel)
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index b94f524b51339b1f243358c2755788242c9c19e3..6b865730487c234058d4be2f437edce838fd2de8 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -2,13 +2,17 @@
   max-width: 90%;
 }
 
-li.milestone {
-  h4 {
-    font-weight: bold;
-  }
+.milestones {
+  .milestone {
+    padding: 10px 16px;
+
+    h4 {
+      font-weight: bold;
+    }
 
-  .progress {
-    height: 6px;
+    .progress {
+      height: 6px;
+    }
   }
 }
 
@@ -64,3 +68,14 @@ li.milestone {
   border-bottom: 1px solid $border-color;
   padding: 20px 0;
 }
+
+@media (max-width: $screen-sm-min) {
+  .milestone-actions {
+    @include clearfix();
+    padding-top: $gl-vert-padding;
+
+    .btn:first-child {
+      margin-left: 0;
+    }
+  }
+}
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index b3e6e468ecd475b84f7ca502f64b64fd6442ad4e..a11c313a6b802ebd99059eb214046904abf87b8e 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -35,6 +35,30 @@ def milestone_issues_by_label_count(milestone, label, state:)
     milestone.issues.with_label(label.title).send(state).size
   end
 
+  # Returns count of milestones for different states
+  # Uses explicit hash keys as the 'opened' state URL params differs from the db value
+  # and we need to add the total
+  def milestone_counts(milestones)
+    counts = milestones.reorder(nil).group(:state).count
+
+    {
+      opened: counts['active'] || 0,
+      closed: counts['closed'] || 0,
+      all: counts.values.sum || 0
+    }
+  end
+
+  # Show 'active' class if provided GET param matches check
+  # `or_blank` allows the function to return 'active' when given an empty param
+  # Could be refactored to be simpler but that may make it harder to read
+  def milestone_class_for_state(param, check, match_blank_param = false)
+    if match_blank_param
+      'active' if param.blank? || param == check
+    else
+      'active' if param == check
+    end
+  end
+
   def milestone_progress_bar(milestone)
     options = {
       class: 'progress-bar progress-bar-success',
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
index cf16c203f9c89eee95c186fbcaf88d41c196f5e1..73d288e22366f32229b4761c5e2032691b4d81d1 100644
--- a/app/views/shared/_milestones_filter.html.haml
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -1,10 +1,19 @@
+- if @project
+  - counts = milestone_counts(@project.milestones)
+
 %ul.nav-links
-  %li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
+  %li{class: milestone_class_for_state(params[:state], 'opened', true)}
     = link_to milestones_filter_path(state: 'opened') do
       Open
-  %li{class: ("active" if params[:state] == 'closed')}
+      - if @project
+        %span.badge #{counts[:opened]}
+  %li{class: milestone_class_for_state(params[:state], 'closed')}
     = link_to milestones_filter_path(state: 'closed') do
       Closed
-  %li{class: ("active" if params[:state] == 'all')}
+      - if @project
+        %span.badge #{counts[:closed]}
+  %li{class: milestone_class_for_state(params[:state], 'all')}
     = link_to milestones_filter_path(state: 'all') do
       All
+      - if @project
+        %span.badge #{counts[:all]}
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index acc3ccf4dcf1796b5dbb659b2115339dde7b62ab..3dccfb147bfaab5363c8842147ca6df4716eaa5f 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -33,7 +33,7 @@
   - if @project
     .row
       .col-sm-6= render('shared/milestone_expired', milestone: milestone)
-      .col-sm-6
+      .col-sm-6.milestone-actions
         - if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
           = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs btn-grouped" do
             Edit
diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb
index e9e85962fe4e6d30b5cfdfed3f8a6e8b767e1df5..84da71ed6dcce196a759e92d6e14fd73911a874a 100644
--- a/spec/factories/milestones.rb
+++ b/spec/factories/milestones.rb
@@ -3,10 +3,15 @@
     title
     project
 
+    trait :active do
+      state "active"
+    end
+
     trait :closed do
-      state :closed
+      state "closed"
     end
 
+    factory :active_milestone, traits: [:active]
     factory :closed_milestone, traits: [:closed]
   end
 end
diff --git a/spec/helpers/milestones_helper_spec.rb b/spec/helpers/milestones_helper_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..28c2268f8d08a7ca3b5e0058ab7fb6b99284a273
--- /dev/null
+++ b/spec/helpers/milestones_helper_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe MilestonesHelper do
+  describe '#milestone_counts' do
+    let(:project) { FactoryGirl.create(:project) }
+    let(:counts) { helper.milestone_counts(project.milestones) }
+
+    context 'when there are milestones' do
+      let!(:milestone_1) { FactoryGirl.create(:active_milestone, project: project) }
+      let!(:milestone_2) { FactoryGirl.create(:active_milestone, project: project) }
+      let!(:milestone_3) { FactoryGirl.create(:closed_milestone, project: project) }
+
+      it 'returns the correct counts' do
+        expect(counts).to eq(opened: 2, closed: 1, all: 3)
+      end
+    end
+
+    context 'when there are only milestones of one type' do
+      let!(:milestone_1) { FactoryGirl.create(:active_milestone, project: project) }
+      let!(:milestone_2) { FactoryGirl.create(:active_milestone, project: project) }
+
+      it 'returns the correct counts' do
+        expect(counts).to eq(opened: 2, closed: 0, all: 2)
+      end
+    end
+
+    context 'when there are no milestones' do
+      it 'returns the correct counts' do
+        expect(counts).to eq(opened: 0, closed: 0, all: 0)
+      end
+    end
+  end
+end