diff --git a/backend/src/baserow/api/v0/applications/serializers.py b/backend/src/baserow/api/v0/applications/serializers.py
index fe73d1b67..1f992c925 100644
--- a/backend/src/baserow/api/v0/applications/serializers.py
+++ b/backend/src/baserow/api/v0/applications/serializers.py
@@ -1,15 +1,17 @@
 from rest_framework import serializers
 
+from baserow.api.v0.groups.serializers import GroupSerializer
 from baserow.core.applications import registry
 from baserow.core.models import Application
 
 
 class ApplicationSerializer(serializers.ModelSerializer):
     type = serializers.SerializerMethodField()
+    group = GroupSerializer()
 
     class Meta:
         model = Application
-        fields = ('id', 'name', 'order', 'type')
+        fields = ('id', 'name', 'order', 'type', 'group')
         extra_kwargs = {
             'id': {
                 'read_only': True
diff --git a/backend/src/baserow/api/v0/applications/views.py b/backend/src/baserow/api/v0/applications/views.py
index f5fd73311..3d201a4b0 100644
--- a/backend/src/baserow/api/v0/applications/views.py
+++ b/backend/src/baserow/api/v0/applications/views.py
@@ -56,6 +56,18 @@ class ApplicationView(APIView):
     permission_classes = (IsAuthenticated,)
     core_handler = CoreHandler()
 
+    @map_exceptions({
+        UserNotIngroupError: ERROR_USER_NOT_IN_GROUP
+    })
+    def get(self, request, application_id):
+        """Selects a single application and responds with a serialized version."""
+        application = get_object_or_404(
+            Application.objects.select_related('group'),
+            pk=application_id, group__users__in=[request.user]
+        )
+
+        return Response(ApplicationSerializer(application).data)
+
     @transaction.atomic
     @validate_body(ApplicationUpdateSerializer)
     @map_exceptions({
diff --git a/backend/tests/baserow/api/v0/applications/test_application_views.py b/backend/tests/baserow/api/v0/applications/test_application_views.py
index 7f0a8272d..e2ee16822 100644
--- a/backend/tests/baserow/api/v0/applications/test_application_views.py
+++ b/backend/tests/baserow/api/v0/applications/test_application_views.py
@@ -94,6 +94,46 @@ def test_create_application(api_client, data_fixture):
     assert response_json['order'] == database.order
 
 
+@pytest.mark.django_db
+def test_get_application(api_client, data_fixture):
+    user, token = data_fixture.create_user_and_token()
+    user_2, token_2 = data_fixture.create_user_and_token()
+    group = data_fixture.create_group(user=user)
+    group_2 = data_fixture.create_group(user=user_2)
+    application = data_fixture.create_database_application(group=group)
+    application_2 = data_fixture.create_database_application(group=group_2)
+
+    url = reverse('api_v0:applications:item',
+                  kwargs={'application_id': application_2.id})
+    response = api_client.get(
+        url,
+        format='json',
+        HTTP_AUTHORIZATION=f'JWT {token}'
+    )
+    assert response.status_code == 404
+
+    url = reverse('api_v0:applications:item',
+                  kwargs={'application_id': 99999})
+    response = api_client.get(
+        url,
+        format='json',
+        HTTP_AUTHORIZATION=f'JWT {token}'
+    )
+    assert response.status_code == 404
+
+    url = reverse('api_v0:applications:item',
+                  kwargs={'application_id': application.id})
+    response = api_client.get(
+        url,
+        format='json',
+        HTTP_AUTHORIZATION=f'JWT {token}'
+    )
+    response_json = response.json()
+    assert response.status_code == 200
+    assert response_json['id'] == application.id
+    assert response_json['group']['id'] == group.id
+
+
 @pytest.mark.django_db
 def test_update_application(api_client, data_fixture):
     user, token = data_fixture.create_user_and_token()
diff --git a/web-frontend/assets/scss/components/_tree.scss b/web-frontend/assets/scss/components/_tree.scss
index 0a7d1347a..63d193983 100644
--- a/web-frontend/assets/scss/components/_tree.scss
+++ b/web-frontend/assets/scss/components/_tree.scss
@@ -129,6 +129,18 @@
   }
 }
 
+.tree-sub-add {
+  display: inline-block;
+  margin: 0 0 10px 10px;
+  font-size: 12px;
+  color: $color-neutral-300;
+
+  &:hover {
+    text-decoration: none;
+    color: $color-neutral-500;
+  }
+}
+
 .tree-options {
   display: none;
   position: absolute;
diff --git a/web-frontend/components/sidebar/SidebarApplication.vue b/web-frontend/components/sidebar/SidebarApplication.vue
index 5d33da4e7..8a2a63e17 100644
--- a/web-frontend/components/sidebar/SidebarApplication.vue
+++ b/web-frontend/components/sidebar/SidebarApplication.vue
@@ -2,11 +2,12 @@
   <li
     class="tree-item"
     :class="{
+      active: application._.selected,
       'tree-item-loading': application._.loading
     }"
   >
     <div class="tree-action">
-      <a class="tree-link">
+      <a class="tree-link" @click="selectApplication(application)">
         <i
           class="tree-type fas"
           :class="'fa-' + application._.type.iconClass"
@@ -42,6 +43,16 @@
         </ul>
       </Context>
     </div>
+    <template
+      v-if="
+        application._.selected && application._.type.hasSelectedSidebarComponent
+      "
+    >
+      <component
+        :is="getSelectedApplicationComponent(application)"
+        :application="application"
+      ></component>
+    </template>
   </li>
 </template>
 
@@ -83,6 +94,24 @@ export default {
           this.setLoading(application, false)
         })
     },
+    selectApplication(application) {
+      this.setLoading(application, true)
+
+      this.$nuxt.$router.push(
+        {
+          name: application._.type.routeName,
+          params: {
+            id: application.id
+          }
+        },
+        () => {
+          this.setLoading(application, false)
+        },
+        () => {
+          this.setLoading(application, false)
+        }
+      )
+    },
     deleteApplication(application) {
       this.$refs.context.hide()
       this.setLoading(application, true)
@@ -90,6 +119,12 @@ export default {
       this.$store.dispatch('application/delete', application).then(() => {
         this.setLoading(application, false)
       })
+    },
+    getSelectedApplicationComponent(application) {
+      const type = this.$store.getters['application/getApplicationByType'](
+        application.type
+      )
+      return type.getSelectedSidebarComponent()
     }
   }
 }
diff --git a/web-frontend/config/nuxt.config.base.js b/web-frontend/config/nuxt.config.base.js
index 2efdff84d..b927c29d8 100644
--- a/web-frontend/config/nuxt.config.base.js
+++ b/web-frontend/config/nuxt.config.base.js
@@ -1,8 +1,8 @@
 export default {
   mode: 'universal',
 
-  /*
-   ** Headers of the page
+  /**
+   * Headers of the page
    */
   head: {
     title: 'Baserow',
@@ -12,18 +12,18 @@ export default {
     ]
   },
 
-  /*
-   ** Customize the progress-bar color
+  /**
+   * Customize the progress-bar color
    */
   loading: { color: '#fff' },
 
-  /*
-   ** Global CSS
+  /**
+   * Global CSS
    */
   css: ['@/assets/scss/default.scss'],
 
-  /*
-   ** Plugins to load before mounting the App
+  /**
+   * Plugins to load before mounting the App
    */
   plugins: [
     { src: '@/plugins/global.js' },
@@ -32,8 +32,8 @@ export default {
     { src: '@/plugins/vuelidate.js' }
   ],
 
-  /*
-   ** Nuxt.js modules
+  /**
+   * Nuxt.js modules
    */
   modules: [
     '@nuxtjs/axios',
diff --git a/web-frontend/core/applications.js b/web-frontend/core/applications.js
index 219936f05..8e7380eff 100644
--- a/web-frontend/core/applications.js
+++ b/web-frontend/core/applications.js
@@ -30,6 +30,14 @@ export class Application {
     return null
   }
 
+  /**
+   * Must return the route name where the application can navigate to when the
+   * application is selected.
+   */
+  getRouteName() {
+    return null
+  }
+
   /**
    * The form component that will be rendered when creating a new instance of
    * this application. By default the ApplicationForm component is returned, but
@@ -40,19 +48,32 @@ export class Application {
     return ApplicationForm
   }
 
+  /**
+   * The sidebar component that will be rendered when an application instance
+   * is selected. By default no component will rendered. This could be used for
+   * example to render a list of tables that belong to a database.
+   */
+  getSelectedSidebarComponent() {
+    return null
+  }
+
   constructor() {
     this.type = this.getType()
     this.iconClass = this.getIconClass()
     this.name = this.getName()
+    this.routeName = this.getRouteName()
 
     if (this.type === null) {
-      throw Error('The type name of an application must be set.')
+      throw new Error('The type name of an application must be set.')
     }
     if (this.iconClass === null) {
-      throw Error('The icon class of an application must be set.')
+      throw new Error('The icon class of an application must be set.')
     }
     if (this.name === null) {
-      throw Error('The name of an application must be set.')
+      throw new Error('The name of an application must be set.')
+    }
+    if (this.routeName === null) {
+      throw new Error('The route name of an application must be set.')
     }
   }
 
@@ -63,7 +84,9 @@ export class Application {
     return {
       type: this.type,
       iconClass: this.iconClass,
-      name: this.name
+      name: this.name,
+      routeName: this.routeName,
+      hasSelectedSidebarComponent: this.getSelectedSidebarComponent() !== null
     }
   }
 }
diff --git a/web-frontend/mixins/application.js b/web-frontend/mixins/application.js
new file mode 100644
index 000000000..d7b1875c9
--- /dev/null
+++ b/web-frontend/mixins/application.js
@@ -0,0 +1,29 @@
+import { notify404 } from '@/utils/error'
+
+/**
+ * This mixin can be used in combination with the page an application routes to
+ * when selected. It will make sure that the application preSelect action is
+ * called so that the all the depending information is loaded. If something
+ * goes wrong while loading this information it will show a standard error.
+ */
+export default {
+  props: {
+    id: {
+      type: Number,
+      required: true
+    }
+  },
+  mounted() {
+    this.$store.dispatch('application/preSelect', this.id).catch(error => {
+      notify404(
+        this.$store.dispatch,
+        error,
+        'Application not found.',
+        "The application with the provided id doesn't exist or you " +
+          "don't have access to it."
+      )
+
+      this.$nuxt.$router.push({ name: 'app' })
+    })
+  }
+}
diff --git a/web-frontend/modules/database/application.js b/web-frontend/modules/database/application.js
index 8a18f25ea..16a60c2b9 100644
--- a/web-frontend/modules/database/application.js
+++ b/web-frontend/modules/database/application.js
@@ -1,4 +1,5 @@
 import { Application } from '@/core/applications'
+import Sidebar from '@/modules/database/components/Sidebar'
 
 export class DatabaseApplication extends Application {
   getType() {
@@ -12,4 +13,12 @@ export class DatabaseApplication extends Application {
   getName() {
     return 'Database'
   }
+
+  getRouteName() {
+    return 'application-database'
+  }
+
+  getSelectedSidebarComponent() {
+    return Sidebar
+  }
 }
diff --git a/web-frontend/modules/database/components/Sidebar.vue b/web-frontend/modules/database/components/Sidebar.vue
new file mode 100644
index 000000000..0145d5cf5
--- /dev/null
+++ b/web-frontend/modules/database/components/Sidebar.vue
@@ -0,0 +1,38 @@
+<template>
+  <div>
+    <ul class="tree-subs">
+      <li class="tree-sub active">
+        <a href="#" class="tree-sub-link">@TODO</a>
+        <a
+          class="tree-options"
+          @click="
+            $refs.context.toggle($event.currentTarget, 'bottom', 'right', 0)
+          "
+        >
+          <i class="fas fa-ellipsis-v"></i>
+        </a>
+        <Context ref="context">
+          <div class="context-menu-title">@TODO</div>
+          <ul class="context-menu">
+            <li>
+              <a>
+                <i class="context-menu-icon fas fa-fw fa-pen"></i>
+                Rename
+              </a>
+            </li>
+            <li>
+              <a>
+                <i class="context-menu-icon fas fa-fw fa-trash"></i>
+                Delete
+              </a>
+            </li>
+          </ul>
+        </Context>
+      </li>
+    </ul>
+    <a href="#" class="tree-sub-add">
+      <i class="fas fa-plus"></i>
+      Create table
+    </a>
+  </div>
+</template>
diff --git a/web-frontend/modules/database/module.js b/web-frontend/modules/database/module.js
index 296c82ab6..ab867ce18 100644
--- a/web-frontend/modules/database/module.js
+++ b/web-frontend/modules/database/module.js
@@ -1,9 +1,15 @@
 import path from 'path'
 
+import { databaseRoutes } from './routes'
+
 export default function DatabaseModule(options) {
   // Add the plugin to register the database application.
   this.addPlugin({
     src: path.resolve(__dirname, 'plugin.js'),
     filename: 'plugin.js'
   })
+
+  this.extendRoutes(routes => {
+    routes.push(...databaseRoutes)
+  })
 }
diff --git a/web-frontend/modules/database/pages/Database.vue b/web-frontend/modules/database/pages/Database.vue
new file mode 100644
index 000000000..a1017bf21
--- /dev/null
+++ b/web-frontend/modules/database/pages/Database.vue
@@ -0,0 +1,29 @@
+<template>
+  <div>
+    <header class="layout-col-3-1 header">
+      <ul class="header-filter">
+        <li class="header-filter-item">&nbsp;</li>
+      </ul>
+      <ul class="header-info">
+        <li>{{ selectedApplication.name }}</li>
+        <li>@TODO table name</li>
+      </ul>
+    </header>
+  </div>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+
+import application from '@/mixins/application'
+
+export default {
+  layout: 'app',
+  mixins: [application],
+  computed: {
+    ...mapState({
+      selectedApplication: state => state.application.selected
+    })
+  }
+}
+</script>
diff --git a/web-frontend/modules/database/routes.js b/web-frontend/modules/database/routes.js
new file mode 100644
index 000000000..cc110db65
--- /dev/null
+++ b/web-frontend/modules/database/routes.js
@@ -0,0 +1,14 @@
+import path from 'path'
+
+export const databaseRoutes = [
+  {
+    name: 'application-database',
+    path: '/database/:id',
+    component: path.resolve(__dirname, 'pages/Database.vue'),
+    props(route) {
+      const props = { ...route.params }
+      props.id = parseInt(props.id)
+      return props
+    }
+  }
+]
diff --git a/web-frontend/pages/app/index.vue b/web-frontend/pages/app/index.vue
index 035ffa406..01d71e138 100644
--- a/web-frontend/pages/app/index.vue
+++ b/web-frontend/pages/app/index.vue
@@ -9,6 +9,11 @@
       {{ applications }}
       <br /><br />
       {{ groupApplications }}
+      <br /><br />
+      <nuxt-link :to="{ name: 'application-database', params: { id: 1 } }">
+        <i class="fas fa-arrow-left"></i>
+        App
+      </nuxt-link>
     </p>
   </div>
 </template>
diff --git a/web-frontend/services/application.js b/web-frontend/services/application.js
index e8313d217..359d0e61e 100644
--- a/web-frontend/services/application.js
+++ b/web-frontend/services/application.js
@@ -7,6 +7,9 @@ export default {
   create(groupId, values) {
     return client.post(`/applications/group/${groupId}/`, values)
   },
+  get(applicationId) {
+    return client.get(`/applications/${applicationId}/`)
+  },
   update(applicationId, values) {
     return client.patch(`/applications/${applicationId}/`, values)
   },
diff --git a/web-frontend/store/application.js b/web-frontend/store/application.js
index 905dfa0e2..0dfeb8552 100644
--- a/web-frontend/store/application.js
+++ b/web-frontend/store/application.js
@@ -3,9 +3,12 @@ import ApplicationService from '@/services/application'
 import { notify404, notifyError } from '@/utils/error'
 
 function populateApplication(application, getters) {
+  const type = getters.getApplicationByType(application.type)
+
   application._ = {
-    type: getters.getApplicationByType(application.type).serialize(),
-    loading: false
+    type: type.serialize(),
+    loading: false,
+    selected: false
   }
   return application
 }
@@ -13,7 +16,8 @@ function populateApplication(application, getters) {
 export const state = () => ({
   applications: {},
   loading: false,
-  items: []
+  items: [],
+  selected: {}
 })
 
 export const mutations = {
@@ -39,6 +43,19 @@ export const mutations = {
   DELETE_ITEM(state, id) {
     const index = state.items.findIndex(item => item.id === id)
     state.items.splice(index, 1)
+  },
+  SET_SELECTED(state, group) {
+    Object.values(state.items).forEach(item => {
+      item._.selected = false
+    })
+    group._.selected = true
+    state.selected = group
+  },
+  UNSELECT(state) {
+    Object.values(state.items).forEach(item => {
+      item._.selected = false
+    })
+    state.selected = {}
   }
 }
 
@@ -165,6 +182,77 @@ export const actions = {
             ' not part of the group where the application is in.'
         )
       })
+  },
+  /**
+   * Select an application.
+   */
+  select({ commit }, application) {
+    commit('SET_SELECTED', application)
+  },
+  /**
+   * Select an application by a given application id.
+   */
+  selectById({ dispatch, getters }, id) {
+    const application = getters.get(id)
+    if (application === undefined) {
+      throw new Error(`Application with id ${id} is not found.`)
+    }
+    return dispatch('select', application)
+  },
+  /**
+   * Unselect the
+   */
+  unselect({ commit }) {
+    commit('UNSELECT', {})
+  },
+  /**
+   * The preSelect action will eventually select an application, but it will
+   * first check which information still needs to be loaded. For example if
+   * no group or not the group where the application is in loaded it will then
+   * first fetch that group and related application so that the sidebar is up
+   * to date. In short it will make sure that the depending state of the given
+   * application will be there.
+   */
+  preSelect({ dispatch, getters, rootGetters }, id) {
+    // First we will check if the application is already in the items.
+    const application = getters.get(id)
+
+    // If the application is already selected we don't have to do anything.
+    if (application !== undefined && application._.selected) {
+      return
+    }
+
+    // This function will select a group by its id which will then automatically
+    // fetch the applications related to that group. When done it will select
+    // the provided application id.
+    const selectGroupAndApplication = (groupId, applicationId) => {
+      return dispatch('group/selectById', groupId, {
+        root: true
+      }).then(() => {
+        return dispatch('selectById', applicationId)
+      })
+    }
+
+    if (application !== undefined) {
+      // If the application is already in the selected groups, which means that
+      // the groups and applications are already loaded, we can just select that
+      // application.
+      dispatch('select', application)
+    } else {
+      // The application is not in the selected group so we need to figure out
+      // in which he is by fetching the application.
+      return ApplicationService.get(id).then(data => {
+        if (!rootGetters['group/isLoaded']) {
+          // If the groups are not already loaded we need to load them first.
+          return dispatch('group/fetchAll', {}, { root: true }).then(() => {
+            return selectGroupAndApplication(data.data.group.id, id)
+          })
+        } else {
+          // The groups are already loaded so we
+          return selectGroupAndApplication(data.data.group.id, id)
+        }
+      })
+    }
   }
 }
 
@@ -172,6 +260,9 @@ export const getters = {
   isLoading(state) {
     return state.loading
   },
+  get: state => id => {
+    return state.items.find(item => item.id === id)
+  },
   applicationTypeExists: state => type => {
     return state.applications.hasOwnProperty(type)
   },
diff --git a/web-frontend/store/group.js b/web-frontend/store/group.js
index 4ee74ae17..2736d53ea 100644
--- a/web-frontend/store/group.js
+++ b/web-frontend/store/group.js
@@ -135,7 +135,6 @@ export const actions = {
     return GroupService.delete(group.id)
       .then(() => {
         if (group._.selected) {
-          console.log('calling unselect')
           dispatch('unselect', group)
         }
 
@@ -159,6 +158,16 @@ export const actions = {
     setGroupCookie(group.id, this.app.$cookies)
     return dispatch('application/fetchAll', group, { root: true })
   },
+  /**
+   * Select a group by a given group id.
+   */
+  selectById({ dispatch, getters }, id) {
+    const group = getters.get(id)
+    if (group === undefined) {
+      throw new Error(`Group with id ${id} is not found.`)
+    }
+    return dispatch('select', group)
+  },
   /**
    * Unselect a group if selected and clears all the fetched applications.
    */