diff --git a/src/api/emoji_packs.js b/src/api/emoji_packs.js
index 294471911323445ed2509ff349332d2ff3b8a1f4..7e6e2d56c230d5bc88fd033fe9aa472ac69b7aaf 100644
--- a/src/api/emoji_packs.js
+++ b/src/api/emoji_packs.js
@@ -70,6 +70,23 @@ export async function updatePackFile(host, token, args) {
   let data = null
 
   switch (args.action) {
+    case 'add': {
+      const { shortcode, file, fileName } = args
+
+      console.log(file)
+
+      data = fileUpdateFormData({
+        action: 'add',
+        shortcode: shortcode,
+        file: file
+      })
+      if (fileName.trim() !== '') {
+        data.set('filename', fileName)
+      }
+
+      break
+    }
+
     case 'update': {
       const { oldName, newName, newFilename } = args
 
diff --git a/src/views/emoji-packs/components/EmojiPack.vue b/src/views/emoji-packs/components/EmojiPack.vue
index cfda6ca284a40e17deebbdd1eb217f9128fb8a12..003b955da46c0e107b1b6a234801dd338d50dc8e 100644
--- a/src/views/emoji-packs/components/EmojiPack.vue
+++ b/src/views/emoji-packs/components/EmojiPack.vue
@@ -30,6 +30,10 @@
 
     <el-collapse v-model="shownPackEmoji" class="contents-collapse">
       <el-collapse-item :name="name" title="Show pack contents">
+        <new-emoji-uploader v-if="isLocal" :pack-name="name" class="new-emoji-uploader" />
+
+        <b>Manage existing emoji</b>
+
         <single-emoji-editor
           v-for="(file, ename) in pack.files"
           :key="ename"
@@ -88,15 +92,20 @@
   .pack-actions {
     margin-top: 1em;
   }
+
+  .new-emoji-uploader {
+    margin-bottom: 3em;
+  }
 </style>
 
 <script>
 import PropEditingRow from './PropertyEditingRow.vue'
 import SingleEmojiEditor from './SingleEmojiEditor.vue'
+import NewEmojiUploader from './NewEmojiUploader.vue'
 
 export default {
 
-  components: { PropEditingRow, SingleEmojiEditor },
+  components: { PropEditingRow, SingleEmojiEditor, NewEmojiUploader },
   props: {
     name: {
       type: String,
diff --git a/src/views/emoji-packs/components/NewEmojiUploader.vue b/src/views/emoji-packs/components/NewEmojiUploader.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8423e04e40cb7031cba8128097c12366a1c61e35
--- /dev/null
+++ b/src/views/emoji-packs/components/NewEmojiUploader.vue
@@ -0,0 +1,85 @@
+<template>
+  <div>
+    <b>Add new emoji to the pack</b>
+
+    <el-row :gutter="20">
+      <el-col :span="4" class="new-emoji-col">
+        <el-input v-model="shortcode" placeholder="Name/Shortcode" />
+      </el-col>
+
+      <el-col :span="8">
+        <div>
+          <b>Upload a file</b>
+        </div>
+        File name
+        <el-input v-model="customFileName" size="mini" placeholder="Custom file name (optional)"/>
+        <input ref="fileUpload" type="file" accept="image/*" >
+
+        <div class="or">
+          or
+        </div>
+
+        <div>
+          <b>Enter a URL</b>
+        </div>
+        <el-input v-model="imageUploadURL" placeholder="Image URL" />
+
+        <small>
+          (If both are filled, the file is used)
+        </small>
+      </el-col>
+
+      <el-col :span="4" class="new-emoji-col">
+        <el-button :disabled="shortcode.trim() == ''" @click="upload">Upload</el-button>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<style>
+  .new-emoji-col {
+    margin-top: 8em;
+  }
+
+  .or {
+    margin: 1em;
+  }
+</style>
+
+<script>
+export default {
+  props: {
+    packName: {
+      type: String,
+      required: true
+    }
+  },
+
+  data() {
+    return {
+      shortcode: '',
+      imageUploadURL: '',
+      customFileName: ''
+    }
+  },
+
+  methods: {
+    upload() {
+      if (this.$refs.fileUpload.files.length > 0) {
+        this.$store.dispatch('UpdateAndSavePackFile', {
+          action: 'add',
+          packName: this.packName,
+          shortcode: this.shortcode,
+          file: this.$refs.fileUpload.files[0],
+          fileName: this.customFileName
+        }).then(() => {
+          this.shortcode = ''
+          this.imageUploadURL = ''
+
+          this.$store.dispatch('ReloadEmoji')
+        })
+      }
+    }
+  }
+}
+</script>
diff --git a/src/views/emoji-packs/components/SingleEmojiEditor.vue b/src/views/emoji-packs/components/SingleEmojiEditor.vue
index ef1fc7f1c656f8c962089de1aabf18a21b601037..f38f1f30521a88d61d7cf868b0d27826d15cf679 100644
--- a/src/views/emoji-packs/components/SingleEmojiEditor.vue
+++ b/src/views/emoji-packs/components/SingleEmojiEditor.vue
@@ -92,15 +92,21 @@ export default {
       })
     },
     remove() {
-      this.$store.dispatch('UpdateAndSavePackFile', {
-        action: 'remove',
-        packName: this.packName,
-        name: this.name
+      this.$confirm('This will delete the emoji, are you sure?', 'Warning', {
+        confirmButtonText: 'Yes, delete the emoji',
+        cancelButtonText: 'No, leave it be',
+        type: 'warning'
       }).then(() => {
-        this.newName = null
-        this.newFile = null
+        this.$store.dispatch('UpdateAndSavePackFile', {
+          action: 'remove',
+          packName: this.packName,
+          name: this.name
+        }).then(() => {
+          this.newName = null
+          this.newFile = null
 
-        this.$store.dispatch('ReloadEmoji')
+          this.$store.dispatch('ReloadEmoji')
+        })
       })
     }
   }