diff --git a/gms-ui/.env.production b/gms-ui/.env.production
index e86ff0cf8400a8ad9c810068c5173b54aba64879..384270e51d87fb2718f8550b2360037269c9ee7f 100644
--- a/gms-ui/.env.production
+++ b/gms-ui/.env.production
@@ -1,2 +1,2 @@
 VUE_APP_API_CLIENT = 'server'
-BASE_API_URL = 'http://localhost:8081/'
+VUE_APP_API_BASE_URL = 'http://localhost:8081/'
diff --git a/gms-ui/src/App.vue b/gms-ui/src/App.vue
index ed823404a633fc965f37c41d34c5a7d70b9994b2..98a824186070dd313fff1c0085d45468f54584c4 100644
--- a/gms-ui/src/App.vue
+++ b/gms-ui/src/App.vue
@@ -4,6 +4,11 @@
     <div class="container">
       <Main />
     </div>
+    <div id="loading" v-if="loading">
+      <div id="spinner-wrapper">
+        <font-awesome-icon icon="spinner" spin />
+      </div>
+    </div>
   </div>
 </template>
 
@@ -11,7 +16,7 @@
 import TopMenu from './components/TopMenu.vue';
 import Main from './components/Main.vue';
 import { mapState } from 'vuex';
-//import { ToastPlugin } from 'bootstrap-vue';
+import client from 'api-client';
 
 export default {
   name: 'app',
@@ -20,11 +25,11 @@ export default {
     Main
   },
   computed: mapState({
-    model: state => state.model
+    model: state => state.model,
+    input: state => state.input,
+    loading: state => state.loading
   }),
   mounted: function() {
-    this.$store.commit('fetchGroupsModel');
-
     var self = this;
     document.addEventListener('apiError', function (event) {
       self.$bvToast.toast(event.message, {
@@ -32,12 +37,21 @@ export default {
         variant: 'danger',
         solid: true
       });
-    })
+    });
+    document.addEventListener('loading', function (event) {
+      self.$store.commit('setLoading', event.value);
+    });
+
+    // retrieve the initial model
+    client.fetchHomePageModel(this.input)
+      .then(model => {
+        this.$store.commit('updateHomePageModel', model);
+      });
   }
 }
 </script>
 
-<style>
+<style scoped>
 #app {
   font-family: 'Avenir', Helvetica, Arial, sans-serif;
   -webkit-font-smoothing: antialiased;
@@ -45,4 +59,23 @@ export default {
   text-align: center;
   color: #2c3e50;
 }
+
+#loading {
+  font-size: 40px;
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  right: 0;
+  left: 0;
+  background-color: rgba(255, 255, 255, 0.7);
+  z-index: 1000;
+}
+
+#spinner-wrapper {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+  width: 100%;
+}
 </style>
diff --git a/gms-ui/src/api/server/index.js b/gms-ui/src/api/server/index.js
index 0e883d9bc93198aee4247f2be3b2c02e5d291e59..46344016b417e96b5e11d043d66c2b4dbfa55250 100644
--- a/gms-ui/src/api/server/index.js
+++ b/gms-ui/src/api/server/index.js
@@ -1,9 +1,11 @@
-const BASE_API_URL = "http://localhost:8081/"
+const BASE_API_URL = process.env.VUE_APP_API_BASE_URL;
 
 function apiRequest(url, data) {
+  loading(true);
   return new Promise((resolve, reject) => {
     fetch(url, data)
       .then(response => {
+        loading(false);
         if([200, 201, 204, 400].includes(response.status)) { // valid status codes
           resolve(response.json());
         } else {
@@ -11,6 +13,7 @@ function apiRequest(url, data) {
         }
       })
       .catch(error => {
+        loading(false);
         dispatchApiErrorEvent(error);
       });
   });
@@ -29,14 +32,35 @@ function dispatchApiErrorEvent(error) {
   document.dispatchEvent(event);
 }
 
+/* For loading animation */
+function loading(value) {
+  let event = new CustomEvent('loading');
+  event.value = value;
+  document.dispatchEvent(event);
+}
+
 export default {
-  fetchGroupsModel (input) {
+  fetchHomePageModel (input) {
+    let url = BASE_API_URL
+            + 'home?groupId=' + input.selectedGroupId
+            + '&paginatorPageSize=' + input.paginatorPageSize
+            + '&paginatorPage=' + input.paginatorPage;
+    return apiRequest(url, {
+      method: 'GET',
+      cache: 'no-cache',
+      credentials: 'include',
+      headers: {
+        'Content-Type': 'application/json',
+        'Accept': 'application/json',
+      }
+    });
+  },
+  fetchPanel (input) {
     let url = BASE_API_URL
-            + 'groups?groupId=' + input.selectedGroupId
-            + '&tab=' + input.selectedTab
+            + input.selectedTab
+            + '?groupId=' + input.selectedGroupId
             + '&paginatorPageSize=' + input.paginatorPageSize
-            + '&paginatorPage=' + input.paginatorPage
-            + '&page=' + input.page;
+            + '&paginatorPage=' + input.paginatorPage;
     return apiRequest(url, {
       method: 'GET',
       cache: 'no-cache',
diff --git a/gms-ui/src/components/GroupsBreadcrumb.vue b/gms-ui/src/components/GroupsBreadcrumb.vue
index b2458774e7ad2a5cc01a00a0c7f9adffdbe22fd5..ea10fc645f301b70829d84f9ea9f664613c02a4c 100644
--- a/gms-ui/src/components/GroupsBreadcrumb.vue
+++ b/gms-ui/src/components/GroupsBreadcrumb.vue
@@ -11,6 +11,7 @@
 
 <script>
 import { mapState } from 'vuex';
+import client from 'api-client';
 
 function buildItems(values) {
   let groups = [];
@@ -22,7 +23,9 @@ function buildItems(values) {
   }
 
   // Activate the last item
-  groups[groups.length - 1].active = true;
+  if(groups.length > 0) {
+    groups[groups.length - 1].active = true;
+  }
 
   return groups;
 }
@@ -31,12 +34,20 @@ export default {
   name: 'GroupsBreadcrumb',
   computed: mapState({
     model: state => state.model,
+    input: state => state.input,
     groups: state => buildItems(state.model.breadcrumbs)
   }),
   methods: {
     changeBreadcrumb: function(groupId) {
-      this.$store.state.input.selectedGroupId = groupId;
-      this.$store.commit('fetchGroupsModel');
+      this.input.selectedGroupId = groupId;
+      if(this.input.selectedTab === 'groups') {
+        client.fetchPanel(this.input)
+          .then(model => {
+            this.$store.commit('updateGroupsPanel', model);
+          });
+      } else {
+        this.$store.commit('setTabIndex', 0);
+      }
     }
   }
 }
diff --git a/gms-ui/src/components/GroupsPanel.vue b/gms-ui/src/components/GroupsPanel.vue
index 04b55fc0d7214cc337a9563a8cae9fd451dbf59e..cdc739a87eb93183db2e484b4840bc0d424594a9 100644
--- a/gms-ui/src/components/GroupsPanel.vue
+++ b/gms-ui/src/components/GroupsPanel.vue
@@ -56,6 +56,7 @@
 import RenameGroupModal from './modals/RenameGroupModal.vue';
 import ConfirmRemoveGroupModal from './modals/ConfirmRemoveGroupModal.vue';
 import { mapState, mapActions } from 'vuex';
+import client from 'api-client';
 
 export default {
   name: 'GroupsPanel',
@@ -80,7 +81,10 @@ export default {
   methods: {
     openGroup: function(group) {
       this.$store.state.input.selectedGroupId = group.groupId;
-      this.$store.commit('fetchGroupsModel');
+      client.fetchPanel(this.input)
+        .then(model => {
+          this.$store.commit('updateGroupsPanel', model);
+        });
     },
     openRenameGroupModal: function(group) {
       this.$refs.renameGroupModal.openRenameGroupModal(group);
diff --git a/gms-ui/src/components/Main.vue b/gms-ui/src/components/Main.vue
index 27862fd25a5a512f9aefc7d2d0a681d2e586e895..1e9a6f17976c2185571db0c488f3ecd8fc446e56 100644
--- a/gms-ui/src/components/Main.vue
+++ b/gms-ui/src/components/Main.vue
@@ -2,7 +2,7 @@
   <div>
     <GroupsBreadcrumb />
     <b-card no-body>
-      <b-tabs content-class="mt-3" card v-on:input="tabChanged">
+      <b-tabs content-class="mt-3" card v-on:input="tabChanged" v-model="input.tabIndex">
         <GroupsPanel />
         <MembersPanel />
         <PermissionsPanel />
@@ -29,6 +29,7 @@ import AddGroupModal from './modals/AddGroupModal.vue'
 import AddMemberModal from './modals/AddMemberModal.vue'
 import AddPermissionModal from './modals/AddPermissionModal.vue'
 import { mapState } from 'vuex';
+import client from 'api-client';
 
 export default {
   name: 'Main',
@@ -43,6 +44,7 @@ export default {
   },
   computed: mapState({
     model: state => state.model,
+    input: state => state.input,
     showAddMemberBtn: state => state.model.permission === 'ADMIN' && state.input.selectedTab === 'members',
     showAddCollaboratorBtn: state => state.model.permission === 'MANAGE_MEMBERS' && state.input.selectedTab === 'members',
     showAddGroupBtn: state => state.model.permission === 'ADMIN' && state.input.selectedTab === 'groups',
@@ -62,9 +64,22 @@ export default {
           tab = 'permissions';
           break;
       }
-      if(this.$store.state.input.selectedTab !== tab) {
-        this.$store.state.input.selectedTab = tab;
-        this.$store.commit('fetchGroupsModel');
+      if(this.input.selectedTab !== tab) {
+        this.input.selectedTab = tab;
+        client.fetchPanel(this.input)
+          .then(model => {
+            switch(this.input.selectedTab) {
+              case 'groups':
+                this.$store.commit('updateGroupsPanel', model);
+                break;
+              case 'members':
+                this.$store.commit('updateMembersPanel', model);
+                break;
+              case 'permissions':
+                this.$store.commit('updatePermissionsPanel', model);
+                break;
+            }
+          });
       }
     },
     openAddGroupModal: function() {
diff --git a/gms-ui/src/main.js b/gms-ui/src/main.js
index 9fb81c612d27ae5ef1f51dd5516bd8cab7609e85..1d6dc7e58bba7da507cc76cf736a39f203640430 100644
--- a/gms-ui/src/main.js
+++ b/gms-ui/src/main.js
@@ -5,10 +5,10 @@ import store from './store.js'
 import './plugins/bootstrap-vue'
 import App from './App.vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
-import { faTrash, faEdit } from '@fortawesome/free-solid-svg-icons'
+import { faTrash, faEdit, faSpinner } from '@fortawesome/free-solid-svg-icons'
 import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
 
-library.add(faTrash, faEdit);
+library.add(faTrash, faEdit, faSpinner);
 
 Vue.component('font-awesome-icon', FontAwesomeIcon);
 
diff --git a/gms-ui/src/plugins/bootstrap-vue.js b/gms-ui/src/plugins/bootstrap-vue.js
index c29a066a30dc2d0588c297632514a86edc1989cd..b9d414fabeae0480e997423c3a02ef5414b5a0b3 100644
--- a/gms-ui/src/plugins/bootstrap-vue.js
+++ b/gms-ui/src/plugins/bootstrap-vue.js
@@ -5,4 +5,3 @@ import 'bootstrap/dist/css/bootstrap.min.css';
 import 'bootstrap-vue/dist/bootstrap-vue.css';
 
 Vue.use(BootstrapVue);
-//Vue.use(ToastPlugin);
diff --git a/gms-ui/src/store.js b/gms-ui/src/store.js
index ddfed3b0703b3cb8d4014d015f11f62efafcb57e..48e0677fe93f176c094fd219f80b298b7a9f8087 100644
--- a/gms-ui/src/store.js
+++ b/gms-ui/src/store.js
@@ -2,38 +2,54 @@
 
 import Vue from 'vue';
 import Vuex from 'vuex';
-import client from 'api-client';
 
 Vue.use(Vuex);
 
 export default new Vuex.Store({
   state: {
     // values populated from API calls
-    model: null,
+    model: {
+      breadcrumbs: [],
+      groupsPanel: null,
+      permissionsPanel: null,
+      membersPanel: null,
+      permission: null,
+      user: null
+    },
     // values used to perform API calls
     input: {
       selectedGroupId: 'ROOT',
       paginatorPageSize: 20,
       paginatorPage: 1,
       selectedTab: 'groups',
-      page: 'main'
-    }
+      tabIndex: 0
+    },
+    loading: false
   },
   mutations: {
-    fetchGroupsModel(state) {
-      client.fetchGroupsModel(this.state.input)
-        .then(model => {
-          this.state.model = model;
-        });
+    updateHomePageModel(state, model) {
+      this.state.model.breadcrumbs = model.breadcrumbs;
+      this.state.model.groupsPanel = model.groupsPanel;
+      this.state.model.permission = model.permission;
+      this.state.model.user = model.user;
     },
-    updateGroupsPanel(state, groupsPanel) {
-      this.state.model.groupsPanel = groupsPanel;
+    updateGroupsPanel(state, model) {
+      this.state.model.breadcrumbs = model.breadcrumbs;
+      this.state.model.groupsPanel = model.groupsPanel;
+      this.state.model.permission = model.permission;
     },
     updatePermissionsPanel(state, permissionsPanel) {
       this.state.model.permissionsPanel = permissionsPanel;
     },
     updateMembersPanel(state, membersPanel) {
       this.state.model.membersPanel = membersPanel;
+    },
+    setTabIndex(state, tabIndex) {
+      // this will trigger the tabChanged() method in Main.vue
+      this.state.input.tabIndex = tabIndex;
+    },
+    setLoading(state, loading) {
+      this.state.loading = loading;
     }
   },
   getters: {
diff --git a/gms-ui/vue.config.js b/gms-ui/vue.config.js
index 06fc6e5e208122a1281dfe9eecba8a89a43b69d4..faddadb79b9d4c35bd8c0740575b9ffff2550f98 100644
--- a/gms-ui/vue.config.js
+++ b/gms-ui/vue.config.js
@@ -1,4 +1,4 @@
-const path = require('path')
+const path = require('path');
 
 module.exports = {
   chainWebpack: config => {