diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js
index 76934e53..c0391f6c 100644
--- a/src/components/emoji_picker/emoji_picker.js
+++ b/src/components/emoji_picker/emoji_picker.js
@@ -51,6 +51,7 @@ const EmojiPicker = {
     onEmoji (emoji) {
       const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement
       this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen })
+      this.$store.commit('emojiUsed', emoji)
     },
     onWheel (e) {
       e.preventDefault()
@@ -96,6 +97,7 @@ const EmojiPicker = {
       )
     },
     emojis () {
+      const recentEmojis = this.$store.getters.recentEmojis
       const standardEmojis = this.$store.state.instance.emoji || []
       const customEmojis = this.sortedEmoji
       const emojiPacks = []
@@ -108,6 +110,15 @@ const EmojiPicker = {
         })
       })
       return [
+        {
+          id: 'recent',
+          text: this.$t('emoji.recent'),
+          first: {
+            imageUrl: '',
+            replacement: '🕒',
+          },
+          emojis: this.filterByKeyword(recentEmojis)
+        },
         {
           id: 'standard',
           text: this.$t('emoji.unicode'),
diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js
index 06e21f6e..75ab252e 100644
--- a/src/components/emoji_reactions/emoji_reactions.js
+++ b/src/components/emoji_reactions/emoji_reactions.js
@@ -3,6 +3,11 @@ import UserListPopover from '../user_list_popover/user_list_popover.vue'
 
 const EMOJI_REACTION_COUNT_CUTOFF = 12
 
+const findEmojiByReplacement = (state, replacement) => {
+  const allEmojis = state.instance.emoji.concat(state.instance.customEmoji)
+  return allEmojis.find(emoji => emoji.replacement === replacement)
+}
+
 const EmojiReactions = {
   name: 'EmojiReactions',
   components: {
@@ -54,6 +59,8 @@ const EmojiReactions = {
     },
     reactWith (emoji) {
       this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
+      const emojiObject = findEmojiByReplacement(this.$store.state, emoji)
+      this.$store.commit('emojiUsed', emojiObject)
     },
     unreact (emoji) {
       this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue
index 3fe81f77..599611b2 100644
--- a/src/components/emoji_reactions/emoji_reactions.vue
+++ b/src/components/emoji_reactions/emoji_reactions.vue
@@ -18,7 +18,6 @@
             :src="reaction.url"
             :title="reaction.name"
             class="reaction-emoji"
-            width="2.55em"
             height="2.55em"
           >
           {{ reaction.count }}
@@ -50,6 +49,7 @@
   display: flex;
   margin-top: 0.25em;
   flex-wrap: wrap;
+  container-type: inline-size;
 }
 
 .unicode-emoji {
@@ -65,7 +65,8 @@
   justify-content: center;
   box-sizing: border-box;
   .reaction-emoji {
-    width: 2.55em !important;
+    width: auto;
+    max-width: 96cqw;
     height: 2.55em !important;
     margin-right: 0.25em;
   }
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index b7c66fc7..f7fef499 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -54,6 +54,14 @@ const pxStringToNumber = (str) => {
   return Number(str.substring(0, str.length - 2))
 }
 
+const deleteDraft = (draftKey) => {
+	const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
+
+	delete draftData[draftKey];
+
+	localStorage.setItem('drafts', JSON.stringify(draftData));
+}
+
 const PostStatusForm = {
   props: [
     'statusId',
@@ -161,6 +169,34 @@ const PostStatusForm = {
       }
     }
 
+    let draftKey = 'status';
+    if (this.replyTo) {
+      draftKey = 'reply:' + this.replyTo;
+    } else if (this.quoteId) {
+      draftKey = 'quote:' + this.quoteId;
+    }
+
+    const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[draftKey];
+
+    if (draft) {
+      statusParams = {
+        spoilerText: draft.data.spoilerText,
+        status: draft.data.status,
+        sensitiveIfSubject,
+        nsfw: draft.data.nsfw,
+        files: draft.data.files,
+        poll: draft.data.poll,
+        mediaDescriptions: draft.data.mediaDescriptions,
+        visibility: draft.data.visibility,
+        language: draft.data.language,
+        contentType: draft.data.contentType
+      }
+
+      if (draft.data.poll) {
+        this.togglePollForm();
+      }
+    }
+
     return {
       dropFiles: [],
       uploadingFiles: false,
@@ -280,6 +316,7 @@ const PostStatusForm = {
     statusChanged () {
       this.autoPreview()
       this.updateIdempotencyKey()
+      this.saveDraft()
     },
     clearStatus () {
       const newStatus = this.newStatus
@@ -401,8 +438,38 @@ const PostStatusForm = {
       }).finally(() => {
         this.previewLoading = false
       })
+
+      let draftKey = 'status';
+      if (this.replyTo) {
+        draftKey = 'reply:' + this.replyTo;
+      } else if (this.quoteId) {
+        draftKey = 'quote:' + this.quoteId;
+      }
+      deleteDraft(draftKey)
     },
     debouncePreviewStatus: debounce(function () { this.previewStatus() }, 500),
+    saveDraft() {
+      const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
+
+      let draftKey = 'status';
+      if (this.replyTo) {
+        draftKey = 'reply:' + this.replyTo;
+      } else if (this.quoteId) {
+        draftKey = 'quote:' + this.quoteId;
+      }
+
+      if (this.newStatus.status || this.newStatus.spoilerText || this.newStatus.files.length > 0 || this.newStatus.poll.length > 0) {
+          draftData[draftKey] = {
+          updatedAt: new Date(),
+          data: this.newStatus,
+        };
+
+        localStorage.setItem('drafts', JSON.stringify(draftData));
+
+      } else {
+        deleteDraft(draftKey);
+      }
+    },
     autoPreview () {
       if (!this.preview) return
       this.previewLoading = true
diff --git a/src/components/rich_content/rich_content.scss b/src/components/rich_content/rich_content.scss
index db08ef1e..63df7d74 100644
--- a/src/components/rich_content/rich_content.scss
+++ b/src/components/rich_content/rich_content.scss
@@ -50,7 +50,6 @@
 
   .emoji {
     display: inline-block;
-    width: var(--emoji-size, 32px);
     height: var(--emoji-size, 32px);
   }
 
diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss
index 230a27ac..d618f65e 100644
--- a/src/components/status_body/status_body.scss
+++ b/src/components/status_body/status_body.scss
@@ -22,21 +22,18 @@
 
   ._mfm_x2_ {
     .emoji {
-      width: 100px;
       height: 100px;
     }
   }
 
   ._mfm_x3_ {
     .emoji {
-      width: 150px;
       height: 150px;
     }
   }
 
   ._mfm_x4_ {
     .emoji {
-      width: 200px;
       height: 200px;
     }
   }
diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue
index ab13141e..62acf5ac 100644
--- a/src/components/status_content/status_content.vue
+++ b/src/components/status_content/status_content.vue
@@ -71,7 +71,7 @@
 
   img, video {
     &.emoji {
-      width: 50px;
+      max-width: 100%;
       height: 50px;
     }
   }
@@ -89,7 +89,6 @@
       animation: none !important;
     }
     .emoji {
-      width: 32px !important;
       height: 32px !important;
     }
   }
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 46890345..92618047 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -86,7 +86,8 @@
         "load_all_hint": "Loaded first {saneAmount} emoji, loading all emoji may cause performance issues.",
         "search_emoji": "Search for an emoji",
         "stickers": "Stickers",
-        "unicode": "Unicode emoji"
+        "unicode": "Unicode emoji",
+        "recent": "Recently used"
     },
     "errors": {
         "storage_unavailable": "Pleroma could not access browser storage. Your login or your local settings won't be saved and you might encounter unexpected issues. Try enabling cookies."
diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js
index 735a10de..a9a9d307 100644
--- a/src/lib/persisted_state.js
+++ b/src/lib/persisted_state.js
@@ -19,7 +19,8 @@ const saveImmedeatelyActions = [
   'setOption',
   'setClientData',
   'setToken',
-  'clearToken'
+  'clearToken',
+  'emojiUsed',
 ]
 
 const defaultStorage = (() => {
diff --git a/src/main.js b/src/main.js
index 01a44c96..cad887e5 100644
--- a/src/main.js
+++ b/src/main.js
@@ -22,6 +22,7 @@ import announcementsModule from './modules/announcements.js'
 import editStatusModule from './modules/editStatus.js'
 import statusHistoryModule from './modules/statusHistory.js'
 import tagModule from './modules/tags.js'
+import recentEmojisModule from './modules/recentEmojis.js'
 
 import { createI18n } from 'vue-i18n'
 
@@ -47,7 +48,8 @@ const persistedStateOptions = {
   paths: [
     'config',
     'users.lastLoginName',
-    'oauth'
+    'oauth',
+    'recentEmojis.emojis',
   ]
 };
 
@@ -98,7 +100,8 @@ const persistedStateOptions = {
       announcements: announcementsModule,
       editStatus: editStatusModule,
       statusHistory: statusHistoryModule,
-      tags: tagModule
+      tags: tagModule,
+      recentEmojis: recentEmojisModule,
     },
     plugins,
     strict: false // Socket modifies itself, let's ignore this for now.
diff --git a/src/modules/api.js b/src/modules/api.js
index 8de1449b..352d7774 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -5,15 +5,25 @@ import { map } from 'lodash'
 const retryTimeout = (multiplier) => 1000 * multiplier
 
 const isVisible = (store, message, visibility) => {
-  if (visibility === 'all') {
+  if (visibility == 'all') {
     return true
-  } else if (visibility === 'following') {
-    return store.getters.relationship(message.in_reply_to_user_id).following
-  } else if (visibility === 'self') {
+  }
+
+  if (visibility == 'following') {
+    if (message.in_reply_to_user_id === null) {
+      return true
+    } else {
+      return store.getters.relationship(message.in_reply_to_user_id).following
+    }
+  }
+
+  if (visibility == 'self') {
     return message.in_reply_to_user_id === store.rootState.users.currentUser.id
   }
+
   return false
 }
+
 const api = {
   state: {
     retryMultiplier: 1,
diff --git a/src/modules/recentEmojis.js b/src/modules/recentEmojis.js
new file mode 100644
index 00000000..baab1c52
--- /dev/null
+++ b/src/modules/recentEmojis.js
@@ -0,0 +1,50 @@
+// each row is 7 emojis, 6 rows chosen arbitrarily. i don't think more than
+// that are going to be useful.
+const RECENT_MAX = 7 * 6
+
+const defaultState = {
+  emojis: [],
+}
+
+const recentEmojis = {
+  state: defaultState,
+
+  mutations: {
+    emojiUsed ({ emojis }, emoji) {
+      if (emoji.displayText === undefined || emoji.displayText === null) {
+        console.error('emojiUsed was called with a bad emoji object: ', emoji)
+        return
+      } else if (emoji.displayText.includes('@')) {
+        console.error('emojiUsed was called with a remote emoji: ', emoji)
+        return
+      }
+
+      const i = emojis.indexOf(emoji.displayText)
+
+      if (i === -1) {
+        // not in `emojis` yet, insert and truncate if necessary
+        const newLength = emojis.unshift(emoji.displayText)
+        if (newLength > RECENT_MAX) {
+          emojis.pop()
+        }
+      } else if (i !== 0) {
+        // emoji is already in `emojis` but needs to be bumped to the top
+        emojis.splice(i, 1)
+        emojis.unshift(emoji.displayText)
+      }
+    },
+  },
+
+  getters: {
+    recentEmojis: (state, getters, rootState) => state.emojis.reduce((objects, displayText) => {
+      const allEmojis = rootState.instance.emoji.concat(rootState.instance.customEmoji)
+      let emojiObject = allEmojis.find(emoji => emoji.displayText === displayText)
+      if (emojiObject !== undefined) {
+        objects.push(emojiObject)
+      }
+      return objects
+    }, []),
+  },
+}
+
+export default recentEmojis