diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js
index bad4be4ceef68f5fab96e87d0e7be62673e5cda2..f04adcf1292e05528177f829ff75de8506d89aa3 100644
--- a/apps/files/js/detailsview.js
+++ b/apps/files/js/detailsview.js
@@ -140,16 +140,14 @@
 				}
 				return orderA - orderB;
 			});
-			if (this._tabViews.length > 1) {
-				// only render headers if there is more than one available
-				templateVars.tabHeaders = _.map(this._tabViews, function(tabView, i) {
-					return {
-						tabId: tabView.id,
-						tabIndex: i,
-						label: tabView.getLabel()
-					};
-				});
-			}
+
+			templateVars.tabHeaders = _.map(this._tabViews, function(tabView, i) {
+				return {
+					tabId: tabView.id,
+					tabIndex: i,
+					label: tabView.getLabel()
+				};
+			});
 
 			this.$el.html(this.template(templateVars));
 
@@ -166,6 +164,8 @@
 
 			this.selectTab(this._currentTabId);
 
+			this._updateTabVisibilities();
+
 			this._dirty = false;
 		},
 
@@ -224,6 +224,8 @@
 
 			if (this._dirty) {
 				this.render();
+			} else {
+				this._updateTabVisibilities();
 			}
 
 			if (this._currentTabId) {
@@ -240,6 +242,37 @@
 			});
 		},
 
+		/**
+		 * Update tab headers based on the current model
+		 */
+		_updateTabVisibilities: function() {
+			// update tab header visibilities
+			var self = this;
+			var deselect = false;
+			var countVisible = 0;
+			var $tabHeaders = this.$el.find('.tabHeaders li');
+			_.each(this._tabViews, function(tabView) {
+				var isVisible = tabView.canDisplay(self.model);
+				if (isVisible) {
+					countVisible += 1;
+				}
+				if (!isVisible && self._currentTabId === tabView.id) {
+					deselect = true;
+				}
+				$tabHeaders.filterAttr('data-tabid', tabView.id).toggleClass('hidden', !isVisible);
+			});
+
+			// hide the whole container if there is only one tab
+			this.$el.find('.tabHeaders').toggleClass('hidden', countVisible <= 1);
+
+			if (deselect) {
+				// select the first visible tab instead
+				var visibleTabId = this.$el.find('.tabHeader:not(.hidden):first').attr('data-tabid');
+				this.selectTab(visibleTabId);
+			}
+
+		},
+
 		/**
 		 * Returns the file info.
 		 *
diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js
index d885e47b15ef5ce533421167f69cbe52566fbf4f..0bd34a881885538e057de5d162a75197fd19eab5 100644
--- a/apps/files/js/detailtabview.js
+++ b/apps/files/js/detailtabview.js
@@ -95,6 +95,17 @@
 		 */
 		nextPage: function() {
 			// load the next page, if applicable
+		},
+
+		/**
+		 * Returns whether the current tab is able to display
+		 * the given file info, for example based on mime type.
+		 *
+		 * @param {OCA.Files.FileInfoModel} fileInfo file info model
+		 * @return {bool} whether to display this tab
+		 */
+		canDisplay: function(fileInfo) {
+			return true;
 		}
 	});
 	DetailTabView._TAB_COUNT = 0;
diff --git a/apps/files/tests/js/detailsviewSpec.js b/apps/files/tests/js/detailsviewSpec.js
index f02e419434f0e58bd81dbe8ced835fcdf6a9edef..26a16b31530cce42d5631272805952d9d66f9bb0 100644
--- a/apps/files/tests/js/detailsviewSpec.js
+++ b/apps/files/tests/js/detailsviewSpec.js
@@ -144,14 +144,76 @@ describe('OCA.Files.DetailsView tests', function() {
 			expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(true);
 			expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(false);
 		});
-		it('does not render tab headers when only one tab exists', function() {
-			detailsView.remove();
-			detailsView = new OCA.Files.DetailsView();
-			testView = new OCA.Files.DetailTabView({id: 'test1'});
-			detailsView.addTabView(testView);
-			detailsView.render();
-
-			expect(detailsView.$el.find('.tabHeader').length).toEqual(0);
+		describe('tab visibility', function() {
+			beforeEach(function() {
+				detailsView.remove();
+				detailsView = new OCA.Files.DetailsView();
+			});
+			it('does not display tab headers when only one tab exists', function() {
+				testView = new OCA.Files.DetailTabView({id: 'test1'});
+				detailsView.addTabView(testView);
+				detailsView.render();
+
+				expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(true);
+				expect(detailsView.$el.find('.tabHeader').length).toEqual(1);
+			});
+			it('does not display tab that do not pass visibility check', function() {
+				testView = new OCA.Files.DetailTabView({id: 'test1'});
+				testView.canDisplay = sinon.stub().returns(false);
+				testView2 = new OCA.Files.DetailTabView({id: 'test2'});
+				var testView3 = new OCA.Files.DetailTabView({id: 'test3'});
+				detailsView.addTabView(testView);
+				detailsView.addTabView(testView2);
+				detailsView.addTabView(testView3);
+
+				var fileInfo = {id: 5, name: 'test.txt'};
+				detailsView.setFileInfo(fileInfo);
+
+				expect(testView.canDisplay.calledOnce).toEqual(true);
+				expect(testView.canDisplay.calledWith(fileInfo)).toEqual(true);
+
+				expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(false);
+				expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('hidden')).toEqual(true);
+				expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('hidden')).toEqual(false);
+				expect(detailsView.$el.find('.tabHeader[data-tabid=test3]').hasClass('hidden')).toEqual(false);
+			});
+			it('does not show tab headers if only one header is visible due to visibility check', function() {
+				testView = new OCA.Files.DetailTabView({id: 'test1'});
+				testView.canDisplay = sinon.stub().returns(false);
+				testView2 = new OCA.Files.DetailTabView({id: 'test2'});
+				detailsView.addTabView(testView);
+				detailsView.addTabView(testView2);
+
+				var fileInfo = {id: 5, name: 'test.txt'};
+				detailsView.setFileInfo(fileInfo);
+
+				expect(testView.canDisplay.calledOnce).toEqual(true);
+				expect(testView.canDisplay.calledWith(fileInfo)).toEqual(true);
+
+				expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(true);
+				expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('hidden')).toEqual(true);
+				expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('hidden')).toEqual(false);
+			});
+			it('deselects the current tab if a model update does not pass the visibility check', function() {
+				testView = new OCA.Files.DetailTabView({id: 'test1'});
+				testView.canDisplay = function(fileInfo) {
+					return fileInfo.mimetype !== 'httpd/unix-directory';
+				};
+				testView2 = new OCA.Files.DetailTabView({id: 'test2'});
+				detailsView.addTabView(testView);
+				detailsView.addTabView(testView2);
+
+				var fileInfo = {id: 5, name: 'test.txt', mimetype: 'text/plain'};
+				detailsView.setFileInfo(fileInfo);
+
+				expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('selected')).toEqual(true);
+				expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('selected')).toEqual(false);
+
+				detailsView.setFileInfo({id: 10, name: 'folder', mimetype: 'httpd/unix-directory'});
+
+				expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('selected')).toEqual(false);
+				expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('selected')).toEqual(true);
+			});
 		});
 		it('sorts by order and then label', function() {
 			detailsView.remove();
diff --git a/apps/files_versions/js/versionstabview.js b/apps/files_versions/js/versionstabview.js
index 6dab8014229f158b981e55ee69917d955e50c805..55f2486819655776c225c12ea099ca053acb024e 100644
Binary files a/apps/files_versions/js/versionstabview.js and b/apps/files_versions/js/versionstabview.js differ