diff --git a/app/assets/javascripts/discourse/components/header-extra-info.js.es6 b/app/assets/javascripts/discourse/components/header-extra-info.js.es6
new file mode 100644
index 00000000000..a323d50df56
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/header-extra-info.js.es6
@@ -0,0 +1,47 @@
+const TopicCategoryComponent = Ember.Component.extend({
+  needsSecondRow: Ember.computed.gt('secondRowItems.length', 0),
+  secondRowItems: function() { return []; }.property(),
+
+  showPrivateMessageGlyph: function() {
+    return !this.get('topic.is_warning') && this.get('topic.isPrivateMessage');
+  }.property('topic.is_warning', 'topic.isPrivateMessage'),
+
+  actions: {
+    jumpToTopPost: function () {
+      var topic = this.get('topic');
+      if (topic) {
+        Discourse.URL.routeTo(topic.get('firstPostUrl'));
+      }
+    }
+  }
+
+});
+
+let id = 0;
+
+// Allow us (and plugins) to register themselves as needing a second
+// row in the header. If there is at least one thing in the second row
+// the style changes to accomodate it.
+function needsSecondRowIf(prop, cb) {
+  const rowId = "_second_row_" + (id++),
+        methodHash = {};
+
+  methodHash[id] = function() {
+    const secondRowItems = this.get('secondRowItems'),
+          propVal = this.get(prop);
+    if (cb.call(this, propVal)) {
+      secondRowItems.addObject(rowId);
+    } else {
+      secondRowItems.removeObject(rowId);
+    }
+  }.observes(prop).on('init');
+
+  TopicCategoryComponent.reopen(methodHash);
+}
+
+needsSecondRowIf('topic.category', function(cat) {
+  return cat && (!cat.get('isUncategorizedCategory') || !this.siteSettings.suppress_uncategorized_badge);
+});
+
+export default TopicCategoryComponent;
+export { needsSecondRowIf };
diff --git a/app/assets/javascripts/discourse/controllers/header.js.es6 b/app/assets/javascripts/discourse/controllers/header.js.es6
index 2511aa50088..b40ac3efdad 100644
--- a/app/assets/javascripts/discourse/controllers/header.js.es6
+++ b/app/assets/javascripts/discourse/controllers/header.js.es6
@@ -10,15 +10,6 @@ export default DiscourseController.extend({
   loginRequired: Em.computed.alias('controllers.application.loginRequired'),
   canSignUp: Em.computed.alias('controllers.application.canSignUp'),
 
-  hasCategory: function() {
-    var cat = this.get('topic.category');
-    return cat && (!cat.get('isUncategorizedCategory') || !this.siteSettings.suppress_uncategorized_badge);
-  }.property('topic.category'),
-
-  showPrivateMessageGlyph: function() {
-    return !this.get('topic.is_warning') && this.get('topic.isPrivateMessage');
-  }.property('topic.is_warning', 'topic.isPrivateMessage'),
-
   showSignUpButton: function() {
     return this.get('canSignUp') && !this.get('showExtraInfo');
   }.property('canSignUp', 'showExtraInfo'),
@@ -78,13 +69,6 @@ export default DiscourseController.extend({
         self.refreshNotifications();
       }
       headerView.showDropdownBySelector("#user-notifications");
-    },
-
-    jumpToTopPost: function () {
-      var topic = this.get('topic');
-      if (topic) {
-        Discourse.URL.routeTo(topic.get('firstPostUrl'));
-      }
     }
   }
 
diff --git a/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs b/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs
new file mode 100644
index 00000000000..c6326286eea
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs
@@ -0,0 +1,21 @@
+<div class="extra-info-wrapper">
+  <div {{bind-attr class=":extra-info needsSecondRow:two-rows"}}>
+    <div class="title-wrapper">
+      <h1>
+        {{#if showPrivateMessageGlyph}}
+          <span class="private-message-glyph">{{fa-icon "envelope"}}</span>
+        {{/if}}
+
+        {{#if topic.details.loaded}}
+          {{topic-status topic=topic}}
+          <a class='topic-link' href='{{unbound topic.url}}' {{action "jumpToTopPost"}}>{{{topic.fancy_title}}}</a>
+        {{else}}
+          {{#if topic.errorLoading}}
+            <span class="error">{{topic.errorTitle}}</span>
+          {{/if}}
+        {{/if}}
+      </h1>
+      {{topic-category topic=topic}}
+    </div>
+  </div>
+</div>
diff --git a/app/assets/javascripts/discourse/templates/header.hbs b/app/assets/javascripts/discourse/templates/header.hbs
index 7eec139b080..0299c69e931 100644
--- a/app/assets/javascripts/discourse/templates/header.hbs
+++ b/app/assets/javascripts/discourse/templates/header.hbs
@@ -77,9 +77,7 @@
       </ul>
 
       {{#if view.renderDropdowns}}
-
         {{render "search"}}
-
         {{render "notifications" notifications}}
 
         {{#if view.renderSiteMap}}
@@ -87,34 +85,11 @@
         {{/if}}
 
         {{render "user-dropdown"}}
-
       {{/if}}
-
     </div>
 
     {{#if showExtraInfo}}
-      <div class="extra-info-wrapper">
-        <div {{bind-attr class=":extra-info hasCategory"}}>
-          <div class="title-wrapper">
-            <h1>
-              {{#if showPrivateMessageGlyph}}
-                <span class="private-message-glyph">{{fa-icon "envelope"}}</span>
-              {{/if}}
-
-              {{#if topic.details.loaded}}
-                {{topic-status topic=topic}}
-                <a class='topic-link' href='{{unbound topic.url}}' {{action "jumpToTopPost"}}>{{{topic.fancy_title}}}</a>
-              {{else}}
-                {{#if topic.errorLoading}}
-                  <span class="error">{{topic.errorTitle}}</span>
-                {{/if}}
-              {{/if}}
-            </h1>
-            {{topic-category topic=topic}}
-          </div>
-        </div>
-      </div>
+      {{header-extra-info topic=topic}}
     {{/if}}
-
   </div>
 </div>
diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss
index f547ad00f80..6451a4e9b2d 100644
--- a/app/assets/stylesheets/desktop/topic-post.scss
+++ b/app/assets/stylesheets/desktop/topic-post.scss
@@ -577,7 +577,7 @@ video {
 }
 
 /* override docked header CSS for topics with categories */
-.extra-info.has-category {
+.extra-info.two-rows {
   h1 {
     line-height: 1.1em;
     margin: 0;