diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 01aeeb68..be2ecc2f 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -1,10 +1,9 @@
 import statusPoster from '../../services/status_poster/status_poster.service.js'
 import MediaUpload from '../media_upload/media_upload.vue'
 import fileTypeService from '../../services/file_type/file_type.service.js'
-import Tribute from '../../../node_modules/tributejs/src/Tribute.js'
-require('../../../node_modules/tributejs/scss/tribute.scss')
+import Completion from '../../services/completion/completion.js'
 
-import { merge, reject, map, uniqBy } from 'lodash'
+import { take, filter, reject, map, uniqBy } from 'lodash'
 
 const buildMentionsString = ({user, attentions}, currentUser) => {
   let allAttentions = [...attentions]
@@ -21,51 +20,6 @@ const buildMentionsString = ({user, attentions}, currentUser) => {
   return mentions.join(' ') + ' '
 }
 
-const defaultCollection = {
-  // symbol that starts the lookup
-  trigger: '@',
-
-  // element to target for @mentions
-  iframe: null,
-
-  // class added in the flyout menu for active item
-  selectClass: 'highlight',
-
-  // function called on select that returns the content to insert
-  selectTemplate: function (item) {
-    return '@' + item.original.screen_name
-  },
-
-  // template for displaying item in menu
-  menuItemTemplate: function (item) {
-    return `<img src="${item.original.profile_image_url}"></img> <div class='name'>${item.string}</div>`
-  },
-
-  // template for when no match is found (optional),
-  // If no template is provided, menu is hidden.
-  noMatchTemplate: null,
-
-  // specify an alternative parent container for the menu
-  menuContainer: document.body,
-
-  // column to search against in the object (accepts function or string)
-  lookup: ({name, screen_name}) => `${name} (@${screen_name})`, // eslint-disable-line camelcase
-
-  // column that contains the content to insert by default
-  fillAttr: 'screen_name',
-
-  // REQUIRED: array of objects to match
-  values: [],
-
-  // specify whether a space is required before the trigger character
-  requireLeadingSpace: true,
-
-  // specify whether a space is allowed in the middle of mentions
-  allowSpaces: false
-}
-
-const tribute = new Tribute({ collection: [] })
-
 const PostStatusForm = {
   props: [
     'replyTo',
@@ -89,30 +43,37 @@ const PostStatusForm = {
       newStatus: {
         status: statusText,
         files: []
-      }
+      },
+      caret: 0
     }
   },
   computed: {
+    candidates () {
+      if (this.textAtCaret.charAt(0) === '@') {
+        const matchedUsers = filter(this.users, (user) => (user.name + user.screen_name).match(this.textAtCaret.slice(1)))
+        return map(take(matchedUsers, 5), ({screen_name, name}) => screen_name)
+      } else {
+        return ['nothing']
+      }
+    },
+    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
-    },
-    completions () {
-      let users = this.users
-      users = merge({values: users}, defaultCollection)
-      return [users]
     }
   },
-  watch: {
-    completions () {
-      tribute.collection = this.completions
-    }
-  },
-  mounted () {
-    const textarea = this.$el.querySelector('textarea')
-    tribute.collection = this.completions
-    tribute.attach(textarea)
-  },
   methods: {
+    replace (replacement) {
+      this.newStatus.status = Completion.replaceWord(this.newStatus.status, this.wordAtCaret, replacement)
+    },
+    setCaret ({target: {selectionStart}}) {
+      this.caret = selectionStart
+    },
     postStatus (newStatus) {
       statusPoster.postStatus({
         status: newStatus.status,
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 07280a41..12a9c88a 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -2,7 +2,7 @@
   <div class="post-status-form">
     <form @submit.prevent="postStatus(newStatus)">
       <div class="form-group" >
-        <textarea v-model="newStatus.status" placeholder="Just landed in L.A." rows="3" class="form-control" @keyup.meta.enter="postStatus(newStatus)" @keyup.ctrl.enter="postStatus(newStatus)" @drop="fileDrop" @dragover.prevent="fileDrag"></textarea>
+        <textarea @click="setCaret" @keyup="setCaret" v-model="newStatus.status" placeholder="Just landed in L.A." rows="3" class="form-control" @keyup.meta.enter="postStatus(newStatus)" @keyup.ctrl.enter="postStatus(newStatus)" @drop="fileDrop" @dragover.prevent="fileDrag"></textarea>
       </div>
       <div class="attachments">
         <div class="attachment" v-for="file in newStatus.files">
@@ -13,6 +13,13 @@
           <a v-if="type(file) === 'unknown'" :href="file.image">{{file.url}}</a>
         </div>
       </div>
+      <div>
+        <h1>Word</h1>
+        <h2>{{textAtCaret}}</h2>
+        <h1>Candidates</h1>
+
+        <h3 v-for="candidate in candidates" @click="replace('@' + candidate)">{{candidate}}</h3>
+      </div>
       <div class='form-bottom'>
         <media-upload @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="enableSubmit" :drop-files="dropFiles"></media-upload>
         <button :disabled="submitDisabled" type="submit" class="btn btn-default base05 base01-background">Submit</button>