diff --git a/package-lock.json b/package-lock.json
index 5427a9f29418634c9f5df80ccef7065c9364874c..0930943b179636984fc7469c445be3066636c6c1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2974,6 +2974,11 @@
       "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
       "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
     },
+    "arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
+    },
     "argparse": {
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -4300,6 +4305,11 @@
         }
       }
     },
+    "clsx": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+      "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
+    },
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -4646,6 +4656,11 @@
         "sha.js": "^2.4.8"
       }
     },
+    "create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
+    },
     "cross-fetch": {
       "version": "3.0.6",
       "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz",
@@ -5256,6 +5271,11 @@
         }
       }
     },
+    "diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
+    },
     "diff-sequences": {
       "version": "26.6.2",
       "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
@@ -10462,6 +10482,11 @@
         }
       }
     },
+    "make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
+    },
     "makeerror": {
       "version": "1.0.11",
       "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
@@ -13396,6 +13421,15 @@
         "workbox-webpack-plugin": "5.1.4"
       }
     },
+    "react-tabs": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-3.1.2.tgz",
+      "integrity": "sha512-OKS1l7QzSNcn+L2uFsxyGFHdXp9YsPGf/YOURWcImp7xLN36n0Wz+/j9HwlwGtlXCZexwshScR5BrcFbw/3P9Q==",
+      "requires": {
+        "clsx": "^1.1.0",
+        "prop-types": "^15.5.0"
+      }
+    },
     "react-transition-group": {
       "version": "4.4.1",
       "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
@@ -15709,6 +15743,19 @@
       "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
       "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA=="
     },
+    "ts-node": {
+      "version": "9.1.1",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
+      "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
+      "requires": {
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "source-map-support": "^0.5.17",
+        "yn": "3.1.1"
+      }
+    },
     "ts-pnp": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
@@ -17684,6 +17731,11 @@
       "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
       "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
     },
+    "yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
+    },
     "yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index 69807f7a639b25bba10cb2e702ec938b1beba339..274d8c70db4d74fc7487f7aa9f8f4019a758d54e 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
     "react-dom": "^17.0.1",
     "react-router-dom": "5.2.0",
     "react-scripts": "4.0.0",
+    "react-tabs": "3.1.2",
     "roslib": "1.1.0",
     "rxjs": "6.6.3",
     "ts-node": "^9.1.1",
@@ -66,7 +67,12 @@
         "error",
         "consistent"
       ],
-      "max-len": ["error", { "code": 120 }]
+      "max-len": [
+        "error",
+        {
+          "code": 120
+        }
+      ]
     }
   },
   "browserslist": {
@@ -85,4 +91,4 @@
     "jest-fetch-mock": "^3.0.3",
     "msw": "^0.23.0"
   }
-}
\ No newline at end of file
+}
diff --git a/src/components/experiment-list/experiment-list-element.js b/src/components/experiment-list/experiment-list-element.js
index 9ae86d2ac15aebd2e83087f3d09bffc563af2e6c..9f7982ed5bd3477f3568ea39470eb1fcf74c2c07 100644
--- a/src/components/experiment-list/experiment-list-element.js
+++ b/src/components/experiment-list/experiment-list-element.js
@@ -2,9 +2,9 @@ import React from 'react';
 import timeDDHHMMSS from '../../utility/time-filter.js';
 import ExperimentStorageService from '../../services/experiments/storage/experiment-storage-service.js';
 import ExperimentExecutionService from '../../services/experiments/execution/experiment-execution-service.js';
+import ExperimentServerService from '../../services/experiments/execution/experiment-server-service.js';
 
 import './experiment-list-element.css';
-import ExperimentServerService from '../../services/experiments/execution/experiment-server-service.js';
 
 const CLUSTER_THRESHOLDS = {
   UNAVAILABLE: 2,
@@ -15,7 +15,7 @@ const SHORT_DESCRIPTION_LENGTH = 200;
 export default class ExperimentListElement extends React.Component {
   constructor(props) {
     super(props);
-    this.state = {};
+    this.state = {availableServers: []};
 
     this.canLaunchExperiment = (this.props.experiment.private && this.props.experiment.owned) ||
     !this.props.experiment.private;
@@ -29,13 +29,27 @@ export default class ExperimentListElement extends React.Component {
     let thumbnail = await ExperimentStorageService.instance.getThumbnail(
       this.props.experiment.name,
       this.props.experiment.configuration.thumbnail);
-    this.setState({ thumbnail: URL.createObjectURL(thumbnail) });
+    this.setState({
+      thumbnail: URL.createObjectURL(thumbnail)
+    });
 
     document.addEventListener('mousedown', this.handleClickOutside);
+
+    this.onUpdateServerAvailability = (availableServers) => {
+      this.setState({availableServers: availableServers});
+    };
+    ExperimentServerService.instance.addListener(
+      ExperimentServerService.EVENTS.UPDATE_SERVER_AVAILABILITY,
+      this.onUpdateServerAvailability
+    );
   }
 
   componentWillUnmount() {
     document.removeEventListener('mousedown', this.handleClickOutside);
+    this.onUpdateServerAvailability && ExperimentServerService.instance.removeListener(
+      ExperimentServerService.EVENTS.UPDATE_SERVER_AVAILABILITY,
+      this.onUpdateServerAvailability
+    );
   }
 
   handleClickOutside(event) {
@@ -45,14 +59,13 @@ export default class ExperimentListElement extends React.Component {
   }
 
   getAvailabilityInfo() {
-    const experiment = this.props.experiment;
     const clusterAvailability = ExperimentServerService.instance.getClusterAvailability();
 
     let status;
     if (clusterAvailability && clusterAvailability.free > CLUSTER_THRESHOLDS.AVAILABLE) {
       status = 'Available';
     }
-    else if (!experiment.availableServers || experiment.availableServers.length === 0) {
+    else if (!this.state.availableServers || this.state.availableServers.length === 0) {
       status = 'Unavailable';
     }
     else {
@@ -60,20 +73,19 @@ export default class ExperimentListElement extends React.Component {
     }
 
     let cluster = `Cluster availability: ${clusterAvailability.free} / ${clusterAvailability.total}`;
-    let backends = `Backends: ${experiment.availableServers.length}`;
+    let backends = `Backends: ${this.state.availableServers.length}`;
 
     return `${status}\n${cluster}\n${backends}`;
   }
 
   getServerStatusClass() {
-    const experiment = this.props.experiment;
     const clusterAvailability = ExperimentServerService.instance.getClusterAvailability();
 
     let status = '';
     if (clusterAvailability && clusterAvailability.free > CLUSTER_THRESHOLDS.AVAILABLE) {
       status = 'server-status-available';
     }
-    else if (!experiment.availableServers || experiment.availableServers.length === 0) {
+    else if (!this.state.availableServers || this.state.availableServers.length === 0) {
       status = 'server-status-unavailable';
     }
     else {
@@ -86,7 +98,7 @@ export default class ExperimentListElement extends React.Component {
   render() {
     const exp = this.props.experiment;
     const config = this.props.experiment.configuration;
-    const pageState = this.props.pageState;
+    const pageState = this.props.pageState;  //TODO: to be removed, migrate to services
 
     return (
       <div className='list-entry-wrapper flex-container left-right'
@@ -136,27 +148,29 @@ export default class ExperimentListElement extends React.Component {
             return exp.id === pageState.selected;
           }}>
             <div className='btn-group' role='group' >
-              {this.canLaunchExperiment && exp.availableServers.length > 0 &&
+              {this.canLaunchExperiment && this.state.availableServers.length > 0 &&
                 exp.configuration.experimentFile && exp.configuration.bibiConfSrc
                 ? <button onClick={() => {
-                  return ExperimentExecutionService.instance.startingExperiment === exp.id ||
-                    ExperimentExecutionService.instance.startNewExperiment(exp, false);
+                  ExperimentExecutionService.instance.startNewExperiment(exp, false);
                 }}
-                disabled={pageState.startingExperiment === exp.id || pageState.deletingExperiment}
+                //TODO: adjust disabled state to be reactive
+                disabled={ExperimentExecutionService.instance.startingExperiment === exp
+                  || pageState.deletingExperiment}
                 className='btn btn-default' >
                   <i className='fa fa-plus'></i> Launch
                 </button>
                 : null}
 
-              {this.canLaunchExperiment && exp.availableServers.length === 0
-                ? <button className='btn btn-default disabled enable-tooltip'
+              {this.canLaunchExperiment && this.state.availableServers.length === 0
+                ? <button disabled={this.canLaunchExperiment && this.state.availableServers.length === 0}
+                  className='btn btn-default disabled enable-tooltip'
                   title='Sorry, no available servers.'>
                   <i className='fa fa-plus'></i> Launch
                 </button>
                 : null}
 
               {this.canLaunchExperiment && config.brainProcesses > 1 &&
-                exp.availableServers.length > 0 &&
+                this.state.availableServers.length > 0 &&
                 exp.configuration.experimentFile && exp.configuration.bibiConfSrc
 
                 ? <button className='btn btn-default'>
@@ -164,7 +178,7 @@ export default class ExperimentListElement extends React.Component {
                 </button>
                 : null}
 
-              {this.canLaunchExperiment && exp.availableServers.length > 1 &&
+              {this.canLaunchExperiment && this.state.availableServers.length > 1 &&
                 exp.configuration.experimentFile && exp.configuration.bibiConfSrc
 
                 ? <button className='btn btn-default' >
@@ -193,7 +207,7 @@ export default class ExperimentListElement extends React.Component {
                 </button>
                 : null}
 
-              {/* Join button */}
+              {/* Simulations button */}
               {this.canLaunchExperiment && exp.joinableServers.length > 0
                 ? <button className='btn btn-default' >
                   <i className='fa fa-sign-in'></i> Simulations »
diff --git a/src/components/experiment-list/experiment-list.js b/src/components/experiment-list/experiment-list.js
index 9087576f77b2e770feba6aacabed97bb97a4099d..b9d0817220f9793b45807f961537e3db5696c8a7 100644
--- a/src/components/experiment-list/experiment-list.js
+++ b/src/components/experiment-list/experiment-list.js
@@ -3,6 +3,7 @@ import { Link } from 'react-router-dom';
 
 import UserMenu from '../user-menu/user-menu.js';
 import ExperimentStorageService from '../../services/experiments/storage/experiment-storage-service.js';
+import ExperimentServerService from '../../services/experiments/execution/experiment-server-service.js';
 
 import ExperimentListElement from './experiment-list-element.js';
 
@@ -60,7 +61,8 @@ export default class ExperimentList extends React.Component {
             {this.state.experiments.map(experiment => {
               return (
                 <li key={experiment.id} className='nostyle'>
-                  <ExperimentListElement experiment={experiment} pageState={this.state.pageState} />
+                  <ExperimentListElement experiment={experiment}
+                    pageState={this.state.pageState} />
                 </li>
               );
             }
diff --git a/src/services/experiments/execution/experiment-execution-service.js b/src/services/experiments/execution/experiment-execution-service.js
index e6ff4a1bc551a5b198e1bf4c50946673d46ad468..2708862a9dc4242ffb3beac6fae51512efccd253 100644
--- a/src/services/experiments/execution/experiment-execution-service.js
+++ b/src/services/experiments/execution/experiment-execution-service.js
@@ -47,10 +47,11 @@ class ExperimentExecutionService extends HttpService {
 
     this.startingExperiment = experiment;
 
+    console.info(ExperimentServerService.instance.getServerAvailability(true));
     let fatalErrorOccurred = false,
       serversToTry = experiment.devServer
         ? [experiment.devServer]
-        : experiment.availableServers.map(s => s.id);
+        : ExperimentServerService.instance.getServerAvailability(true).map(s => s.id);
 
     let brainProcesses = launchSingleMode ? 1 : experiment.configuration.brainProcesses;
 
diff --git a/src/services/experiments/execution/experiment-server-service.js b/src/services/experiments/execution/experiment-server-service.js
index 8443c8d7e662bc47c28bbe2b3e479ddbef53deed..3ea09ac84e9e689cab2fbbbdcfcffa637e2cd3e9 100644
--- a/src/services/experiments/execution/experiment-server-service.js
+++ b/src/services/experiments/execution/experiment-server-service.js
@@ -11,12 +11,14 @@ import endpoints from '../../proxy/data/endpoints.json';
 import config from '../../../config.json';
 const proxyServerURL = `${config.api.proxy.url}${endpoints.proxy.server.url}`;
 const slurmMonitorURL = `${config.api.slurmmonitor.url}/api/v1/partitions/interactive`;
+const availableServersURL = `${config.api.proxy.url}${endpoints.proxy.availableServers.url}`;
 
 let _instance = null;
 const SINGLETON_ENFORCER = Symbol();
 
 let rosConnections = new Map();
 const SLURM_MONITOR_POLL_INTERVAL = 5000;
+const SERVER_AVAILABILITY_POLL_INTERVAL = 5000;
 let clusterAvailability = { free: 'N/A', total: 'N/A' };
 
 /**
@@ -49,7 +51,12 @@ class ExperimentServerService extends HttpService {
       .pipe(map(({ free, nodes }) => ({ free, total: nodes[3] })))
       .pipe(multicast(new Subject())).refCount();
 
+    this.listOfAvailableServers = [];
+
     this.startUpdates();
+    window.onbeforeunload = () => {
+      this.stopUpdates();
+    };
   }
 
   static get instance() {
@@ -60,6 +67,13 @@ class ExperimentServerService extends HttpService {
     return _instance;
   }
 
+  /**
+   * Get available servers.
+   */
+  get availableServers() {
+    return this.listOfAvailableServers;
+  }
+
   /**
    * Start polling updates.
    */
@@ -67,14 +81,21 @@ class ExperimentServerService extends HttpService {
     this.clusterAvailabilitySubscription = this.clusterAvailabilityObservable.subscribe(
       availability => (clusterAvailability = availability)
     );
+
+    this.timerPollServerAvailability = setInterval(
+      () => {
+        this.getServerAvailability(true);
+      },
+      SERVER_AVAILABILITY_POLL_INTERVAL
+    );
   }
 
   /**
    * Stop polling updates.
    */
-  //TODO: find proper place to call
   stopUpdates() {
     this.clusterAvailabilitySubscription && this.clusterAvailabilitySubscription.unsubscribe();
+    this.timerPollServerAvailability && clearInterval(this.timerPollServerAvailability);
   }
 
   /**
@@ -85,6 +106,19 @@ class ExperimentServerService extends HttpService {
     return clusterAvailability;
   }
 
+  getServerAvailability(forceUpdate = false) {
+    if (!this.listOfAvailableServers || forceUpdate) {
+      let update = async () => {
+        let response = await this.httpRequestGET(availableServersURL);
+        this.listOfAvailableServers = await response.json();
+      };
+      update();
+      this.emit(ExperimentServerService.EVENTS.UPDATE_SERVER_AVAILABILITY, this.listOfAvailableServers);
+    }
+
+    return this.listOfAvailableServers;
+  }
+
   /**
    * Get the server config for a given server ID.
    * @param {string} serverID - ID of the server
@@ -204,4 +238,8 @@ class ExperimentServerService extends HttpService {
   };
 }
 
+ExperimentServerService.EVENTS = Object.freeze({
+  UPDATE_SERVER_AVAILABILITY: 'UPDATE_SERVER_AVAILABILITY'
+});
+
 export default ExperimentServerService;
diff --git a/src/services/experiments/storage/experiment-storage-service.js b/src/services/experiments/storage/experiment-storage-service.js
index 8cc5cd6d41b75d6fc3c434160c375a6fc177a9d2..ca7cda948bdfd15eef9b44fe68a79eb825851fb3 100644
--- a/src/services/experiments/storage/experiment-storage-service.js
+++ b/src/services/experiments/storage/experiment-storage-service.js
@@ -3,7 +3,6 @@ import { HttpService } from '../../http-service.js';
 import endpoints from '../../proxy/data/endpoints.json';
 import config from '../../../config.json';
 const storageExperimentsURL = `${config.api.proxy.url}${endpoints.proxy.storage.experiments.url}`;
-const availableServersURL = `${config.api.proxy.url}${endpoints.proxy.availableServers.url}`;
 
 let _instance = null;
 const SINGLETON_ENFORCER = Symbol();
@@ -35,8 +34,9 @@ class ExperimentStorageService extends HttpService {
    *
    * @return experiments - the list of template experiments
    */
-  async getExperiments() {
-    if (!this.experiments) {
+  async getExperiments(forceUpdate = false) {
+    console.info('getExperiments');
+    if (!this.experiments || forceUpdate) {
       let response = await this.httpRequestGET(storageExperimentsURL);
       this.experiments = await response.json();
       this.sortExperiments();
@@ -78,11 +78,7 @@ class ExperimentStorageService extends HttpService {
   }
 
   async fillExperimentDetails() {
-    let response = await this.httpRequestGET(availableServersURL);
-    let availableServers = await response.json();
-
     this.experiments.forEach(exp => {
-      exp.availableServers = availableServers;
       if (!exp.configuration.brainProcesses && exp.configuration.bibiConfSrc) {
         exp.configuration.brainProcesses = 1;
       }
diff --git a/src/services/http-service.js b/src/services/http-service.js
index 0e1e89cc33a895d836d1204c334ccb79a520967a..16cae76bcc5e68752e06f9d7ae04f9ef945b9731 100644
--- a/src/services/http-service.js
+++ b/src/services/http-service.js
@@ -1,3 +1,6 @@
+
+import {EventEmitter} from 'events';
+
 import AuthenticationService from './authentication-service.js';
 
 /**
@@ -5,11 +8,13 @@ import AuthenticationService from './authentication-service.js';
  * If children need other options they can override the options or the
  * http verb (GET, POST, PUT etc) functions.
  */
-export class HttpService {
+export class HttpService extends EventEmitter {
   /**
    * Create a simple http request object with default options, default method is GET
    */
   constructor() {
+    super();
+
     this.options = {
       method: 'GET', // *GET, POST, PUT, DELETE, etc.
       mode: 'cors', // no-cors, *cors, same-origin