diff --git a/frontend/src/components/CameraFileSource.vue b/frontend/src/components/CameraFileSource.vue
new file mode 100644
index 0000000..5a93a89
--- /dev/null
+++ b/frontend/src/components/CameraFileSource.vue
@@ -0,0 +1,76 @@
+<template>
+    <div class="d-inline-block" v-if="show_camera">
+        <label for="pictures">
+            <slot></slot>
+        </label>
+        <input type="file" accept="image/*" multiple capture="camera" id="pictures" @change="loadFiles" class="d-none">
+    </div>
+</template>
+
+<style scoped>
+</style>
+
+<script>
+import * as BIcons from "bootstrap-icons-vue";
+
+export default {
+    name: "CameraFileSource",
+    components: {
+        ...BIcons
+    },
+    emits: ["input"],
+    data() {
+        return {
+            show_camera: false
+        }
+    },
+    methods: {
+        loadFiles() {
+            const files = document.getElementById("pictures").files;
+            const jobs = [...files].map((file) => {
+                return new Promise((resolve, reject) => {
+                    var reader = new FileReader();
+                    reader.onload = () => {
+                        const buffer = reader.result;
+                        if (!(buffer instanceof ArrayBuffer)) {
+                            console.log(buffer)
+                            reject("Not an ArrayBuffer");
+                        }
+                        const data = new Uint8Array(buffer);
+                        const hash = nacl.crypto_hash(data).reduce((a, b) => a + b.toString(16).padStart(2, "0"), "");
+                        var base64 = btoa(
+                            data.reduce((a, b) => a + String.fromCharCode(b), '')
+                        );
+                        resolve({
+                            name: file.name,
+                            size: file.size,
+                            mime_type: file.type,
+                            data: base64,
+                            hash: hash,
+                        });
+                    };
+                    reader.onerror = (e) => {
+                        reject(e);
+                    };
+                    reader.readAsArrayBuffer(file)
+                })
+            });
+            Promise.all(jobs).then((files) => {
+                this.$emit("input", files)
+            })
+        }
+    },
+    mounted() {
+        //detect media api
+        if ('capture' in document.createElement('input')) {
+            console.log('capture supported.');
+            this.show_camera = true;
+        }
+
+        if (navigator.getUserMedia) {
+            console.log('getUserMedia supported.');
+            this.show_camera = true;
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/frontend/src/components/CombinedFileField.vue b/frontend/src/components/CombinedFileField.vue
index ef678f7..06ee839 100644
--- a/frontend/src/components/CombinedFileField.vue
+++ b/frontend/src/components/CombinedFileField.vue
@@ -1,33 +1,33 @@
 <template>
-    <div class="input-group">
+    <drag-drop-file-source @input="addFiles">
+        <h3 v-if="create">Create New</h3>
+        <h3 v-else>Update</h3>
         <ul>
-            <li v-for="file in files" :key="file.id">
+            <li v-for="file in whithout_images(files)" :key="file.id">
+                {{ file.name }}
+            </li>
+            <li v-for="file in whithout_images(item_files)" :key="file.id">
                 {{ file.name }}
             </li>
         </ul>
-        <ul>
-            <img v-for="file in files" :key="file.id" :src="file.name" :alt="file.name" class="img-thumbnail">
-        </ul>
-        <h4>Files</h4>
-        <div class="input-group">
-            <input type="file" class="form-control" multiple id="files">
-            <button class="btn btn-outline-secondary" type="button">
-                <b-icon-trash></b-icon-trash>
-            </button>
-            <button class="btn btn-outline-secondary" type="button" @click="uploadFiles">
-                <b-icon-upload></b-icon-upload>
-            </button>
+        <hr>
+        <div>
+            <img v-for="file in only_images(files)" :key="file.id" :src="file.name" :alt="file.name"
+                 class="img-thumbnail" :title="file.mime_type">
+            <img v-for="file in whithout_images(item_files)" :key="file.id" :alt="file.name"
+                 :src="'data:' + file.mime_type + ';base64,' + file.data" class="img-thumbnail border-info">
+            <fs-file-source @input="addFiles">
+                <div class="img-thumbnail btn btn-outline-primary">
+                    <b-icon-upload></b-icon-upload>
+                </div>
+            </fs-file-source>
+            <camera-file-source @input="addFiles">
+                <div class="img-thumbnail btn btn-outline-primary">
+                    <b-icon-camera></b-icon-camera>
+                </div>
+            </camera-file-source>
         </div>
-        <div class="input-group" v-if="show_camera">
-            <input type="file" class="form-control" accept="image/*" multiple capture="camera" id="photos">
-            <button class="btn btn-outline-secondary" type="button">
-                <b-icon-trash></b-icon-trash>
-            </button>
-            <button class="btn btn-outline-secondary" type="button" @click="uploadPhoto">
-                <b-icon-upload></b-icon-upload>
-            </button>
-        </div>
-    </div>
+    </drag-drop-file-source>
 </template>
 
 <style scoped>
@@ -36,108 +36,82 @@
     height: 54px;
     object-fit: cover;
 }
+
+.img-thumbnail svg {
+    width: 100%;
+    height: 100%;
+}
 </style>
 
 <script>
 import * as BIcons from "bootstrap-icons-vue";
 import {mapActions, mapState} from "vuex";
+import DragDropFileSource from "@/components/DragDropFileSource.vue";
+import FsFileSource from "@/components/FsFileSource.vue";
+import CameraFileSource from "@/components/CameraFileSource.vue";
 
 export default {
     name: "CombinedFileField",
-    data() {
-        return {
-            show_camera: false
-        }
-    },
     components: {
-        ...BIcons
+        ...BIcons,
+        DragDropFileSource,
+        CameraFileSource,
+        FsFileSource
     },
     props: {
-        value: {
+        item_files: {
             type: Array,
             required: true
         },
+        item_id: {
+            type: Number
+        },
+        create: {
+            type: Boolean,
+            default: false
+        }
     },
-    model: {
-        prop: "value",
-        event: "input"
-    },
+    emits: ["change"],
     computed: {
         ...mapState(["files"]),
     },
     methods: {
         ...mapActions(["fetchFiles", "pushFile"]),
-        async uploadFiles() {
-            const files = document.getElementById("files").files;
-            for (let i = 0; i < files.length; i++) {
-                var reader = new FileReader();
-                const file = files[i];
-                const buffer = await new Promise((resolve, reject) => {
-                    reader.onload = (e) => {
-                        resolve(e.target.result);
-                    };
-                    reader.onerror = (e) => {
-                        reject(e);
-                    };
-                    reader.readAsArrayBuffer(file)
-                });
-                const data = new Uint8Array(buffer);
-                const hash = nacl.crypto_hash(data);
-                var base64 = btoa(
-                    data.reduce((a, b) => a + String.fromCharCode(b), '')
-                );
+        async uploadFiles(files) {
+            const jobs = files.map(async file => {
                 await this.pushFile({
-                    file: {
-                        mime_type: file.type,
-                        data: base64,
-                    },
-                    item_id: 1
+                    file: file,
+                    item_id: this.item_id
                 });
-                await this.fetchFiles();
-            }
+            });
+            const responses = await Promise.all(jobs);
+            console.log(responses);
+            return responses;
         },
-        async uploadPhoto() {
-            const files = document.getElementById("photos").files;
-            for (let i = 0; i < files.length; i++) {
-                var reader = new FileReader();
-                const file = files[i];
-                const buffer = await new Promise((resolve, reject) => {
-                    reader.onload = (e) => {
-                        resolve(e.target.result);
-                    };
-                    reader.onerror = (e) => {
-                        reject(e);
-                    };
-                    reader.readAsArrayBuffer(file)
-                });
-                const data = new Uint8Array(buffer);
-                const hash = nacl.crypto_hash(data);
-                var base64 = btoa(
-                    data.reduce((a, b) => a + String.fromCharCode(b), '')
-                );
-                await this.pushFile({
-                    file: {
-                        mime_type: file.type,
-                        data: base64,
-                    },
-                    item_id: 1
-                });
-                await this.fetchFiles();
+        addFiles(files) {
+            const newfiles = files.filter(file => !this.item_files.find(f => f.hash === file.hash));
+            if (newfiles.length === 0) {
+                console.log("no new files");
+                return;
             }
+            if (!this.create) {
+                this.uploadFiles(newfiles).then((uploaded) => {
+                    console.log(uploaded);
+                })
+            }
+            this.$emit("change", [...this.item_files, ...newfiles]);
+            console.log(this.item_files);
+
+        },
+        only_images(files) {
+            return files.filter(file => file.mime_type.startsWith("image/"));
+        },
+        whithout_images(files) {
+            return files.filter(file => !file.mime_type.startsWith("image/"));
         },
     },
     mounted() {
         this.fetchFiles();
-        //detect media api
-        if ('capture' in document.createElement('input')) {
-            console.log('capture supported.');
-            this.show_camera = true;
-        }
-
-        if (navigator.getUserMedia) {
-            console.log('getUserMedia supported.');
-            this.show_camera = true;
-        }
     }
 }
 </script>
\ No newline at end of file
diff --git a/frontend/src/components/DragDropFileSource.vue b/frontend/src/components/DragDropFileSource.vue
new file mode 100644
index 0000000..6646ef5
--- /dev/null
+++ b/frontend/src/components/DragDropFileSource.vue
@@ -0,0 +1,88 @@
+<template>
+    <div @dragenter.prevent="dragenter" @dragover.prevent="dragover" @dragleave.prevent="dragleave"
+         @drop.prevent="drop" ref="dropzone">
+        <slot></slot>
+    </div>
+</template>
+
+<style scoped>
+.dragover {
+    opacity: 0.5;
+}
+</style>
+
+<script>
+import * as BIcons from "bootstrap-icons-vue";
+
+export default {
+    name: "DragDropFileSource",
+    components: {
+        ...BIcons
+    },
+    emits: ["input"],
+    methods: {
+        dragenter(e) {
+            if (e.dataTransfer.types.includes("Files")) {
+                e.preventDefault()
+                e.stopPropagation()
+                this.$refs.dropzone.classList.add("dragover")
+            }else{
+                console.log(e.dataTransfer.types) //TODO text/uri-list?
+            }
+        },
+        dragover(e) {
+            if (e.dataTransfer.types.includes("Files")) {
+                e.preventDefault()
+                e.stopPropagation()
+                this.$refs.dropzone.classList.add("dragover")
+            }
+        },
+        dragleave(e) {
+            if (e.dataTransfer.types.includes("Files")) {
+                e.preventDefault()
+                e.stopPropagation()
+                this.$refs.dropzone.classList.remove("dragover")
+            }
+        },
+        drop(e) {
+            if (e.dataTransfer.types.includes("Files")) {
+                e.preventDefault()
+                e.stopPropagation()
+                this.$refs.dropzone.classList.remove("dragover")
+                const jobs = [...e.dataTransfer.files].map((file) => {
+                    return new Promise((resolve, reject) => {
+                        let reader = new FileReader();
+                        reader.readAsArrayBuffer(file)
+                        reader.onloadend = () => {
+                            const buffer = reader.result;
+                            if (!(buffer instanceof ArrayBuffer)) {
+                                console.log(buffer)
+                                reject("Not an ArrayBuffer");
+                            }
+                            const data = new Uint8Array(buffer);
+                            const hash = nacl.crypto_hash(data).reduce((a, b) => a + b.toString(16).padStart(2, "0"), "");
+                            var base64 = btoa(
+                                data.reduce((a, b) => a + String.fromCharCode(b), '')
+                            );
+                            resolve({
+                                name: file.name,
+                                size: file.size,
+                                mime_type: file.type,
+                                data: base64,
+                                hash: hash,
+                            });
+                        }
+                        reader.onerror = (e) => {
+                            reject(e)
+                        }
+                    })
+                })
+                Promise.all(jobs).then((files) => {
+                    this.$emit("input", files)
+                    console.log("drop", files)
+                })
+            }
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/frontend/src/components/FsFileSource.vue b/frontend/src/components/FsFileSource.vue
new file mode 100644
index 0000000..29bff9a
--- /dev/null
+++ b/frontend/src/components/FsFileSource.vue
@@ -0,0 +1,59 @@
+<template>
+    <div class="d-inline-block">
+        <label for="files">
+            <slot></slot>
+        </label>
+        <input type="file" multiple id="files" @change="loadFiles" class="d-none">
+    </div>
+</template>
+
+<style scoped>
+</style>
+
+<script>
+import * as BIcons from "bootstrap-icons-vue";
+
+export default {
+    name: "FsFileSource",
+    components: {
+        ...BIcons
+    },
+    emits: ["input"],
+    methods: {
+        loadFiles() {
+            const files = document.getElementById("files").files;
+            const jobs = [...files].map((file) => {
+                return new Promise((resolve, reject) => {
+                    var reader = new FileReader();
+                    reader.onload = () => {
+                        const buffer = reader.result;
+                        if (!(buffer instanceof ArrayBuffer)) {
+                            console.log(buffer)
+                            reject("Not an ArrayBuffer");
+                        }
+                        const data = new Uint8Array(buffer);
+                        const hash = nacl.crypto_hash(data).reduce((a, b) => a + b.toString(16).padStart(2, "0"), "");
+                        var base64 = btoa(
+                            data.reduce((a, b) => a + String.fromCharCode(b), '')
+                        );
+                        resolve({
+                            name: file.name,
+                            size: file.size,
+                            mime_type: file.type,
+                            data: base64,
+                            hash: hash,
+                        });
+                    };
+                    reader.onerror = (e) => {
+                        reject(e);
+                    };
+                    reader.readAsArrayBuffer(file)
+                })
+            });
+            Promise.all(jobs).then((files) => {
+                this.$emit("input", files)
+            })
+        }
+    },
+}
+</script>
\ No newline at end of file
diff --git a/frontend/src/store.js b/frontend/src/store.js
index 36d06dc..9eb157f 100644
--- a/frontend/src/store.js
+++ b/frontend/src/store.js
@@ -253,7 +253,7 @@ export default createStore({
         async pushFile({state, dispatch, getters}, {item_id, file}) {
             const servers = await dispatch('getHomeServers')
             const data = await servers.post(getters.signAuth, '/api/item_files/'+item_id+'/', file)
-            if (data.mime_type) {
+            if (data.hash) {
                 state.files.push(data)
                 return data
             }
diff --git a/frontend/src/views/InventoryDetail.vue b/frontend/src/views/InventoryDetail.vue
index c0d4f8b..40dcd98 100644
--- a/frontend/src/views/InventoryDetail.vue
+++ b/frontend/src/views/InventoryDetail.vue
@@ -36,10 +36,10 @@
                     </div>
                     <!-- actions -->
                     <div class="card">
-                        <a class="btn btn-primary" :href="'/inventory/' + id + '/edit'">
+                        <button class="btn btn-primary" @click="$router.push('/inventory/' + id + '/edit')">
                             <b-icon-pencil-square></b-icon-pencil-square>
                             Edit
-                        </a>
+                        </button>
                         <button type="submit" class="btn btn-danger"
                                 @click="deleteInventoryItem(item).then(() => $router.push('/inventory'))">
                             <b-icon-trash></b-icon-trash>
diff --git a/frontend/src/views/InventoryEdit.vue b/frontend/src/views/InventoryEdit.vue
index e1ef54c..4e0f409 100644
--- a/frontend/src/views/InventoryEdit.vue
+++ b/frontend/src/views/InventoryEdit.vue
@@ -6,6 +6,13 @@
                     <div class="card">
                         <div class="card-header">Edit Item</div>
                         <div class="card-body">
+                            <div class="mb-3">
+                                <ul>
+                                    <li v-for="file in item.files" :key="file.name">
+                                        {{ file.name }} ({{ file.hash }})
+                                    </li>
+                                </ul>
+                            </div>
                             <div class="mb-3">
                                 <label for="name" class="form-label">Name</label>
                                 <input type="text" class="form-control" id="name" name="name"
@@ -31,7 +38,7 @@
                             </div>
                             <div class="mb-3">
                                 <label for="image" class="form-label">Image</label>
-                                <combined-file-field :value="item.files"></combined-file-field>
+                                <combined-file-field :item_files="item.files" :item_id="item.id" @change="addFiles"></combined-file-field>
                             </div>
                         </div>
                         <div class="card">
@@ -78,12 +85,16 @@ export default {
                 description: "",
                 owned_quantity: 0,
                 image: "",
+                files: [],
                 ...this.inventory_items.find(item => item.id === parseInt(this.id))
             }
         }
     },
     methods: {
-        ...mapActions(["fetchInventoryItems", "updateInventoryItem"])
+        ...mapActions(["fetchInventoryItems", "updateInventoryItem"]),
+        addFiles(files) {
+            this.inventory_items.find(item => item.id === parseInt(this.id)).files = files
+        },
     },
     async mounted() {
         await this.fetchInventoryItems()
diff --git a/frontend/src/views/InventoryNew.vue b/frontend/src/views/InventoryNew.vue
index 0b78fc0..e23d63e 100644
--- a/frontend/src/views/InventoryNew.vue
+++ b/frontend/src/views/InventoryNew.vue
@@ -6,6 +6,13 @@
                     <div class="card">
                         <div class="card-header">Create New Item</div>
                         <div class="card-body">
+                            <div class="mb-3">
+                                <ul>
+                                    <li v-for="file in item.files" :key="file.name">
+                                        {{ file.name }} ({{ file.hash }})
+                                    </li>
+                                </ul>
+                            </div>
                             <div class="mb-3">
                                 <label for="name" class="form-label">Name</label>
                                 <input type="text" class="form-control" id="name" name="name"
@@ -31,7 +38,8 @@
                             </div>
                             <div class="mb-3">
                                 <label for="image" class="form-label">Image</label>
-                                <combined-file-field :value="item.files"></combined-file-field>
+                                <combined-file-field :item_files="item.files" create
+                                                     @change="item.files = $event"></combined-file-field>
                             </div>
                         </div>
                         <div class="card">
@@ -71,7 +79,8 @@ export default {
                 owned_quantity: 0,
                 image: "",
                 tags: [],
-                properties: []
+                properties: [],
+                files: []
             }
         }
     },