diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index be63baf..b7a3b21 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -1,25 +1,6 @@
 <template>
   <div class="app-container">
-    <header v-if="user">
-      <div class="wrapper">
-        <nav>
-          <li class="main">
-            <RouterLink to="/">
-              <font-awesome-icon icon="map-marker-alt" />&nbsp;&nbsp;GPSTracker
-            </RouterLink>
-          </li>
-
-          <div class="spacer" />
-
-          <li class="right">
-            <RouterLink to="/logout">
-              <font-awesome-icon icon="sign-out-alt" />&nbsp;&nbsp;
-              <span class="logout-text">Logout</span>
-            </RouterLink>
-          </li>
-        </nav>
-      </div>
-    </header>
+    <Header :user="user" />
 
     <div class="body">
       <Loading v-if="loading" />
@@ -35,13 +16,19 @@ import { RouterLink, RouterView } from 'vue-router'
 
 import { type Optional } from './models/Types';
 import Api from './mixins/Api.vue';
+import Dropdowns from './mixins/Dropdowns.vue';
+import Header from './components/Header.vue';
 import Loading from './elements/Loading.vue';
 import Messages from './components/Messages.vue'
 import User from './models/User';
 
 export default {
-  mixins: [Api],
+  mixins: [
+    Api,
+    Dropdowns,
+  ],
   components: {
+    Header,
     Loading,
     Messages,
     RouterLink,
@@ -57,6 +44,7 @@ export default {
 
   async mounted() {
     this.loading = true
+    this.installDropdownHandler()
 
     try {
       const auth = await this.fetchUser()
@@ -93,8 +81,6 @@ export default {
 <style lang="scss" scoped>
 @use "@/styles/common.scss" as *;
 
-$header-height: 3rem;
-
 .app-container {
   width: 100%;
   height: 100vh;
diff --git a/frontend/src/components/Header.vue b/frontend/src/components/Header.vue
new file mode 100644
index 0000000..33fd443
--- /dev/null
+++ b/frontend/src/components/Header.vue
@@ -0,0 +1,132 @@
+<template>
+  <header v-if="user">
+    <div class="wrapper">
+      <nav>
+        <li class="main">
+          <RouterLink :to="{ name: 'home', hash: $route.hash }">
+            <font-awesome-icon icon="map-marker-alt" />&nbsp;&nbsp;GPSTracker
+          </RouterLink>
+        </li>
+
+        <div class="spacer" />
+
+        <li class="right">
+          <Dropdown>
+            <template #button>
+              <button class="options" title="Options">
+                <font-awesome-icon icon="user" />
+              </button>
+            </template>
+
+            <DropdownItem>
+              <RouterLink to="/devices">
+                <font-awesome-icon icon="mobile-alt" />&nbsp;&nbsp;
+                <span class="item-text">Devices</span>
+              </RouterLink>
+            </DropdownItem>
+
+            <DropdownItem>
+              <RouterLink to="/logout">
+                <font-awesome-icon icon="sign-out-alt" />&nbsp;&nbsp;
+                <span class="item-text">Logout</span>
+              </RouterLink>
+            </DropdownItem>
+          </Dropdown>
+        </li>
+      </nav>
+    </div>
+  </header>
+</template>
+
+<script lang="ts">
+import { RouterLink } from 'vue-router'
+
+import { type Optional } from '../models/Types';
+import Dropdown from './Dropdown.vue';
+import DropdownItem from './DropdownItem.vue';
+import User from '../models/User';
+
+export default {
+  components: {
+    Dropdown,
+    DropdownItem,
+    RouterLink,
+  },
+
+  props: {
+    user: {
+      type: Object as () => Optional<User>,
+      required: true,
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@use "@/styles/common.scss" as *;
+
+header {
+  width: 100%;
+  height: $header-height;
+  margin-bottom: 0.2rem;
+  padding: 0.5rem 0;
+  box-shadow: 0 0 0.5rem 0 rgba(0, 0, 0, 0.5);
+
+  nav {
+    display: flex;
+    align-items: center;
+
+    li {
+      display: inline-block;
+      list-style: none;
+      border-radius: 0.25rem;
+
+      &.main {
+        font-size: 1.25rem;
+        margin-left: 0.5rem;
+
+        :deep(a) {
+          &:hover {
+            background: none;
+            font-size: 1.5rem;
+          }
+        }
+
+        &:hover {
+          margin-top: -0.25rem;
+        }
+      }
+
+      &:not(.main) {
+        :deep(a) {
+          color: var(--color-text);
+          text-decoration: none;
+        }
+      }
+
+      &.right {
+        margin-right: 0.5rem;
+      }
+
+      .logout-text {
+        @include mobile {
+          display: none;
+        }
+      }
+    }
+
+    .spacer {
+      flex: 1;
+    }
+  }
+
+  button {
+    background: none;
+    border: none;
+    cursor: pointer;
+    font-size: 1.25rem;
+    margin-right: 0.5rem;
+    padding: 0.25rem;
+  }
+}
+</style>
diff --git a/frontend/src/components/Map.vue b/frontend/src/components/Map.vue
index 102ce3e..84cfd08 100644
--- a/frontend/src/components/Map.vue
+++ b/frontend/src/components/Map.vue
@@ -440,15 +440,6 @@ main {
   box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.5);
 }
 
-@keyframes unroll {
-  from {
-    transform: translateY(7.5em);
-  }
-  to {
-    transform: translateY(0);
-  }
-}
-
 :deep(.ol-viewport) {
   .ol-attribution {
     position: absolute !important;
diff --git a/frontend/src/styles/common.scss b/frontend/src/styles/common.scss
index 1b34d6f..12045a8 100644
--- a/frontend/src/styles/common.scss
+++ b/frontend/src/styles/common.scss
@@ -4,6 +4,8 @@ $screen-sm: 768px;
 $screen-md: 992px;
 $screen-lg: 1200px;
 
+$header-height: 3.5rem;
+
 // @media utilities for common screen sizes
 @mixin mobile {
   @media (max-width: $screen-sm) {
@@ -112,3 +114,12 @@ button {
     opacity: 0;
   }
 }
+
+@keyframes unroll {
+  from {
+    transform: translateY(7.5em);
+  }
+  to {
+    transform: translateY(0);
+  }
+}