diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index d20059c..359f06a 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -13,6 +13,7 @@
         "js-nacl": "^1.4.0",
         "moment": "^2.29.4",
         "vue": "^3.2.47",
+        "vue-multiselect": "^2.1.7",
         "vue-router": "^4.1.6",
         "vuex": "^4.1.0"
       },
@@ -2062,14 +2063,14 @@
       "integrity": "sha512-i/I1Omf6lADjVBlwJpQifZOePV15snHny9w04+lc71+3t8PyWuLC/7clyoOSHOBNGXFe2PAGxmTiZ+Z4HWsPyw=="
     },
     "node_modules/vite": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.1.tgz",
-      "integrity": "sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==",
+      "version": "4.3.9",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
+      "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==",
       "dev": true,
       "dependencies": {
         "esbuild": "^0.17.5",
-        "postcss": "^8.4.21",
-        "rollup": "^3.20.2"
+        "postcss": "^8.4.23",
+        "rollup": "^3.21.0"
       },
       "bin": {
         "vite": "bin/vite.js"
@@ -2234,6 +2235,15 @@
         "@vue/shared": "3.2.47"
       }
     },
+    "node_modules/vue-multiselect": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/vue-multiselect/-/vue-multiselect-2.1.7.tgz",
+      "integrity": "sha512-KIegcN+Ntwg3cbkY/jhw2s/+XJUM0Lpi/LcKFYCS8PrZHcWBl2iKCVze7ZCnRj3w8H7/lUJ9v7rj9KQiNxApBw==",
+      "engines": {
+        "node": ">= 4.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
     "node_modules/vue-router": {
       "version": "4.1.6",
       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 5b4727a..29846ea 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -13,6 +13,7 @@
     "js-nacl": "^1.4.0",
     "moment": "^2.29.4",
     "vue": "^3.2.47",
+    "vue-multiselect": "^2.1.7",
     "vue-router": "^4.1.6",
     "vuex": "^4.1.0"
   },
diff --git a/frontend/src/federation.js b/frontend/src/federation.js
index b91b5ea..f4553bd 100644
--- a/frontend/src/federation.js
+++ b/frontend/src/federation.js
@@ -190,12 +190,11 @@ class ServerSetUnion {
 
     async get(auth, target) {
         try {
-            const ret = await this.serverSets.reduce(async (acc, serverset) => {
+            return await this.serverSets.reduce(async (acc, serverset) => {
                 return acc.then(async (acc) => {
                     return acc.concat(await serverset.get(auth, target))
                 })
             }, Promise.resolve([]))
-            return ret
         } catch (e) {
             throw new Error('all servers failed')
         }
@@ -203,12 +202,11 @@ class ServerSetUnion {
 
     async post(auth, target, data) {
         try {
-            const ret = await this.serverSets.reduce(async (acc, serverset) => {
+            return await this.serverSets.reduce(async (acc, serverset) => {
                 return acc.then(async (acc) => {
                     return acc.concat(await serverset.post(auth, target, data))
                 })
             }, Promise.resolve([]))
-            return ret
         } catch (e) {
             throw new Error('all servers failed')
         }
@@ -216,12 +214,11 @@ class ServerSetUnion {
 
     async patch(auth, target, data) {
         try {
-            const ret = await this.serverSets.reduce(async (acc, serverset) => {
+            return await this.serverSets.reduce(async (acc, serverset) => {
                 return acc.then(async (acc) => {
                     return acc.concat(await serverset.patch(auth, target, data))
                 })
             }, Promise.resolve([]))
-            return ret
         } catch (e) {
             throw new Error('all servers failed')
         }
@@ -229,12 +226,11 @@ class ServerSetUnion {
 
     async put(auth, target, data) {
         try {
-            const ret = await this.serverSets.reduce(async (acc, serverset) => {
+            return await this.serverSets.reduce(async (acc, serverset) => {
                 return acc.then(async (acc) => {
                     return acc.concat(await serverset.put(auth, target, data))
                 })
             }, Promise.resolve([]))
-            return ret
         } catch (e) {
             throw new Error('all servers failed')
         }
@@ -242,12 +238,11 @@ class ServerSetUnion {
 
     async delete(auth, target) {
         try {
-            const ret = await this.serverSets.reduce(async (acc, serverset) => {
+            return await this.serverSets.reduce(async (acc, serverset) => {
                 return acc.then(async (acc) => {
                     return acc.concat(await serverset.delete(auth, target))
                 })
             }, Promise.resolve([]))
-            return ret
         } catch (e) {
             throw new Error('all servers failed')
         }
diff --git a/frontend/src/store.js b/frontend/src/store.js
index 0884638..df69f85 100644
--- a/frontend/src/store.js
+++ b/frontend/src/store.js
@@ -23,6 +23,7 @@ export default createStore({
         unreachable_neighbors: new NeighborsCache(),
         tags: [],
         properties: [],
+        files: [],
     },
     mutations: {
         setUser(state, user) {
@@ -67,6 +68,9 @@ export default createStore({
         setProperties(state, properties) {
             state.properties = properties;
         },
+        setFiles(state, files) {
+            state.files = files;
+        },
         logout(state) {
             state.user = null;
             state.token = null;
@@ -236,6 +240,22 @@ export default createStore({
             // TODO implement
             console.log('declining friend ' + args)
         },
+        async fetchFiles({state, commit, dispatch, getters}) {
+            if (state.last_load.files > Date.now() - 1000 * 60 * 60 * 24) {
+                return state.files
+            }
+            const servers = await dispatch('getHomeServers')
+            const data = await servers.get(getters.signAuth, '/api/files/')
+            commit('setFiles', data)
+            state.last_load.files = Date.now()
+            return data
+        },
+        async pushFile({state, dispatch, getters}, {file}) {
+            const servers = await dispatch('getHomeServers')
+            const data = await servers.post(getters.signAuth, '/api/files/', file)
+            state.files.push(data)
+            return data
+        },
         async fetchTags({state, commit, dispatch, getters}) {
             if (state.last_load.tags > Date.now() - 1000 * 60 * 60 * 24) {
                 return state.tags
diff --git a/frontend/src/views/InventoryEdit.vue b/frontend/src/views/InventoryEdit.vue
index cf516bb..e1ef54c 100644
--- a/frontend/src/views/InventoryEdit.vue
+++ b/frontend/src/views/InventoryEdit.vue
@@ -29,6 +29,10 @@
                                 <input type="number" class="form-control" id="quantity" name="quantity"
                                        placeholder="Enter quantity" v-model="item.owned_quantity">
                             </div>
+                            <div class="mb-3">
+                                <label for="image" class="form-label">Image</label>
+                                <combined-file-field :value="item.files"></combined-file-field>
+                            </div>
                         </div>
                         <div class="card">
                             <button type="submit" class="btn btn-primary" @click="updateInventoryItem(item)">Update
@@ -47,6 +51,7 @@ import {mapActions, mapGetters} from "vuex";
 import BaseLayout from "@/components/BaseLayout.vue";
 import TagField from "@/components/TagField.vue";
 import PropertyField from "@/components/PropertyField.vue";
+import CombinedFileField from "@/components/CombinedFileField.vue";
 
 export default {
     name: "InventoryEdit",
@@ -54,6 +59,7 @@ export default {
         BaseLayout,
         TagField,
         PropertyField,
+        CombinedFileField,
         ...BIcons
     },
     props: {
diff --git a/frontend/src/views/InventoryNew.vue b/frontend/src/views/InventoryNew.vue
index 8c17b48..0b78fc0 100644
--- a/frontend/src/views/InventoryNew.vue
+++ b/frontend/src/views/InventoryNew.vue
@@ -29,12 +29,10 @@
                                 <input type="number" class="form-control" id="quantity" name="quantity"
                                        placeholder="Enter quantity" v-model="item.owned_quantity">
                             </div>
-                            <!-- TODO -->
-                            <!--div class="mb-3">
+                            <div class="mb-3">
                                 <label for="image" class="form-label">Image</label>
-                                <input type="text" class="form-control" id="image" name="image"
-                                       placeholder="Enter image" v-model="item.image">
-                            </div-->
+                                <combined-file-field :value="item.files"></combined-file-field>
+                            </div>
                         </div>
                         <div class="card">
                             <button type="submit" class="btn btn-primary"
@@ -54,6 +52,7 @@ import {mapActions} from "vuex";
 import BaseLayout from "@/components/BaseLayout.vue";
 import TagField from "@/components/TagField.vue";
 import PropertyField from "@/components/PropertyField.vue";
+import CombinedFileField from "@/components/CombinedFileField.vue";
 
 export default {
     name: "InventoryNew",
@@ -61,6 +60,7 @@ export default {
         BaseLayout,
         TagField,
         PropertyField,
+        CombinedFileField,
         ...BIcons
     },
     data() {
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index e711d7b..a2af71a 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -46,6 +46,9 @@ export default defineConfig({
             '^/static/': {
                 target: "http://127.0.0.1:8000/",
             },
+            '^/media/': {
+                target: "http://127.0.0.1:8000/",
+            },
             '^/wiki/': {
                 target: "http://127.0.0.1:8080/",
                 rewrite: (path) => path.replace(/^\/wiki/, ''),