diff --git a/front/src/components/ServiceMessages.vue b/front/src/components/ServiceMessages.vue
index 4e53be241b04c244d977aac29157e37969801f07..a7fad93a57fb25ecb60e366b9c5c998b970ef374 100644
--- a/front/src/components/ServiceMessages.vue
+++ b/front/src/components/ServiceMessages.vue
@@ -1,83 +1,11 @@
 <template>
-  <div class="service-messages">
-    <message v-for="message in displayedMessages" :key="String(message.date)" :class="['large', getLevel(message)]">
-      <p>{{ message.content }}</p>
-    </message>
+  <div class="ui toast-container">
+    <message v-for="message in $store.state.ui.messages" :message="message" :key="message.key"></message>
     <slot></slot>
   </div>
 </template>
 
 <script>
-import {mapState} from 'vuex'
 
-export default {
-  data () {
-    return {
-      date: new Date(),
-      interval: null
-    }
-  },
-  created () {
-    this.setupInterval()
-  },
-  destroyed () {
-    if (this.interval) {
-      clearInterval(this.interval)
-    }
-  },
-  computed: {
-    ...mapState({
-      messages: state => state.ui.messages,
-      displayDuration: state => state.ui.messageDisplayDuration
-    }),
-    displayedMessages () {
-      let now = this.date
-      let interval = this.displayDuration
-      let toDisplay = this.messages.filter(m => {
-        return now - m.date <= interval
-      })
-      return toDisplay.slice(0, 3)
-    }
-  },
-  methods: {
-    setupInterval () {
-      if (this.interval) {
-        return
-      }
-      let self = this
-      this.interval = setInterval(() => {
-        if (self.displayedMessages.length === 0) {
-          clearInterval(self.interval)
-          this.interval = null
-        }
-        self.date = new Date()
-      }, 1000)
-    },
-    getLevel (message) {
-      return message.level || 'info'
-    }
-  },
-  watch: {
-    messages: {
-      handler (v) {
-        if (v.length > 0 && !this.interval) {
-          this.setupInterval()
-        }
-      },
-      deep: true
-    }
-  }
-}
+export default {}
 </script>
-
-<style>
-.service-messages {
-  z-index: 9999;
-  margin-left: 1em;
-  min-width: 20em;
-  max-width: 40em;
-}
-.service-messages .message:last-child {
-  margin-bottom: 0;
-}
-</style>
diff --git a/front/src/components/common/Message.vue b/front/src/components/common/Message.vue
index bbde16a1250fdf7d8b4c8c53128580ed8595d2b4..e837faa4e3417438fe766fba69f6bf1c37e323c1 100644
--- a/front/src/components/common/Message.vue
+++ b/front/src/components/common/Message.vue
@@ -1,33 +1,25 @@
 <template>
-  <div class="ui message">
-    <div class="content">
-      <slot></slot>
-    </div>
-    <i class="close icon"></i>
-  </div>
+  <div></div>
 </template>
 <script>
 import $ from 'jquery'
 
 export default {
+  props: ['message'],
   mounted () {
     let self = this
-    $(this.$el).on('click', function () {
-      $(self.$el).transition('fade', 125)
-    })
+    let params = {
+      context: "#app",
+      message: this.message.content,
+      showProgress: 'top',
+      position: "bottom right",
+      progressUp: true,
+      onRemove () {
+        self.$store.commit("ui/removeMessage", self.message.key)
+      },
+      ...this.message,
+    }
+    $("body").toast(params)
   }
 }
 </script>
-<style scoped>
-.ui.message .content {
-  padding-right: 0.5em;
-  cursor: pointer;
-}
-.ui.message .content :first-child {
-  margin-top: 0;
-}
-
-.ui.message .content :last-child {
-  margin-bottom: 0;
-}
-</style>
diff --git a/front/src/main.js b/front/src/main.js
index 917e105b2e6c00bfffeada17e9dca312763d6069..cea2d095ff2d1748612eed87753fce73d6aaa99f 100644
--- a/front/src/main.js
+++ b/front/src/main.js
@@ -117,7 +117,7 @@ axios.interceptors.response.use(function (response) {
     store.commit("ui/addMessage", {
       content: message,
       date: new Date(),
-      level: 'error',
+      class: 'error',
     })
     logger.default.error('This client is rate-limited!', rateLimitStatus)
   } else if (error.response.status === 500) {
diff --git a/front/src/semantic.js b/front/src/semantic.js
index 206b59149fcc099e3c2f012b14149fe06f5ece00..279997f3d4886fd72f652996b6eb554c816eb020 100644
--- a/front/src/semantic.js
+++ b/front/src/semantic.js
@@ -18,6 +18,7 @@ require('fomantic-ui-css/components/site.min.js')
 require('fomantic-ui-css/components/state.min.js')
 require('fomantic-ui-css/components/sticky.min.js')
 // require('fomantic-ui-css/components/tab.min.js')
+require('fomantic-ui-css/components/toast.min.js')
 require('fomantic-ui-css/components/transition.min.js')
 // require('fomantic-ui-css/components/video.min.js')
 require('fomantic-ui-css/components/visibility.min.js')
diff --git a/front/src/store/ui.js b/front/src/store/ui.js
index 1146e5201a3fc9d24af5cfde3d8642d6f2ca4023..195458d2fe74ad692413890d22e40fce972c6b4f 100644
--- a/front/src/store/ui.js
+++ b/front/src/store/ui.js
@@ -153,11 +153,21 @@ export default {
       state.theme = value
     },
     addMessage (state, message) {
-      state.messages.push(message)
+      let finalMessage = {
+        displayTime: state.messageDisplayDuration,
+        key: String(new Date()),
+        ...message,
+      }
+      state.messages.push(finalMessage)
       if (state.messages.length > state.maxMessages) {
         state.messages.shift()
       }
     },
+    removeMessage (state, key) {
+      state.messages = state.messages.filter((m) => {
+        return m.key != key
+      })
+    },
     notifications (state, {type, count}) {
       state.notifications[type] = count
     },
diff --git a/front/src/style/_main.scss b/front/src/style/_main.scss
index 3ffea35286c322ba6916310fc2ee2b44358ef23c..365e1da22a5d229a143a05faf4db639b82f9825c 100644
--- a/front/src/style/_main.scss
+++ b/front/src/style/_main.scss
@@ -70,6 +70,7 @@
 @import "~fomantic-ui-css/components/sticky.css";
 @import "~fomantic-ui-css/components/tab.css";
 @import "~fomantic-ui-css/components/text.css";
+@import "~fomantic-ui-css/components/toast.css";
 @import "~fomantic-ui-css/components/transition.css";
 
 
@@ -108,7 +109,7 @@ html {
   flex-direction: column;
   &.has-bottom-player {
     padding-bottom: $bottom-player-height;
-    .service-messages {
+    .toast-container {
       bottom: $bottom-player-height + 1rem;
     }
 
@@ -173,14 +174,6 @@ html {
   }
 }
 
-.service-messages {
-  position: fixed;
-  bottom: 1em;
-  right: 1em;
-  > .ui.message {
-    box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.7);
-  }
-}
 .main-pusher {
   padding: 1.5rem 0;
 }
diff --git a/front/tests/unit/specs/store/ui.spec.js b/front/tests/unit/specs/store/ui.spec.js
deleted file mode 100644
index 2f810e064346355f7df8b2fbaec8ac9d1df1ff0e..0000000000000000000000000000000000000000
--- a/front/tests/unit/specs/store/ui.spec.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import {expect} from 'chai'
-import store from '@/store/ui'
-
-describe('store/ui', () => {
-  describe('mutations', () => {
-    it('addMessage', () => {
-      const state = {maxMessages: 100, messages: []}
-      store.mutations.addMessage(state, 'hello')
-      expect(state.messages).to.deep.equal(['hello'])
-    })
-    it('addMessage', () => {
-      const state = {maxMessages: 1, messages: ['hello']}
-      store.mutations.addMessage(state, 'world')
-      expect(state.messages).to.deep.equal(['world'])
-    })
-  })
-})