diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 603de348..ea5a3f58 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -153,7 +153,11 @@ const getStaticEmoji = async ({ store }) => {
     if (res.ok) {
       const values = await res.json()
       const emoji = Object.keys(values).map((key) => {
-        return { shortcode: key, image_url: false, 'utf': values[key] }
+        return {
+          shortcode: key,
+          image_url: false,
+          'replacement': values[key]
+        }
       })
       store.dispatch('setInstanceOption', { name: 'emoji', value: emoji })
     } else {
@@ -174,7 +178,11 @@ 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) => {
-        return { shortcode: key, image_url: values[key].image_url || values[key] }
+        return {
+          shortcode: key,
+          image_url: values[key].image_url || values[key],
+          replacement: `:${key}: `
+        }
       })
       store.dispatch('setInstanceOption', { name: 'customEmoji', value: emoji })
       store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: true })
diff --git a/src/components/emoji-input/emoji-input.js b/src/components/emoji-input/emoji-input.js
index a5bb6eaf..466341c0 100644
--- a/src/components/emoji-input/emoji-input.js
+++ b/src/components/emoji-input/emoji-input.js
@@ -1,15 +1,17 @@
 import Completion from '../../services/completion/completion.js'
-import { take, filter, map } from 'lodash'
+import { take } from 'lodash'
 
 const EmojiInput = {
   props: [
-    'value',
     'placeholder',
+    'suggest',
+    'value',
     'type',
     'classname'
   ],
   data () {
     return {
+      input: undefined,
       highlighted: 0,
       caret: 0
     }
@@ -17,35 +19,46 @@ const EmojiInput = {
   computed: {
     suggestions () {
       const firstchar = this.textAtCaret.charAt(0)
-      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) => ({
-          shortcode: `:${shortcode}:`,
-          utf: utf || '',
-          // eslint-disable-next-line camelcase
-          img: utf ? '' : this.$store.state.instance.server + image_url,
-          highlighted: index === this.highlighted
-        }))
-      } else {
+      if (this.textAtCaret === firstchar) { return }
+      const matchedSuggestions = this.suggest(this.textAtCaret)
+      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
+      }))
     },
     textAtCaret () {
       return (this.wordAtCaret || {}).word || ''
     },
     wordAtCaret () {
-      const word = Completion.wordAtPosition(this.value, this.caret - 1) || {}
-      return word
+      if (this.value && this.caret) {
+        const word = Completion.wordAtPosition(this.value, this.caret - 1) || {}
+        return word
+      }
     },
-    emoji () {
-      return this.$store.state.instance.emoji || []
-    },
-    customEmoji () {
-      return this.$store.state.instance.customEmoji || []
+  },
+  mounted () {
+    const slots = this.$slots.default
+    if (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)
+    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)
     }
   },
   methods: {
@@ -54,23 +67,21 @@ const EmojiInput = {
       this.$emit('input', newValue)
       this.caret = 0
     },
-    replaceEmoji (e) {
+    replaceText () {
       const len = this.suggestions.length || 0
-      if (this.textAtCaret === ':' || e.ctrlKey) { return }
+      if (this.textAtCaret.length === 1) { return }
       if (len > 0) {
-        e.preventDefault()
-        const emoji = this.suggestions[this.highlighted]
-        const replacement = emoji.utf || (emoji.shortcode + ' ')
+        const suggestion = this.suggestions[this.highlighted]
+        const replacement = suggestion.replacement
         const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement)
         this.$emit('input', newValue)
         this.caret = 0
         this.highlighted = 0
       }
     },
-    cycleBackward (e) {
+    cycleBackward () {
       const len = this.suggestions.length || 0
       if (len > 0) {
-        e.preventDefault()
         this.highlighted -= 1
         if (this.highlighted < 0) {
           this.highlighted = this.suggestions.length - 1
@@ -79,11 +90,9 @@ const EmojiInput = {
         this.highlighted = 0
       }
     },
-    cycleForward (e) {
+    cycleForward () {
       const len = this.suggestions.length || 0
       if (len > 0) {
-        if (e.shiftKey) { return }
-        e.preventDefault()
         this.highlighted += 1
         if (this.highlighted >= len) {
           this.highlighted = 0
@@ -92,13 +101,33 @@ const EmojiInput = {
         this.highlighted = 0
       }
     },
-    onKeydown (e) {
+    onKeyDown (e) {
+      this.setCaret(e)
       e.stopPropagation()
+
+      const { ctrlKey, shiftKey, key } = e
+      if (key === 'Tab') {
+        if (shiftKey) {
+          this.cycleBackward()
+        } else {
+          this.cycleForward()
+        }
+      }
+      if (key === 'ArrowUp') {
+        this.cycleBackward()
+      } else if (key === 'ArrowDown') {
+        this.cycleForward()
+      }
+      if (key === 'Enter') {
+        if (!ctrlKey) {
+          this.replaceText()
+        }
+      }
     },
     onInput (e) {
       this.$emit('input', e.target.value)
     },
-    setCaret ({target: {selectionStart}}) {
+    setCaret ({ target: { selectionStart, value } }) {
       this.caret = selectionStart
     }
   }
diff --git a/src/components/emoji-input/emoji-input.vue b/src/components/emoji-input/emoji-input.vue
index 338b77cd..eec33d1a 100644
--- a/src/components/emoji-input/emoji-input.vue
+++ b/src/components/emoji-input/emoji-input.vue
@@ -1,23 +1,6 @@
 <template>
   <div class="emoji-input">
-    <input
-      v-if="type !== 'textarea'"
-      :class="classname"
-      :type="type"
-      :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"
-    />
-    <textarea
-      v-else
+    <slot
       :class="classname"
       :value="value"
       :placeholder="placeholder"
@@ -30,21 +13,22 @@
       @keydown.shift.tab="cycleBackward"
       @keydown.tab="cycleForward"
       @keydown.enter="replaceEmoji"
-    ></textarea>
+      >
+    </slot>
     <div class="autocomplete-panel" v-if="suggestions">
       <div class="autocomplete-panel-body">
         <div
-          v-for="(emoji, index) in suggestions"
+          v-for="(suggestion, index) in suggestions"
           :key="index"
-          @click="replace(emoji.utf || (emoji.shortcode + ' '))"
+          @click="replace(suggestion.replacement)"
           class="autocomplete-item"
-          :class="{ highlighted: emoji.highlighted }"
+          :class="{ highlighted: suggestion.highlighted }"
         >
-          <span v-if="emoji.img">
-            <img :src="emoji.img" />
+          <span v-if="suggestion.img">
+            <img :src="suggestion.img" />
           </span>
-          <span v-else>{{emoji.utf}}</span>
-          <span>{{emoji.shortcode}}</span>
+          <span v-else>{{suggestion.replacement}}</span>
+          <span>{{suggestion.shortcode}}</span>
         </div>
       </div>
     </div>
diff --git a/src/components/emoji-input/suggestor.js b/src/components/emoji-input/suggestor.js
new file mode 100644
index 00000000..f1a0d0da
--- /dev/null
+++ b/src/components/emoji-input/suggestor.js
@@ -0,0 +1,38 @@
+export default function suggest (data) {
+  return input => {
+    const trimmed = input.trim()
+    const firstChar = trimmed[0]
+    console.log(`'${trimmed}'`, firstChar, firstChar === ':')
+    if (firstChar === ':' && data.emoji) {
+      return suggestEmoji(data.emoji)(trimmed)
+    }
+    if (firstChar === '@' && data.users) {
+      return suggestUsers(data.users)(trimmed)
+    }
+    return []
+  }
+}
+
+function suggestEmoji (emojis) {
+  return input => {
+    const shortcode = input.toLowerCase().substr(1)
+    console.log(shortcode)
+    return emojis.filter(emoji => emoji.shortcode.toLowerCase().startsWith(shortcode))
+  }
+}
+
+function suggestUsers (users) {
+  return input => {
+    const shortcode = input.toLowerCase().substr(1)
+    return users.filter(
+      user =>
+        user.screen_name.toLowerCase().startsWith('@' + shortcode) ||
+        user.name.toLowerCase().startsWith(shortcode)
+    ).map(({ screen_name, name, profile_image_url_original }) => ({
+      shortcode: screen_name,
+      detail: name,
+      image_url: profile_image_url_original,
+      replacement: '@' + screen_name
+    }))
+  }
+}
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index cbd2024a..1516dd43 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -5,6 +5,7 @@ import EmojiInput from '../emoji-input/emoji-input.vue'
 import fileTypeService from '../../services/file_type/file_type.service.js'
 import Completion from '../../services/completion/completion.js'
 import { take, filter, reject, map, uniqBy } from 'lodash'
+import suggestor from '../emoji-input/suggestor.js'
 
 const buildMentionsString = ({user, attentions}, currentUser) => {
   let allAttentions = [...attentions]
@@ -119,13 +120,6 @@ const PostStatusForm = {
         return false
       }
     },
-    textAtCaret () {
-      return (this.wordAtCaret || {}).word || ''
-    },
-    wordAtCaret () {
-      const word = Completion.wordAtPosition(this.newStatus.status, this.caret - 1) || {}
-      return word
-    },
     users () {
       return this.$store.state.users.users
     },
@@ -138,6 +132,21 @@ const PostStatusForm = {
             : this.$store.state.config.minimalScopesMode
       return !minimalScopesMode
     },
+    emojiUserSuggestor () {
+      return suggestor({
+        emoji: [
+          ...this.$store.state.instance.emoji,
+          ...this.$store.state.instance.customEmoji
+        ],
+        users: this.$store.state.users.users
+      })
+    },
+    emojiSuggestor () {
+      suggestor({ emoji: [
+        ...this.$store.state.instance.emoji,
+        ...this.$store.state.instance.customEmoji
+      ]})
+    },
     emoji () {
       return this.$store.state.instance.emoji || []
     },
@@ -188,57 +197,6 @@ const PostStatusForm = {
     }
   },
   methods: {
-    replace (replacement) {
-      this.newStatus.status = Completion.replaceWord(this.newStatus.status, this.wordAtCaret, replacement)
-      const el = this.$el.querySelector('textarea')
-      el.focus()
-      this.caret = 0
-    },
-    replaceCandidate (e) {
-      const len = this.candidates.length || 0
-      if (this.textAtCaret === ':' || e.ctrlKey) { return }
-      if (len > 0) {
-        e.preventDefault()
-        const candidate = this.candidates[this.highlighted]
-        const replacement = candidate.utf || (candidate.screen_name + ' ')
-        this.newStatus.status = Completion.replaceWord(this.newStatus.status, this.wordAtCaret, replacement)
-        const el = this.$el.querySelector('textarea')
-        el.focus()
-        this.caret = 0
-        this.highlighted = 0
-      }
-    },
-    cycleBackward (e) {
-      const len = this.candidates.length || 0
-      if (len > 0) {
-        e.preventDefault()
-        this.highlighted -= 1
-        if (this.highlighted < 0) {
-          this.highlighted = this.candidates.length - 1
-        }
-      } else {
-        this.highlighted = 0
-      }
-    },
-    cycleForward (e) {
-      const len = this.candidates.length || 0
-      if (len > 0) {
-        if (e.shiftKey) { return }
-        e.preventDefault()
-        this.highlighted += 1
-        if (this.highlighted >= len) {
-          this.highlighted = 0
-        }
-      } else {
-        this.highlighted = 0
-      }
-    },
-    onKeydown (e) {
-      e.stopPropagation()
-    },
-    setCaret ({target: {selectionStart}}) {
-      this.caret = selectionStart
-    },
     postStatus (newStatus) {
       if (this.posting) { return }
       if (this.submitDisabled) { return }
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 25c5284f..507b14bf 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -31,32 +31,35 @@
         <span v-if="safeDMEnabled">{{ $t('post_status.direct_warning_to_first_only') }}</span>
         <span v-else>{{ $t('post_status.direct_warning_to_all') }}</span>
       </p>
-      <EmojiInput
+      <emoji-input
+        :suggest="emojiSuggestor" v-model="newStatus.spoilerText"
         v-if="newStatus.spoilerText || alwaysShowSubject"
-        type="text"
-        :placeholder="$t('post_status.content_warning')"
-        v-model="newStatus.spoilerText"
-        classname="form-control"
-      />
-      <textarea
-        ref="textarea"
-        @click="setCaret"
-        @keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control"
-        @keydown="onKeydown"
-        @keydown.down="cycleForward"
-        @keydown.up="cycleBackward"
-        @keydown.shift.tab="cycleBackward"
-        @keydown.tab="cycleForward"
-        @keydown.enter="replaceCandidate"
-        @keydown.meta.enter="postStatus(newStatus)"
-        @keyup.ctrl.enter="postStatus(newStatus)"
-        @drop="fileDrop"
-        @dragover.prevent="fileDrag"
-        @input="resize"
-        @paste="paste"
-        :disabled="posting"
-      >
-      </textarea>
+        >
+        <input
+
+          type="text"
+          :placeholder="$t('post_status.content_warning')"
+          v-model="newStatus.spoilerText"
+          classname="form-control"
+          />
+      </emoji-input>
+      <emoji-input :suggest="emojiUserSuggestor" v-model="newStatus.status">
+        <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"
+          @dragover.prevent="fileDrag"
+          @input="resize"
+          @paste="paste"
+          :disabled="posting"
+        >
+        </textarea>
+      </emoji-input>
       <div class="visibility-tray">
         <div class="text-format" v-if="formattingOptionsEnabled">
           <label for="post-content-type" class="select">
@@ -77,21 +80,6 @@
           :onScopeChange="changeVis"/>
       </div>
     </div>
-    <div class="autocomplete-panel" v-if="candidates">
-        <div class="autocomplete-panel-body">
-          <div
-            v-for="(candidate, index) in candidates"
-            :key="index"
-            @click="replace(candidate.utf || (candidate.screen_name + ' '))"
-            class="autocomplete-item"
-            :class="{ highlighted: candidate.highlighted }"
-          >
-            <span v-if="candidate.img"><img :src="candidate.img" /></span>
-            <span v-else>{{candidate.utf}}</span>
-            <span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
-          </div>
-        </div>
-      </div>
       <div class='form-bottom'>
         <media-upload ref="mediaUpload" @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="uploadFailed" :drop-files="dropFiles"></media-upload>
 
diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
index ae36e5e8..ca7c23ec 100644
--- a/src/components/user_settings/user_settings.js
+++ b/src/components/user_settings/user_settings.js
@@ -12,6 +12,7 @@ import MuteCard from '../mute_card/mute_card.vue'
 import SelectableList from '../selectable_list/selectable_list.vue'
 import ProgressButton from '../progress_button/progress_button.vue'
 import EmojiInput from '../emoji-input/emoji-input.vue'
+import suggestor from '../emoji-input/suggestor.js'
 import Autosuggest from '../autosuggest/autosuggest.vue'
 import Importer from '../importer/importer.vue'
 import Exporter from '../exporter/exporter.vue'
@@ -81,6 +82,21 @@ const UserSettings = {
     user () {
       return this.$store.state.users.currentUser
     },
+    emojiUserSuggestor () {
+      return suggestor({
+        emoji: [
+          ...this.$store.state.instance.emoji,
+          ...this.$store.state.instance.customEmoji
+        ],
+        users: this.$store.state.users.users
+      })
+    },
+    emojiSuggestor () {
+      suggestor({ emoji: [
+        ...this.$store.state.instance.emoji,
+        ...this.$store.state.instance.customEmoji
+      ]})
+    },
     pleromaBackend () {
       return this.$store.state.instance.pleromaBackend
     },
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index 20b10979..d3d333bd 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -22,18 +22,20 @@
           <div class="setting-item" >
             <h2>{{$t('settings.name_bio')}}</h2>
             <p>{{$t('settings.name')}}</p>
-            <EmojiInput
-              type="text"
-              v-model="newName"
-              id="username"
-              classname="name-changer"
-            />
+            <emoji-input :suggest="emojiSuggestor" v-model="newName">
+              <input
+                v-model="newName"
+                id="username"
+                classname="name-changer"
+              />
+            </emoji-input>
             <p>{{$t('settings.bio')}}</p>
-            <EmojiInput
-              type="textarea"
-              v-model="newBio"
-              classname="bio"
-            />
+            <emoji-input :suggest="emojiUserSuggestor" v-model="newBio">
+              <textarea
+                v-model="newBio"
+                classname="bio"
+                />
+            </emoji-input>
             <p>
               <input type="checkbox" v-model="newLocked" id="account-locked">
               <label for="account-locked">{{$t('settings.lock_account_description')}}</label>