diff --git a/frontend/src/components/Dropdown.vue b/frontend/src/components/Dropdown.vue
new file mode 100644
index 0000000..ce0c100
--- /dev/null
+++ b/frontend/src/components/Dropdown.vue
@@ -0,0 +1,86 @@
+<template>
+  <div class="dropdown">
+    <button class="dropdown__button" @click="show">
+      <slot name="button" />
+    </button>
+
+    <div class="dropdown__container" ref="container">
+      <div class="dropdown__content">
+        <slot />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  computed: {
+    container() {
+      return this.$refs.container as HTMLElement;
+    },
+  },
+
+  methods: {
+    show() {
+      this.container.classList.add('show');
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@use "@/styles/common.scss" as *;
+
+.dropdown {
+  position: relative;
+  display: inline-block;
+
+  &__button {
+    background: none;
+    border: none;
+    cursor: pointer;
+
+    &:focus,
+    &:hover {
+      color: var(--color-hover);
+      outline: none;
+    }
+  }
+
+  &__container {
+    position: absolute;
+    display: none;
+    top: 100%;
+    right: 0;
+    z-index: 20;
+
+    &.show {
+      display: block;
+    }
+  }
+
+  &__content {
+    min-width: 10rem;
+    display: flex;
+    background-color: var(--color-background);
+    border: 1px solid var(--color-border);
+    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5);
+    border-radius: 0.25rem;
+    padding: 0.25rem;
+    flex-direction: column;
+    animation: dropdown 0.25s ease-out;
+  }
+
+  @keyframes dropdown {
+    from {
+      opacity: 0;
+      transform: translateY(-0.5rem);
+    }
+
+    to {
+      opacity: 1;
+      transform: translateY(0);
+    }
+  }
+}
+</style>
diff --git a/frontend/src/components/DropdownItem.vue b/frontend/src/components/DropdownItem.vue
new file mode 100644
index 0000000..0a68eb8
--- /dev/null
+++ b/frontend/src/components/DropdownItem.vue
@@ -0,0 +1,48 @@
+<template>
+  <div class="dropdown__item" @click="$emit('click')">
+    <div class="dropdown__item__icon">
+      <slot name="icon" />
+    </div>
+    <div class="dropdown__item__text">
+      <slot />
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  emits: ['click'],
+}
+</script>
+
+<style lang="scss" scoped>
+.dropdown__item {
+  display: flex;
+  flex: 1;
+  padding: 0.5rem 0;
+  cursor: pointer;
+
+  &:hover {
+    color: var(--color-hover);
+
+    :deep(a) {
+      background-color: inherit;
+      color: var(--color-hover);
+    }
+  }
+
+  &__icon {
+    margin-right: 0.5rem;
+  }
+
+  &__text {
+    display: flex;
+    flex: 1;
+
+    :deep(a) {
+      flex: 1;
+      text-decoration: none;
+    }
+  }
+}
+</style>
diff --git a/frontend/src/mixins/Dropdowns.vue b/frontend/src/mixins/Dropdowns.vue
new file mode 100644
index 0000000..84863a0
--- /dev/null
+++ b/frontend/src/mixins/Dropdowns.vue
@@ -0,0 +1,16 @@
+<script lang="ts">
+export default {
+  methods: {
+    installDropdownHandler() {
+      document.addEventListener('click', (event) => {
+        if (!(event.target as HTMLElement).closest('.dropdown__button')) {
+          document.querySelectorAll('.dropdown__container').forEach((content) => {
+            content.classList.remove('show');
+          });
+        }
+      });
+    },
+  },
+}
+</script>
+