diff --git a/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx b/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx
index b3e122cbc78e59a1dedd976dc1c45c2e8ab5cbe2..942463586a2f08ce7f67c9fccadee684975191d9 100644
--- a/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx
+++ b/frontend/src/metabase/admin/databases/containers/DatabaseEditApp.jsx
@@ -2,6 +2,7 @@
 import React, { Component } from "react";
 import PropTypes from "prop-types";
 import { connect } from "react-redux";
+import title from "metabase/hoc/Title";
 
 import MetabaseSettings from "metabase/lib/settings";
 import DeleteDatabaseModal from "../components/DeleteDatabaseModal.jsx";
@@ -41,6 +42,7 @@ const mapDispatchToProps = {
 };
 
 @connect(mapStateToProps, mapDispatchToProps)
+@title(({ database }) => database && database.name)
 export default class DatabaseEditApp extends Component {
     static propTypes = {
         database: PropTypes.object,
diff --git a/frontend/src/metabase/admin/permissions/routes.jsx b/frontend/src/metabase/admin/permissions/routes.jsx
index 47308bb5699934df97207a1de4e079202407ddd8..23c3df9119eedcda2a0710f7a4c4a5b29718097b 100644
--- a/frontend/src/metabase/admin/permissions/routes.jsx
+++ b/frontend/src/metabase/admin/permissions/routes.jsx
@@ -1,5 +1,7 @@
+
 import React from "react";
-import { Route, IndexRedirect } from 'react-router';
+import { Route } from "metabase/hoc/Title";
+import { IndexRedirect } from 'react-router';
 
 import DataPermissionsApp from "./containers/DataPermissionsApp.jsx";
 import DatabasesPermissionsApp from "./containers/DatabasesPermissionsApp.jsx";
@@ -7,7 +9,7 @@ import SchemasPermissionsApp from "./containers/SchemasPermissionsApp.jsx";
 import TablesPermissionsApp from "./containers/TablesPermissionsApp.jsx";
 
 const getRoutes = (store) =>
-    <Route path="permissions" component={DataPermissionsApp}>
+    <Route title="Permissions" path="permissions" component={DataPermissionsApp}>
         <IndexRedirect to="databases" />
         <Route path="databases" component={DatabasesPermissionsApp} />
         <Route path="databases/:databaseId/schemas" component={SchemasPermissionsApp} />
diff --git a/frontend/src/metabase/admin/settings/containers/SettingsEditorApp.jsx b/frontend/src/metabase/admin/settings/containers/SettingsEditorApp.jsx
index 0928a1420d60e83ec6c58c92eec2ed837016f83b..83e121977c4634a7a59b361963bfa73e21636f57 100644
--- a/frontend/src/metabase/admin/settings/containers/SettingsEditorApp.jsx
+++ b/frontend/src/metabase/admin/settings/containers/SettingsEditorApp.jsx
@@ -2,6 +2,8 @@ import React, { Component } from "react";
 import PropTypes from "prop-types";
 import { Link } from "react-router";
 import { connect } from "react-redux";
+
+import title from "metabase/hoc/Title";
 import MetabaseAnalytics from "metabase/lib/analytics";
 
 import AdminLayout from "metabase/components/AdminLayout.jsx";
@@ -41,6 +43,7 @@ const mapDispatchToProps = {
 }
 
 @connect(mapStateToProps, mapDispatchToProps)
+@title(({ activeSection }) => activeSection && activeSection.name)
 export default class SettingsEditorApp extends Component {
     static propTypes = {
         sections: PropTypes.array.isRequired,
diff --git a/frontend/src/metabase/dashboard/containers/DashboardApp.jsx b/frontend/src/metabase/dashboard/containers/DashboardApp.jsx
index 8e704cde2cdc3225d7c6a29d626cc344f587e87d..568e01fefc0fa1fc671e1ce89d0ce0a76104fa86 100644
--- a/frontend/src/metabase/dashboard/containers/DashboardApp.jsx
+++ b/frontend/src/metabase/dashboard/containers/DashboardApp.jsx
@@ -3,6 +3,7 @@
 import React, { Component } from "react";
 import { connect } from "react-redux";
 import { push } from "react-router-redux";
+import title from "metabase/hoc/Title";
 
 import Dashboard from "../components/Dashboard.jsx";
 
@@ -40,6 +41,7 @@ const mapDispatchToProps = {
 }
 
 @connect(mapStateToProps, mapDispatchToProps)
+@title(({ dashboard }) => dashboard && dashboard.name)
 export default class DashboardApp extends Component {
     render() {
         return <Dashboard {...this.props} />;
diff --git a/frontend/src/metabase/hoc/Title.jsx b/frontend/src/metabase/hoc/Title.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..d150131d270ef4d6c2fca11c2f1721000be623ea
--- /dev/null
+++ b/frontend/src/metabase/hoc/Title.jsx
@@ -0,0 +1,88 @@
+import React from "react";
+
+import _ from "underscore";
+
+const componentStack = [];
+
+let SEPARATOR = " · ";
+let HIERARCHICAL = true;
+let BASE_NAME = null;
+
+export const setSeparator = (separator) => SEPARATOR = separator;
+export const setHierarchical = (hierarchical) => HIERARCHICAL = hierarchical;
+export const setBaseName = (baseName) => BASE_NAME = baseName;
+
+const updateDocumentTitle = _.debounce(() => {
+    if (HIERARCHICAL) {
+        document.title = componentStack
+            .map(component => component._documentTitle)
+            .filter(title => title)
+            .reverse()
+            .join(SEPARATOR);
+    } else {
+        // update with the top-most title
+        for (let i = componentStack.length - 1; i >= 0; i--) {
+            let title = componentStack[i]._documentTitle;
+            if (title) {
+                if (BASE_NAME) {
+                    title += SEPARATOR + BASE_NAME;
+                }
+                if (document.title !== title) {
+                    document.title = title;
+                }
+                break;
+            }
+        }
+    }
+})
+
+const title = (documentTitleOrGetter) => (ComposedComponent) =>
+    class extends React.Component {
+        static displayName = "Title["+(ComposedComponent.displayName || ComposedComponent.name)+"]";
+
+        componentWillMount() {
+            componentStack.push(this);
+            this._updateDocumentTitle();
+        }
+        componentDidUpdate() {
+            this._updateDocumentTitle();
+        }
+        componentWillUnmount() {
+            for (let i = 0; i < componentStack.length; i++) {
+                if (componentStack[i] === this) {
+                    componentStack.splice(i, 1);
+                    break;
+                }
+            }
+            this._updateDocumentTitle();
+        }
+
+        _updateDocumentTitle() {
+            if (typeof documentTitleOrGetter === "string") {
+                this._documentTitle = documentTitleOrGetter;
+            } else if (typeof documentTitleOrGetter === "function") {
+                this._documentTitle = documentTitleOrGetter(this.props);
+            }
+            updateDocumentTitle();
+        }
+
+        render() {
+            return <ComposedComponent {...this.props} />;
+        }
+    }
+
+export default title;
+
+import { Route as _Route } from "react-router";
+
+// react-router Route wrapper that adds a `title` property
+export class Route extends _Route {
+    static createRouteFromReactElement(element) {
+        if (element.props.title) {
+            element = React.cloneElement(element, {
+                component: title(element.props.title)(element.props.component || (({ children }) => children))
+            });
+        }
+        return _Route.createRouteFromReactElement(element);
+    }
+}
diff --git a/frontend/src/metabase/pulse/containers/PulseEditApp.jsx b/frontend/src/metabase/pulse/containers/PulseEditApp.jsx
index 60c68b4aa41af0ee53acb4adb1de150d9ef6a8b8..cc54cf9df4699f98a7e7586a0b3c3623b63fc692 100644
--- a/frontend/src/metabase/pulse/containers/PulseEditApp.jsx
+++ b/frontend/src/metabase/pulse/containers/PulseEditApp.jsx
@@ -3,6 +3,8 @@ import React, { Component } from "react";
 import { connect } from "react-redux";
 import { push } from "react-router-redux";
 
+import title from "metabase/hoc/Title";
+
 import PulseEdit from "../components/PulseEdit.jsx";
 
 import { editPulseSelectors } from "../selectors";
@@ -39,6 +41,7 @@ const mapDispatchToProps = {
 };
 
 @connect(mapStateToProps, mapDispatchToProps)
+@title(({ pulse }) => pulse && pulse.name)
 export default class PulseEditApp extends Component {
     render() {
         return (
diff --git a/frontend/src/metabase/qb/components/TimeseriesFilterWidget.jsx b/frontend/src/metabase/qb/components/TimeseriesFilterWidget.jsx
index 285e528400e100284e6c370c8d317912019f1290..bacd9397af7e9c28ef6b18e944c21831d1a453d1 100644
--- a/frontend/src/metabase/qb/components/TimeseriesFilterWidget.jsx
+++ b/frontend/src/metabase/qb/components/TimeseriesFilterWidget.jsx
@@ -154,7 +154,9 @@ export default class TimeseriesFilterWidget extends Component<*, Props, State> {
                                 this._popover.close();
                             }
                         }}
-                    >Apply</Button>
+                    >
+                        Apply
+                    </Button>
                 </div>
             </PopoverWithTrigger>
         );
diff --git a/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx b/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx
index d934891be8cb6a19b532b1d3611cda766ee24d69..261438e2947f45d2a1050a7e96705a415562cfcd 100644
--- a/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx
+++ b/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx
@@ -1,6 +1,7 @@
 import React, { Component } from "react";
 import ReactDOM from "react-dom";
 import { connect } from "react-redux";
+
 import cx from "classnames";
 import _ from "underscore";
 
@@ -18,6 +19,8 @@ import TagEditorSidebar from "../components/template_tags/TagEditorSidebar.jsx";
 import SavedQuestionIntroModal from "../components/SavedQuestionIntroModal.jsx";
 import ActionsWidget from "../components/ActionsWidget.jsx";
 
+import title from "metabase/hoc/Title";
+
 import {
     getCard,
     getOriginalCard,
@@ -123,6 +126,7 @@ const mapDispatchToProps = {
 };
 
 @connect(mapStateToProps, mapDispatchToProps)
+@title(({ card }) => (card && card.name) || "Question")
 export default class QueryBuilder extends Component {
 
     constructor(props, context) {
diff --git a/frontend/src/metabase/questions/containers/CollectionPage.jsx b/frontend/src/metabase/questions/containers/CollectionPage.jsx
index c69b15be2244a3fd6a3394eeaf68a405e4f03789..49822508801a6e045b1436d90cb7b8b7aa3623fd 100644
--- a/frontend/src/metabase/questions/containers/CollectionPage.jsx
+++ b/frontend/src/metabase/questions/containers/CollectionPage.jsx
@@ -1,6 +1,7 @@
 import React, { Component } from "react";
 import { connect } from "react-redux";
 import { push, replace, goBack } from "react-router-redux";
+import title from "metabase/hoc/Title";
 
 import Icon from "metabase/components/Icon";
 import HeaderWithBack from "metabase/components/HeaderWithBack";
@@ -27,6 +28,7 @@ const mapDispatchToProps = ({
 })
 
 @connect(mapStateToProps, mapDispatchToProps)
+@title(({ collection }) => collection && collection.name)
 export default class CollectionPage extends Component {
     componentWillMount () {
         this.props.loadCollections();
diff --git a/frontend/src/metabase/routes.jsx b/frontend/src/metabase/routes.jsx
index f52854442201ee62eae1d9693c4d623e074c5099..d09921e45620562426d7f06c7353ed7b1f9f5cb0 100644
--- a/frontend/src/metabase/routes.jsx
+++ b/frontend/src/metabase/routes.jsx
@@ -2,7 +2,8 @@
 
 import React from "react";
 
-import { Route, Redirect, IndexRedirect, IndexRoute } from 'react-router';
+import { Route } from "metabase/hoc/Title";
+import { Redirect, IndexRedirect, IndexRoute } from 'react-router';
 import { routerActions } from 'react-router-redux';
 import { UserAuthWrapper } from 'redux-auth-wrapper';
 
@@ -114,7 +115,7 @@ const IsAdmin = MetabaseIsSetup(UserIsAuthenticated(UserIsAdmin(({ children }) =
 const IsNotAuthenticated = MetabaseIsSetup(UserIsNotAuthenticated(({ children }) => children));
 
 export const getRoutes = (store) =>
-    <Route component={App}>
+    <Route title="Metabase" component={App}>
         {/* SETUP */}
         <Route path="/setup" component={SetupApp} onEnter={(nextState, replace) => {
             if (!MetabaseSettings.hasSetupToken()) {
@@ -137,7 +138,7 @@ export const getRoutes = (store) =>
             <Route path="/auth">
                 <IndexRedirect to="/auth/login" />
                 <Route component={IsNotAuthenticated}>
-                    <Route path="login" component={LoginApp} />
+                    <Route path="login" title="Login" component={LoginApp} />
                 </Route>
                 <Route path="logout" component={LogoutApp} />
                 <Route path="forgot_password" component={ForgotPasswordApp} />
@@ -151,17 +152,17 @@ export const getRoutes = (store) =>
                 <Route path="/" component={HomepageApp} />
 
                 {/* DASHBOARD */}
-                <Route path="/dashboard/:dashboardId" component={DashboardApp} />
+                <Route path="/dashboard/:dashboardId" title="Dashboard" component={DashboardApp} />
 
                 {/* QUERY BUILDER */}
                 <Route path="/question" component={QueryBuilder} />
                 <Route path="/question/:cardId" component={QueryBuilder} />
 
                 {/* QUESTIONS */}
-                <Route path="/questions">
+                <Route path="/questions" title="Questions">
                     <IndexRoute component={QuestionIndex} />
-                    <Route path="search" component={SearchResults} />
-                    <Route path="archive" component={Archive} />
+                    <Route path="search" title={({ location: { query: { q } }}) => "Search: " + q} component={SearchResults} />
+                    <Route path="archive" title="Archive" component={Archive} />
                     <Route path="collections/:collectionSlug" component={CollectionPage} />
                 </Route>
 
@@ -182,9 +183,9 @@ export const getRoutes = (store) =>
                 </Route>
 
                 {/* REFERENCE */}
-                <Route path="/reference" component={ReferenceApp}>
+                <Route path="/reference" title="Data Reference" component={ReferenceApp}>
                     <IndexRedirect to="/reference/guide" />
-                    <Route path="guide" component={ReferenceGettingStartedGuide} />
+                    <Route path="guide" title="Getting Started" component={ReferenceGettingStartedGuide} />
                     <Route path="metrics" component={ReferenceEntityList} />
                     <Route path="metrics/:metricId" component={ReferenceEntity} />
                     <Route path="metrics/:metricId/questions" component={ReferenceEntityList} />
@@ -205,23 +206,27 @@ export const getRoutes = (store) =>
                 </Route>
 
                 {/* PULSE */}
-                <Route path="/pulse" component={PulseListApp} />
-                <Route path="/pulse/create" component={PulseEditApp} />
-                <Route path="/pulse/:pulseId" component={PulseEditApp} />
+                <Route path="/pulse" title="Pulses">
+                    <IndexRoute component={PulseListApp} />
+                    <Route path="create" component={PulseEditApp} />
+                    <Route path=":pulseId" component={PulseEditApp} />
+                </Route>
 
                 {/* USER */}
                 <Route path="/user/edit_current" component={UserSettingsApp} />
             </Route>
 
             {/* ADMIN */}
-            <Route path="/admin" component={IsAdmin}>
+            <Route path="/admin" title="Admin" component={IsAdmin}>
                 <IndexRedirect to="/admin/settings" />
 
-                <Route path="databases" component={DatabaseListApp} />
-                <Route path="databases/create" component={DatabaseEditApp} />
-                <Route path="databases/:databaseId" component={DatabaseEditApp} />
+                <Route path="databases" title="Databases">
+                    <IndexRoute component={DatabaseListApp} />
+                    <Route path="create" component={DatabaseEditApp} />
+                    <Route path=":databaseId" component={DatabaseEditApp} />
+                </Route>
 
-                <Route path="datamodel">
+                <Route path="datamodel" title="Data Model">
                     <IndexRedirect to="database" />
                     <Route path="database" component={MetadataEditorApp} />
                     <Route path="database/:databaseId" component={MetadataEditorApp} />
@@ -235,16 +240,20 @@ export const getRoutes = (store) =>
                 </Route>
 
                 {/* PEOPLE */}
-                <Route path="people" component={AdminPeopleApp}>
+                <Route path="people" title="People" component={AdminPeopleApp}>
                     <IndexRoute component={PeopleListingApp} />
-                    <Route path="groups">
+                    <Route path="groups" title="Groups">
                         <IndexRoute component={GroupsListingApp} />
                         <Route path=":groupId" component={GroupDetailApp} />
                     </Route>
                 </Route>
 
-                <Route path="settings" component={SettingsEditorApp} />
-                <Route path="settings/:section" component={SettingsEditorApp} />
+                {/* SETTINGS */}
+                <Route path="settings" title="Settings">
+                    <IndexRedirect to="/admin/settings/setup" />
+                    {/* <IndexRoute component={SettingsEditorApp} /> */}
+                    <Route path=":section" component={SettingsEditorApp} />
+                </Route>
 
                 {getAdminPermissionsRoutes(store)}
             </Route>