diff --git a/_assets/src/components/Listing.vue b/_assets/src/components/Listing.vue
index bcf1b53f..5e468e0c 100644
--- a/_assets/src/components/Listing.vue
+++ b/_assets/src/components/Listing.vue
@@ -74,6 +74,7 @@
 import {mapState} from 'vuex'
 import Item from './ListingItem'
 import api from '@/utils/api'
+import buttons from '@/utils/buttons'
 
 export default {
   name: 'listing',
@@ -159,7 +160,7 @@ export default {
     handleFiles: function (files, base) {
       this.resetOpacity()
 
-      // buttons.setLoading('upload')
+      buttons.loading('upload')
       let promises = []
 
       for (let file of files) {
@@ -168,12 +169,12 @@ export default {
 
       Promise.all(promises)
         .then(() => {
+          buttons.done('upload')
           this.$store.commit('setReload', true)
-          // buttons.setDone('upload')
         })
         .catch(e => {
+          buttons.done('upload', false)
           console.log(e)
-          // buttons.setDone('upload', false)
         })
 
       return false
diff --git a/_assets/src/components/prompts/Delete.vue b/_assets/src/components/prompts/Delete.vue
index 309e8c12..1a6d5658 100644
--- a/_assets/src/components/prompts/Delete.vue
+++ b/_assets/src/components/prompts/Delete.vue
@@ -14,6 +14,7 @@
 import {mapGetters, mapMutations, mapState} from 'vuex'
 import api from '@/utils/api'
 import url from '@/utils/url'
+import buttons from '@/utils/buttons'
 
 export default {
   name: 'delete',
@@ -25,16 +26,16 @@ export default {
     ...mapMutations(['closeHovers']),
     submit: function (event) {
       this.closeHovers()
-      // buttons.setLoading('delete')
+      buttons.loading('delete')
 
       if (this.req.kind !== 'listing') {
         api.delete(this.$route.path)
           .then(() => {
-            // buttons.setDone('delete')
+            buttons.done('delete')
             this.$router.push({path: url.removeLastDir(this.$route.path) + '/'})
           })
           .catch(error => {
-            // buttons.setDone('delete', false)
+            buttons.done('delete', false)
             console.log(error)
           })
 
@@ -55,12 +56,12 @@ export default {
       Promise.all(promises)
         .then(() => {
           this.$store.commit('setReload', true)
-          // buttons.setDone('delete')
+          buttons.done('delete')
         })
         .catch(error => {
           console.log(error)
           this.$store.commit('setReload', true)
-          // buttons.setDone('delete', false)
+          buttons.done('delete', false)
         })
     }
   }
diff --git a/_assets/src/components/prompts/Move.vue b/_assets/src/components/prompts/Move.vue
index d3916c10..1f19e707 100644
--- a/_assets/src/components/prompts/Move.vue
+++ b/_assets/src/components/prompts/Move.vue
@@ -58,7 +58,7 @@ export default {
       let el = event.currentTarget
       let promises = []
       let dest = this.current
-      // buttons.setLoading('move')
+      buttons.loading('move')
 
       let selected = el.querySelector('li[aria-selected=true]')
       if (selected !== null) {
@@ -77,11 +77,11 @@ export default {
 
       Promise.all(promises)
         .then(() => {
-          // buttons.setDone('move')
+          buttons.done('move')
           this.$router.push({page: dest})
         })
         .catch(e => {
-          // buttons.setDone('move', false)
+          buttons.done('move', false)
           console.log(e)
         })
     },
diff --git a/_assets/src/components/prompts/NewDir.vue b/_assets/src/components/prompts/NewDir.vue
index 600a85c0..d93961d6 100644
--- a/_assets/src/components/prompts/NewDir.vue
+++ b/_assets/src/components/prompts/NewDir.vue
@@ -34,14 +34,12 @@ export default {
       uri += this.name + '/'
       uri = uri.replace('//', '/')
 
-      // buttons.setLoading('newDir')
       api.put(uri)
         .then(() => {
-          // buttons.setDone('newDir')
           this.$router.push({ path: uri })
         })
         .catch(error => {
-          // buttons.setDone('newDir', false)
+          // TODO: Show error message!
           console.log(error)
         })
 
diff --git a/_assets/src/components/prompts/NewFile.vue b/_assets/src/components/prompts/NewFile.vue
index dbbd5fa2..17ac47a6 100644
--- a/_assets/src/components/prompts/NewFile.vue
+++ b/_assets/src/components/prompts/NewFile.vue
@@ -34,14 +34,12 @@ export default {
       uri += this.name
       uri = uri.replace('//', '/')
 
-      // buttons.setLoading('newFile')
       api.put(uri)
         .then(() => {
-          // buttons.setDone('newFile')
           this.$router.push({ path: uri })
         })
         .catch(error => {
-          // buttons.setDone('newFile', false)
+          // TODO: show error message in a box
           console.log(error)
         })
 
diff --git a/_assets/src/components/prompts/Rename.vue b/_assets/src/components/prompts/Rename.vue
index d10427f4..7677c803 100644
--- a/_assets/src/components/prompts/Rename.vue
+++ b/_assets/src/components/prompts/Rename.vue
@@ -52,8 +52,6 @@ export default {
       this.name = encodeURIComponent(this.name)
       newLink = url.removeLastDir(oldLink) + '/' + this.name
 
-      // buttons.setLoading('rename')
-
       api.move(oldLink, newLink)
         .then(() => {
           if (this.req.kind !== 'listing') {
@@ -61,10 +59,9 @@ export default {
             return
           }
           // TODO: keep selected after reload?
-          // buttons.setDone('rename')
           this.$store.commit('setReload', true)
         }).catch(error => {
-          // buttons.setDone('rename', false)
+          // TODO: show error message
           console.log(error)
         })
 
diff --git a/_assets/src/utils/buttons.js b/_assets/src/utils/buttons.js
new file mode 100644
index 00000000..46797ac2
--- /dev/null
+++ b/_assets/src/utils/buttons.js
@@ -0,0 +1,54 @@
+function loading (button) {
+  let el = document.querySelector(`#${button}-button > i`)
+
+  if (el === undefined || el === null) {
+    console.log('Error getting button ' + button)
+    return
+  }
+
+  el.dataset.icon = el.innerHTML
+  el.style.opacity = 0
+
+  setTimeout(() => {
+    el.classList.add('spin')
+    el.innerHTML = 'autorenew'
+    el.style.opacity = 1
+  }, 200)
+}
+
+function done (button, success = true) {
+  let el = document.querySelector(`#${button}-button > i`)
+
+  if (el === undefined || el === null) {
+    console.log('Error getting button ' + button)
+    return
+  }
+
+  el.style.opacity = 0
+
+  let third = () => {
+    el.innerHTML = el.dataset.icon
+    el.style.opacity = null
+  }
+
+  let second = () => {
+    el.style.opacity = 0
+    setTimeout(third, 200)
+  }
+
+  let first = () => {
+    el.classList.remove('spin')
+    el.innerHTML = success
+      ? 'done'
+      : 'close'
+    el.style.opacity = 1
+    setTimeout(second, 200)
+  }
+
+  setTimeout(first, 200)
+}
+
+export default {
+  loading,
+  done
+}