diff --git a/src/App.scss b/src/App.scss
index 52a786ad02e8b433713f82d0055bf5413caa4329..fd7249793b436ca11d21b08745dfa54b256748c1 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -809,14 +809,10 @@ nav {
 
 .autocomplete {
   &-panel {
-    position: relative;
-
     &-body {
       margin: 0 0.5em 0 0.5em;
       border-radius: $fallback--tooltipRadius;
       border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
-      position: absolute;
-      z-index: 1;
       box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
       // this doesn't match original but i don't care, making it uniform.
       box-shadow: var(--popupShadow);
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index ea5a3f58c055a811a51067ef0922232c356a8a5d..c0faa0a2838511a4ee39bd9b8e8cdd19b0d172ca 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -154,9 +154,9 @@ const getStaticEmoji = async ({ store }) => {
       const values = await res.json()
       const emoji = Object.keys(values).map((key) => {
         return {
-          shortcode: key,
-          image_url: false,
-          'replacement': values[key]
+          displayText: key,
+          imageUrl: false,
+          replacement: values[key]
         }
       })
       store.dispatch('setInstanceOption', { name: 'emoji', value: emoji })
@@ -178,9 +178,10 @@ const getCustomEmoji = async ({ store }) => {
       const result = await res.json()
       const values = Array.isArray(result) ? Object.assign({}, ...result) : result
       const emoji = Object.keys(values).map((key) => {
+        const imageUrl = values[key].image_url
         return {
-          shortcode: key,
-          image_url: values[key].image_url || values[key],
+          displayText: key,
+          imageUrl: imageUrl ? store.state.instance.server + imageUrl : values[key],
           replacement: `:${key}: `
         }
       })
diff --git a/src/components/emoji-input/emoji-input.js b/src/components/emoji-input/emoji-input.js
index 466341c07eb70dd1f174a79a1ea67f9dae55eec0..fb60d998f7363ac9caaad9eee5f3a1d92652455b 100644
--- a/src/components/emoji-input/emoji-input.js
+++ b/src/components/emoji-input/emoji-input.js
@@ -13,7 +13,19 @@ const EmojiInput = {
     return {
       input: undefined,
       highlighted: 0,
-      caret: 0
+      caret: 0,
+      focused: false,
+      popupOptions: {
+        placement: 'bottom-start',
+        trigger: 'hover',
+        // See: https://github.com/RobinCK/vue-popper/issues/63
+        'delay-on-mouse-over': 9999999,
+        'delay-on-mouse-out': 9999999,
+        modifiers: {
+          arrow: { enabled: true },
+          offset: { offset: '0, 5px' }
+        }
+      }
     }
   },
   computed: {
@@ -24,13 +36,17 @@ const EmojiInput = {
       if (matchedSuggestions.length <= 0) {
         return false
       }
-      return take(matchedSuggestions, 5).map(({shortcode, image_url, replacement}, index) => ({
-        shortcode,
-        replacement,
-        // eslint-disable-next-line camelcase
-        img: !image_url ? '' : this.$store.state.instance.server + image_url,
-        highlighted: index === this.highlighted
-      }))
+      return take(matchedSuggestions, 5)
+        .map(({ displayText, imageUrl, replacement }, index) => ({
+          displayText,
+          replacement,
+          // eslint-disable-next-line camelcase
+          img: imageUrl || '',
+          highlighted: index === this.highlighted
+        }))
+    },
+    showPopup () {
+      return this.focused && this.suggestions && this.suggestions.length > 0
     },
     textAtCaret () {
       return (this.wordAtCaret || {}).word || ''
@@ -40,25 +56,29 @@ const EmojiInput = {
         const word = Completion.wordAtPosition(this.value, this.caret - 1) || {}
         return word
       }
-    },
+    }
   },
   mounted () {
     const slots = this.$slots.default
-    if (slots.length === 0) return
+    if (!slots || slots.length === 0) return
     const input = slots.find(slot => ['input', 'textarea'].includes(slot.tag))
     if (!input) return
     this.input = input
-    input.elm.addEventListener('keyup', this.setCaret)
-    input.elm.addEventListener('paste', this.setCaret)
-    input.elm.addEventListener('focus', this.setCaret)
+    this.resize()
+    input.elm.addEventListener('blur', this.onBlur)
+    input.elm.addEventListener('focus', this.onFocus)
+    input.elm.addEventListener('paste', this.onPaste)
+    input.elm.addEventListener('keyup', this.onKeyUp)
     input.elm.addEventListener('keydown', this.onKeyDown)
   },
   unmounted () {
-    if (this.input) {
-      this.input.elm.removeEventListener('keyup', this.setCaret)
-      this.input.elm.removeEventListener('paste', this.setCaret)
-      this.input.elm.removeEventListener('focus', this.setCaret)
-      this.input.elm.removeEventListener('keydown', this.onKeyDown)
+    const { input } = this
+    if (input) {
+      input.elm.removeEventListener('blur', this.onBlur)
+      input.elm.removeEventListener('focus', this.onFocus)
+      input.elm.removeEventListener('paste', this.onPaste)
+      input.elm.removeEventListener('keyup', this.onKeyUp)
+      input.elm.removeEventListener('keydown', this.onKeyDown)
     }
   },
   methods: {
@@ -101,26 +121,49 @@ const EmojiInput = {
         this.highlighted = 0
       }
     },
+    onBlur (e) {
+      this.focused = false
+      this.setCaret(e)
+      this.resize(e)
+    },
+    onFocus (e) {
+      this.focused = true
+      this.setCaret(e)
+      this.resize(e)
+    },
+    onKeyUp (e) {
+      this.setCaret(e)
+      this.resize(e)
+    },
+    onPaste (e) {
+      this.setCaret(e)
+      this.resize(e)
+    },
     onKeyDown (e) {
       this.setCaret(e)
-      e.stopPropagation()
+      this.resize(e)
 
       const { ctrlKey, shiftKey, key } = e
       if (key === 'Tab') {
         if (shiftKey) {
           this.cycleBackward()
+          e.preventDefault()
         } else {
           this.cycleForward()
+          e.preventDefault()
         }
       }
       if (key === 'ArrowUp') {
         this.cycleBackward()
+        e.preventDefault()
       } else if (key === 'ArrowDown') {
         this.cycleForward()
+        e.preventDefault()
       }
       if (key === 'Enter') {
         if (!ctrlKey) {
           this.replaceText()
+          e.preventDefault()
         }
       }
     },
@@ -129,6 +172,12 @@ const EmojiInput = {
     },
     setCaret ({ target: { selectionStart, value } }) {
       this.caret = selectionStart
+    },
+    resize () {
+      const { panel } = this.$refs
+      if (!panel) return
+      const { offsetHeight, offsetTop } = this.input.elm
+      this.$refs.panel.style.top = (offsetTop + offsetHeight) + 'px'
     }
   }
 }
diff --git a/src/components/emoji-input/emoji-input.vue b/src/components/emoji-input/emoji-input.vue
index eec33d1ad635a788ec3830dc095e89df9b1f222a..0ca743227bf4a63e1f3a585e8b6b595c8e2df297 100644
--- a/src/components/emoji-input/emoji-input.vue
+++ b/src/components/emoji-input/emoji-input.vue
@@ -1,38 +1,25 @@
 <template>
-  <div class="emoji-input">
-    <slot
-      :class="classname"
-      :value="value"
-      :placeholder="placeholder"
-      @input="onInput"
-      @click="setCaret"
-      @keyup="setCaret"
-      @keydown="onKeydown"
-      @keydown.down="cycleForward"
-      @keydown.up="cycleBackward"
-      @keydown.shift.tab="cycleBackward"
-      @keydown.tab="cycleForward"
-      @keydown.enter="replaceEmoji"
-      >
-    </slot>
-    <div class="autocomplete-panel" v-if="suggestions">
-      <div class="autocomplete-panel-body">
-        <div
-          v-for="(suggestion, index) in suggestions"
-          :key="index"
-          @click="replace(suggestion.replacement)"
-          class="autocomplete-item"
-          :class="{ highlighted: suggestion.highlighted }"
+<div class="emoji-input">
+  <slot></slot>
+  <div ref="panel" class="autocomplete-panel" :class="{ hide: suggestions}">
+    <div class="autocomplete-panel-body">
+      <div
+        v-for="(suggestion, index) in suggestions"
+        :key="index"
+        @click.stop.prevent="replace(suggestion.replacement)"
+        class="autocomplete-item"
+        :class="{ highlighted: suggestion.highlighted }"
         >
-          <span v-if="suggestion.img">
-            <img :src="suggestion.img" />
-          </span>
-          <span v-else>{{suggestion.replacement}}</span>
-          <span>{{suggestion.shortcode}}</span>
+        <span v-if="suggestion.img">
+          <img :src="suggestion.img" />
+        </span>
+        <span v-else>{{suggestion.replacement}}</span>
+        <span>{{suggestion.displayText}}</span>
+        <span>{{suggestion.detailText}}</span>
         </div>
-      </div>
     </div>
   </div>
+</div>
 </template>
 
 <script src="./emoji-input.js"></script>
@@ -41,8 +28,21 @@
 @import '../../_variables.scss';
 
 .emoji-input {
-  .form-control {
-    width: 100%;
+  display: flex;
+  flex-direction: column;
+
+  &.hide {
+    display: none
+  }
+
+  .autocomplete-panel {
+    position: absolute;
+    z-index: 9;
+    margin-top: 2px;
+  }
+
+  input, textarea {
+    flex: 1;
   }
 }
 </style>
diff --git a/src/components/emoji-input/suggestor.js b/src/components/emoji-input/suggestor.js
index f1a0d0da85912106f51b48fb17ed3b5af2d96659..54fd7f29ee20011a9d71131d616751426b6cc64d 100644
--- a/src/components/emoji-input/suggestor.js
+++ b/src/components/emoji-input/suggestor.js
@@ -15,24 +15,26 @@ export default function suggest (data) {
 
 function suggestEmoji (emojis) {
   return input => {
-    const shortcode = input.toLowerCase().substr(1)
-    console.log(shortcode)
-    return emojis.filter(emoji => emoji.shortcode.toLowerCase().startsWith(shortcode))
+    const noPrefix = input.toLowerCase().substr(1)
+    return emojis
+      .filter(({ displayText }) => displayText.toLowerCase().startsWith(noPrefix))
   }
 }
 
 function suggestUsers (users) {
   return input => {
-    const shortcode = input.toLowerCase().substr(1)
+    const noPrefix = input.toLowerCase().substr(1)
     return users.filter(
       user =>
-        user.screen_name.toLowerCase().startsWith('@' + shortcode) ||
-        user.name.toLowerCase().startsWith(shortcode)
+        user.screen_name.toLowerCase().startsWith(noPrefix) ||
+        user.name.toLowerCase().startsWith(noPrefix)
+      /* eslint-disable camelcase */
     ).map(({ screen_name, name, profile_image_url_original }) => ({
-      shortcode: screen_name,
-      detail: name,
-      image_url: profile_image_url_original,
+      displayText: screen_name,
+      detailText: name,
+      imageUrl: profile_image_url_original,
       replacement: '@' + screen_name
     }))
+    /* eslint-enable camelcase */
   }
 }
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 1516dd4349ba8875b2e35ebbba77bd26c33159b8..008b821a5759bcce93057ebe8b33650eedc7f3a3 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -83,43 +83,6 @@ const PostStatusForm = {
     }
   },
   computed: {
-    candidates () {
-      const firstchar = this.textAtCaret.charAt(0)
-      if (firstchar === '@') {
-        const query = this.textAtCaret.slice(1).toUpperCase()
-        const matchedUsers = filter(this.users, (user) => {
-          return user.screen_name.toUpperCase().startsWith(query) ||
-            user.name && user.name.toUpperCase().startsWith(query)
-        })
-        if (matchedUsers.length <= 0) {
-          return false
-        }
-        // eslint-disable-next-line camelcase
-        return map(take(matchedUsers, 5), ({screen_name, name, profile_image_url_original}, index) => ({
-          // eslint-disable-next-line camelcase
-          screen_name: `@${screen_name}`,
-          name: name,
-          img: profile_image_url_original,
-          highlighted: index === this.highlighted
-        }))
-      } else if (firstchar === ':') {
-        if (this.textAtCaret === ':') { return }
-        const matchedEmoji = filter(this.emoji.concat(this.customEmoji), (emoji) => emoji.shortcode.startsWith(this.textAtCaret.slice(1)))
-        if (matchedEmoji.length <= 0) {
-          return false
-        }
-        return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}, index) => ({
-          screen_name: `:${shortcode}:`,
-          name: '',
-          utf: utf || '',
-          // eslint-disable-next-line camelcase
-          img: utf ? '' : this.$store.state.instance.server + image_url,
-          highlighted: index === this.highlighted
-        }))
-      } else {
-        return false
-      }
-    },
     users () {
       return this.$store.state.users.users
     },
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 507b14bf7530f4427e4bd826f07a430fdae0a0bc..a8a34265e859765437053be834c568a24351d493 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -32,24 +32,29 @@
         <span v-else>{{ $t('post_status.direct_warning_to_all') }}</span>
       </p>
       <emoji-input
-        :suggest="emojiSuggestor" v-model="newStatus.spoilerText"
         v-if="newStatus.spoilerText || alwaysShowSubject"
+        :suggest="emojiSuggestor"
+        v-model="newStatus.spoilerText"
+        class="form-control"
         >
         <input
 
           type="text"
           :placeholder="$t('post_status.content_warning')"
           v-model="newStatus.spoilerText"
-          classname="form-control"
+          class="form-post-subject"
           />
       </emoji-input>
-      <emoji-input :suggest="emojiUserSuggestor" v-model="newStatus.status">
+      <emoji-input
+        :suggest="emojiUserSuggestor"
+        v-model="newStatus.status"
+        class="form-control"
+        >
         <textarea
           ref="textarea"
           v-model="newStatus.status"
           :placeholder="$t('post_status.default')"
           rows="1"
-          class="form-control"
           @keydown.meta.enter="postStatus(newStatus)"
           @keyup.ctrl.enter="postStatus(newStatus)"
           @drop="fileDrop"
@@ -57,6 +62,7 @@
           @input="resize"
           @paste="paste"
           :disabled="posting"
+          class="form-post-body"
         >
         </textarea>
       </emoji-input>
@@ -251,7 +257,7 @@
     min-height: 1px;
   }
 
-  form textarea.form-control {
+  .form-post-body {
     line-height:16px;
     resize: none;
     overflow: hidden;
@@ -260,7 +266,7 @@
     box-sizing: content-box;
   }
 
-  form textarea.form-control:focus {
+  .form-post-body:focus {
     min-height: 48px;
   }