diff --git a/aleksis/core/frontend/app/apollo.js b/aleksis/core/frontend/app/apollo.js
index a7dbba4b1a53695430a62f922313654cfa9ef99c..a78ef2247b9e6235373a128f9d1f6717de388af4 100644
--- a/aleksis/core/frontend/app/apollo.js
+++ b/aleksis/core/frontend/app/apollo.js
@@ -10,8 +10,6 @@ import { InMemoryCache } from "@/apollo-cache-inmemory";
 
 import gql from "@/graphql-tag";
 
-import gqlSnackbarItems from "../components/app/snackbarItems.graphql";
-
 // Cache for GraphQL query results in memory and persistent across sessions
 const cache = new InMemoryCache();
 await persistCache({
@@ -19,66 +17,6 @@ await persistCache({
   storage: new LocalStorageWrapper(window.localStorage),
 });
 
-/**
- * Type definitions for Apollo/s local state management
- *  cf. https://www.apollographql.com/docs/react/local-state/local-state-management/
- */
-const typeDefs = gql`
-  type snackbarItem {
-    id: ID!
-    messageKey: String!
-    color: String!
-    read: Boolean!
-  }
-
-  type globalState {
-    contentLoading: Boolean!
-    browserTitle: String
-  }
-
-  type Mutation {
-    toggleSnackbarItem(id: ID!): Boolean
-  }
-`;
-
-/** Resolvers for local state management */
-const resolvers = {
-  Mutation: {
-    /** Toggle snackbar item read or unread, given its ID */
-    toggleSnackbarItem: (_, { id }, { cache }) => {
-      const data = cache.readQuery({ query: gqlSnackbarItems });
-      const currentItem = data.snackbarItems.find((item) => item.id === id);
-      currentItem.read = !currentItem.read;
-      cache.writeQuery({ query: gqlSnackbarItems, data });
-      return currentItem.read;
-    },
-  },
-};
-
-/**
- * Utility function to add a snackbar on GraphQL errors.
- */
-function addErrorSnackbarItem(messageKey) {
-  let uuid = crypto.randomUUID();
-  cache.writeQuery({
-    query: gqlSnackbarItems,
-    data: {
-      snackbarItems: [
-        {
-          __typename: "snackbarItem",
-          id: uuid,
-          messageKey: messageKey,
-          color: "red",
-          read: false,
-        },
-      ],
-    },
-    variables: {
-      id: uuid,
-    },
-  });
-}
-
 /**
  * Construct the GraphQL endpoint URI.
  *
@@ -104,8 +42,6 @@ const links = [
 
 /** Upstream Apollo GraphQL client */
 const apolloClient = new ApolloClient({
-  typeDefs,
-  resolvers,
   cache,
   link: from(links),
 });
@@ -124,7 +60,12 @@ const apolloOpts = {
             console.error("GraphQL query error:", err.message);
           }
           // Add a snackbar on all errors returned by the GraphQL endpoint
-          addErrorSnackbarItem("graphql.snackbar_error_message");
+          vm.$root.snackbarItems.push({
+            id: crypto.randomUUID(),
+            timeout: 5000,
+            messageKey: "graphql.snackbar_error_message",
+            color: "red",
+          });
         }
         if (networkError) {
           // Set app offline globally on network errors
diff --git a/aleksis/core/frontend/components/app/App.vue b/aleksis/core/frontend/components/app/App.vue
index 9c114d06c93947d41e07d1973627c373540063de..ab6abeb05ac8816e2e9f3423ba7fe69b74aa33b3 100644
--- a/aleksis/core/frontend/components/app/App.vue
+++ b/aleksis/core/frontend/components/app/App.vue
@@ -182,13 +182,11 @@
         </v-card>
       </v-footer>
     </div>
-    <div v-if="snackbarItems">
-      <snackbar-item
-        v-for="item in snackbarItems"
-        :key="item.id"
-        :snackbar-item="item"
-      />
-    </div>
+    <snackbar-item
+      v-for="item in $root.snackbarItems"
+      :key="item.id"
+      :snackbar-item="item"
+    />
     <v-snackbar v-model="needRefresh" v-if="!refreshDismissed">
       {{ $t("service_worker.new_version_available") }}
 
@@ -216,7 +214,6 @@ import SnackbarItem from "./SnackbarItem.vue";
 import gqlWhoAmI from "./whoAmI.graphql";
 import gqlMessages from "./messages.graphql";
 import gqlSystemProperties from "./systemProperties.graphql";
-import gqlSnackbarItems from "./snackbarItems.graphql";
 
 import useRegisterSWMixin from "../../mixins/useRegisterSW";
 import offlineMixin from "../../mixins/offline";
@@ -229,7 +226,6 @@ export default {
       whoAmI: null,
       systemProperties: null,
       messages: null,
-      snackbarItems: null,
     };
   },
   apollo: {
@@ -243,10 +239,6 @@ export default {
       },
       pollInterval: 10000,
     },
-    snackbarItems: {
-      query: gqlSnackbarItems,
-      pollInterval: 1000,
-    },
     messages: {
       query: gqlMessages,
       pollInterval: 1000,
diff --git a/aleksis/core/frontend/components/app/SnackbarItem.vue b/aleksis/core/frontend/components/app/SnackbarItem.vue
index 95eeb13ab10367e5d7576dd22247e6c7c33993da..ee442c44f99d9e1222ca80f0cd8f9beda49d0c65 100644
--- a/aleksis/core/frontend/components/app/SnackbarItem.vue
+++ b/aleksis/core/frontend/components/app/SnackbarItem.vue
@@ -1,8 +1,13 @@
 <template>
-  <v-snackbar :value="!snackbarItem.read" :color="snackbarItem.color">
+  <v-snackbar
+    v-model="visible"
+    :color="snackbarItem.color"
+    :timeout="snackbarItem.timeout"
+    @input="deleteSnackbarItem"
+  >
     {{ $t(snackbarItem.messageKey) }}
     <template #action="{ attrs }">
-      <v-btn icon @click="toggleSnackbarItem(snackbarItem.id)"
+      <v-btn icon @click="deleteSnackbarItem"
         ><v-icon>mdi-close</v-icon>
       </v-btn>
     </template>
@@ -10,10 +15,13 @@
 </template>
 
 <script>
-import gqlCheckSnackbarItem from "./toggleSnackbarItem.graphql";
-
 export default {
   name: "SnackbarItem",
+  data() {
+    return {
+      visible: true,
+    };
+  },
   props: {
     snackbarItem: {
       type: Object,
@@ -21,11 +29,10 @@ export default {
     },
   },
   methods: {
-    toggleSnackbarItem(id) {
-      this.$apollo.mutate({
-        mutation: gqlCheckSnackbarItem,
-        variables: { id },
-      });
+    deleteSnackbarItem() {
+      this.$root.snackbarItems = this.$root.snackbarItems.filter(
+        (obj) => obj.id !== this.snackbarItem.id
+      );
     },
   },
 };
diff --git a/aleksis/core/frontend/components/app/snackbarItems.graphql b/aleksis/core/frontend/components/app/snackbarItems.graphql
deleted file mode 100644
index c31423e87179649f7d29e80edc32c84e54490ee1..0000000000000000000000000000000000000000
--- a/aleksis/core/frontend/components/app/snackbarItems.graphql
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  snackbarItems @client {
-    id
-    messageKey
-    color
-    read
-  }
-}
diff --git a/aleksis/core/frontend/components/app/toggleSnackbarItem.graphql b/aleksis/core/frontend/components/app/toggleSnackbarItem.graphql
deleted file mode 100644
index 2badaf3185ce7ecb9c16b8ffd12ba5f6cff0f34b..0000000000000000000000000000000000000000
--- a/aleksis/core/frontend/components/app/toggleSnackbarItem.graphql
+++ /dev/null
@@ -1,3 +0,0 @@
-mutation ($id: String!) {
-  toggleSnackbarItem(id: $id) @client
-}
diff --git a/aleksis/core/frontend/index.js b/aleksis/core/frontend/index.js
index a11198e6c6ec23cee4d93a286eae07fc85060bf5..9bc91c8fb33fa7609b5c5923ac57f69a754248a1 100644
--- a/aleksis/core/frontend/index.js
+++ b/aleksis/core/frontend/index.js
@@ -50,6 +50,7 @@ const app = new Vue({
     contentLoading: false,
     offline: false,
     backgroundActive: true,
+    snackbarItems: [],
   }),
   router,
   i18n,
diff --git a/aleksis/core/frontend/mixins/aleksis.js b/aleksis/core/frontend/mixins/aleksis.js
index 396345dcbc988334823ffaead235d66cedd7eb65..036e2eac71db6304bba88e2a01c3cfbe33edca51 100644
--- a/aleksis/core/frontend/mixins/aleksis.js
+++ b/aleksis/core/frontend/mixins/aleksis.js
@@ -19,6 +19,9 @@ const aleksisMixin = {
       });
     },
   },
+  mounted() {
+    this.$root.contentLoading = false;
+  },
   beforeDestroy() {
     // Unregister all safely added event listeners as to not leak them
     for (let trackedEvent in this.$data.$_aleksis_safeTrackedEvents) {