diff --git a/src/components/ImageUpload.vue b/src/components/ImageUpload.vue
index 3329ea003c58014e0f1fd4d053b3c1741027d20f..5f0a996c2f788f4c0e1a3239fce3428da11f983a 100644
--- a/src/components/ImageUpload.vue
+++ b/src/components/ImageUpload.vue
@@ -1,46 +1,5 @@
 <template>
   <div>
-    <div class="m-2">
-      Click on the image's preview to remove the entry from uploading
-    </div>
-    <div
-      v-for="(image, id) in imageList"
-      :key="image.url"
-      class="flex flex-row"
-    >
-      <img
-        :src="image.url"
-        @click="removeImage(id)"
-        alt=""
-        class="max-w-xs m-4"
-      />
-      <form :id="'image-update-form-' + id" class="flex flex-col">
-        <input
-          type="text"
-          placeholder="Source"
-          name="source"
-          class="inputBox inputPayload"
-        />
-        <input
-          type="text"
-          placeholder="Parent"
-          name="parent"
-          class="inputBox inputPayload"
-        />
-        <textarea
-          type="text"
-          placeholder="Commentary"
-          name="commentary"
-          class="inputBox inputPayload"
-        />
-        <textarea
-          type="text"
-          placeholder="Commentary translation"
-          name="commentary_translation"
-          class="inputBox inputPayload"
-        />
-      </form>
-    </div>
     <div
       class="h-48 m-4 border-dotted border-4 border-gray-400"
       @drop.prevent="
@@ -79,7 +38,7 @@
         Please upload PNG, JPEG or GIF image only.
       </WarningBox>
       <button
-        v-show="imageList.length"
+        v-show="imageUploadList.length"
         @click="submitImage"
         class="
           border border-gray-500
@@ -95,12 +54,68 @@
         Submit
       </button>
     </div>
+    <strong class="m-2" v-show="imageUploadList.length">
+      Click on the image's preview to remove the entry from uploading
+    </strong>
+    <div
+      v-for="(image, id) in imageUploadList"
+      :key="image.url"
+      class="flex flex-row divide-y-4 divide-gray-600 divide-dashed"
+    >
+      <img
+        :src="image.url"
+        @click="removeImage(id)"
+        alt=""
+        class="max-w-xs m-4"
+      />
+      <div class="flex flex-col flex-1">
+        <form :id="'image-update-form-' + id" class="flex flex-col flex-1">
+          <input
+            type="text"
+            placeholder="Source"
+            name="source"
+            autocomplete="off"
+            class="inputBox inputPayload"
+          />
+          <input
+            type="text"
+            placeholder="Parent"
+            name="parent"
+            autocomplete="off"
+            class="inputBox inputPayload"
+          />
+          <textarea
+            type="text"
+            placeholder="Commentary"
+            name="commentary"
+            autocomplete="off"
+            class="inputBox inputPayload resize-none flex-1"
+          />
+          <textarea
+            type="text"
+            placeholder="Commentary translation"
+            name="commentary_translation"
+            autocomplete="off"
+            class="inputBox inputPayload resize-none flex-1"
+          />
+        </form>
+        <input
+          type="text"
+          placeholder="Tags"
+          name="tags"
+          autocomplete="off"
+          :id="'tagsInput' + id"
+          class="inputBox inputPayload"
+        />
+      </div>
+    </div>
   </div>
 </template>
 
 <script>
 import { mapActions, mapState } from "vuex";
 import WarningBox from "@/components/WarningBox.vue";
+import paths from "@/assets/js/paths.js";
 
 export default {
   components: {
@@ -108,7 +123,7 @@ export default {
   },
   data: () => {
     return {
-      imageList: [],
+      imageUploadList: [],
       invalidType: false,
     };
   },
@@ -118,17 +133,18 @@ export default {
   methods: {
     updateWrapper(event) {
       this.checkImageType(event)
-        ? this.updateImageList(event)
+        ? this.updateImageUploadList(event)
         : (this.$refs.imageInput.value = "");
     },
-    updateImageList(event) {
-      this.imageList = this.imageList.concat(
+    updateImageUploadList(event) {
+      this.imageUploadList = this.imageUploadList.concat(
         event
-          ? this.addMetadata([...event.dataTransfer.files])
-          : this.addMetadata([...this.$refs.imageInput.files])
+          ? this.mapFilesToPreviews([...event.dataTransfer.files])
+          : this.mapFilesToPreviews([...this.$refs.imageInput.files])
       );
     },
-    addMetadata(files) {
+    // Return an array of objects whose fields are the image's file and the preview's URL
+    mapFilesToPreviews(files) {
       let tempArray = [];
       files.forEach((file) => {
         tempArray.push({ file, url: URL.createObjectURL(file) });
@@ -137,11 +153,11 @@ export default {
     },
     checkImageType(event) {
       const acceptedTypes = ["image/png", "image/jpeg", "image/gif"];
-      let imageList = event
+      let imageUploadList = event
         ? [...event.dataTransfer.files]
         : [...this.$refs.imageInput.files];
-      for (let i = 0; i < imageList.length; i++) {
-        if (!acceptedTypes.includes(imageList[i].type)) {
+      for (let i = 0; i < imageUploadList.length; i++) {
+        if (!acceptedTypes.includes(imageUploadList[i].type)) {
           this.invalidType = true;
           return false;
         }
@@ -150,21 +166,22 @@ export default {
       return true;
     },
     removeImage(id) {
-      this.imageList.splice(id, 1);
+      this.imageUploadList.splice(id, 1);
     },
     async submitImage() {
       let formData = new FormData();
-      for (let i = 0; i < this.imageList.length; i++) {
-        formData.set("image", this.imageList[i].file);
-        let data = await this.postImage(formData);
-        this.updateImage(
+      for (let i = 0; i < this.imageUploadList.length; i++) {
+        formData.set("image", this.imageUploadList[i].file);
+        let data = await this.postImageFile(formData);
+        this.setImageInfo(
           data.snowflake,
           this.serializeForm("image-update-form-" + i)
         );
+        this.setImageTags(data.snowflake, "tagsInput" + i);
       }
       // Clean up
       this.invalidType = false;
-      this.imageList = [];
+      this.imageUploadList = [];
     },
     // Serialize form inputs into JSON object that can be sent to API
     serializeForm(formID) {
@@ -172,7 +189,9 @@ export default {
         Object.fromEntries(new FormData(document.getElementById(formID)))
       );
     },
-    async updateImage(flake, imageUpdatePayload) {
+    // Send an image update payload including 4 fields: "source", "parent", "commentary",
+    // "commentary translation" to paths.ImageField
+    async setImageInfo(flake, imageUpdatePayload) {
       const options = {
         method: "PATCH",
         headers: {
@@ -182,7 +201,20 @@ export default {
       };
       await fetch(`/api/image/${flake}`, options);
     },
-    async postImage(fd) {
+    async setImageTags(flake, tagsInputID) {
+      const tags = document
+        .getElementById(tagsInputID)
+        .value.split(" ")
+        .filter((element) => element.length > 0);
+      const options = {
+        method: "PUT",
+      };
+
+      for (let i = 0; i < tags.length; i++) {
+        await fetch(paths.ImageTagField(flake, tags[i]), options);
+      }
+    },
+    async postImageFile(fd) {
       const options = {
         method: "POST",
         headers: {