diff --git a/package-lock.json b/package-lock.json
index 6aa4a668de5fe10d69877598481f004bd6e601d4..f6bc7dff255fbaaa4c57f7256814d58707850ca0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3259,6 +3259,11 @@
 				"resolve": "^1.12.0"
 			}
 		},
+		"babel-helper-vue-jsx-merge-props": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
+			"integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg=="
+		},
 		"babel-loader": {
 			"version": "8.2.1",
 			"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.1.tgz",
@@ -11316,6 +11321,14 @@
 				"tinycolor2": "^1.1.2"
 			}
 		},
+		"vue-content-loader": {
+			"version": "0.2.3",
+			"resolved": "https://registry.npmjs.org/vue-content-loader/-/vue-content-loader-0.2.3.tgz",
+			"integrity": "sha512-gJlNEdXkuHGvgnyY0lBMsrSsOMk+TTog5uNAil5MSLv08f/mE7Apag0VavpxJ6ieb4P5J1iVKEIhHI41HQNq9Q==",
+			"requires": {
+				"babel-helper-vue-jsx-merge-props": "^2.0.3"
+			}
+		},
 		"vue-eslint-parser": {
 			"version": "7.1.1",
 			"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.1.tgz",
diff --git a/package.json b/package.json
index d173c44a685e3bc8c9c21ba780f8ba9d8699c78b..6507d10792a26694e79b9ed94833fe166795ea24 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
 		"vue": "^2.6.12",
 		"vue-blurhash": "^0.1.2",
 		"vue-clipboard2": "^0.3.1",
+		"vue-content-loader": "^0.2.3",
 		"vue-resize-observer": "^1.0.32",
 		"vue-router": "^3.4.9",
 		"vue-show-more-text": "^2.0.2",
diff --git a/src/router.js b/src/router.js
index 5eaaa6907040e1add837634e12311ba07198f677..bc7e9c6045e9618ca698752fc607d6df57715125 100644
--- a/src/router.js
+++ b/src/router.js
@@ -27,6 +27,7 @@ import axios from '@nextcloud/axios'
 
 import Main from './components/Main'
 import Show from './views/Show'
+import ShowEmpty from './views/ShowEmpty'
 import Episode from './views/Episode'
 import store from './store.js'
 
@@ -39,7 +40,7 @@ const router = new Router({
 	routes: [
 		{
 			path: '/listening',
-			component: Main,
+			component: ShowEmpty,
 			name: 'LISTENING',
 		},
 		{
diff --git a/src/views/Show.vue b/src/views/Show.vue
index 0847e7775ddb8ef699a1b113730c2ac565bd58c7..76e8bc90de903948b982cc1e69c2a02ad63d883e 100644
--- a/src/views/Show.vue
+++ b/src/views/Show.vue
@@ -19,52 +19,51 @@
   - along with this program. If not, see <http://www.gnu.org/licenses/>.
   -
   -->
-
 <template>
 	<div>
-		<div
-			class="podcastHeaderBg"
-			:style="{ backgroundImage: `url(${podcast.smallImageURL})` }">
-			<div class="podcastHeader">
+		<ShowEmpty v-show="pageLoading" />
+		<transition name="fade">
+			<div v-show="!pageLoading">
 				<div
-					class="podcastImage"
-					:style="{ backgroundImage: `url(${podcast.smallImageURL})` }" />
-				<div class="podcastDescription">
-					<h1>{{ podcast.title }}</h1>
-					<div class="podcastAuthor">
-						by <a :href="podcast.htmlURL" target="_blank">{{ podcast.author }}</a>
-					</div>
-					<div class="podcastControls">
-						<a href="#" class="button">Subscribe</a>
-						<ul class="podcastCategory">
-							<a v-for="(category, idx) in podcast.categories"
-								:key="idx"
-								:href="`#/browse/category/${podcast.categories[idx]}`">
-								<li>
-									{{ getCategoryName(podcast.categories[idx]) }}
-								</li>
-							</a>
-						</ul>
+					class="podcastHeaderBg"
+					:style="{ backgroundImage: `url(${podcast.smallImageURL})` }">
+					<div class="podcastHeader">
+						<div
+							class="podcastImage"
+							:style="{ backgroundImage: `url(${podcast.smallImageURL})` }" />
+						<div class="podcastDescription">
+							<h1>{{ podcast.title }}</h1>
+							<div class="podcastAuthor">
+								by <a :href="podcast.htmlURL" target="_blank">{{ podcast.author }}</a>
+							</div>
+							<div class="podcastControls">
+								<a href="#" class="button">Subscribe</a>
+								<ul class="podcastCategory">
+									<a v-for="(category, idx) in podcast.categories"
+										:key="idx"
+										:href="`#/browse/category/${podcast.categories[idx]}`">
+										<li>
+											{{ getCategoryName(podcast.categories[idx]) }}
+										</li>
+									</a>
+								</ul>
+							</div>
+							<vue-show-more-text
+								:text="podcast.description"
+								additional-container-css="padding: 0px;" />
+						</div>
 					</div>
-					<vue-show-more-text
-						:text="podcast.description"
-						additional-container-css="padding: 0px;" />
 				</div>
+				<Table
+					v-resize="onResize"
+					:episodes="podcast.episodes"
+					@doPlay="doPlay" />
 			</div>
-		</div>
-		<Table
-			v-show="!pageLoading && podcast.episodes.length > 0"
-			v-resize="onResize"
-			:episodes="podcast.episodes"
-			@doPlay="doPlay" />
-		<EmptyContent
-			v-if="pageLoading"
-			icon="icon-loading" />
+		</transition>
 	</div>
 </template>
 
 <script>
-import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
 import Table from '../components/Table'
 
 import { showError } from '@nextcloud/dialogs'
@@ -75,12 +74,14 @@ import vueShowMoreText from 'vue-show-more-text'
 import { audioPlayer, doPlay } from '../services/player'
 import { getCategoryName } from '../services/podcastApi'
 
+import ShowEmpty from './ShowEmpty'
+
 export default {
 	name: 'Show',
 	components: {
 		Table,
-		EmptyContent,
 		vueShowMoreText,
+		ShowEmpty,
 	},
 	data: () => ({
 		podcast: {
diff --git a/src/views/ShowEmpty.vue b/src/views/ShowEmpty.vue
new file mode 100644
index 0000000000000000000000000000000000000000..969ff8efdebc3ec68c9d9b1b0d9c9400df42352e
--- /dev/null
+++ b/src/views/ShowEmpty.vue
@@ -0,0 +1,180 @@
+<!--
+  - @copyright Copyright (c) 2020 Jonas Heinrich
+  -
+  - @author Jonas Heinrich <onny@project-insanity.org>
+  -
+  - @license GNU AGPL version 3 or any later version
+  -
+  - This program is free software: you can redistribute it and/or modify
+  - it under the terms of the GNU Affero General Public License as
+  - published by the Free Software Foundation, either version 3 of the
+  - License, or (at your option) any later version.
+  -
+  - This program is distributed in the hope that it will be useful,
+  - but WITHOUT ANY WARRANTY; without even the implied warranty of
+  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  - GNU Affero General Public License for more details.
+  -
+  - You should have received a copy of the GNU Affero General Public License
+  - along with this program. If not, see <http://www.gnu.org/licenses/>.
+  -
+  -->
+
+<template>
+	<div>
+		<div class="podcastHeaderEmpty">
+			<div class="podcastImageEmpty" />
+			<div class="podcastDescriptionEmpty">
+				<div class="podcastDescriptionTitle" />
+				<div
+					class="podcastDescriptionSubTitle"
+					style="width: 300px;" />
+				<div
+					class="podcastDescriptionSubTitle"
+					style="width: 400px;" />
+				<div
+					class="podcastDescriptionSubTitle"
+					style="width: 200px;" />
+			</div>
+		</div>
+		<table>
+			<thead>
+				<tr>
+					<th class="iconColumn" />
+					<th class="nameColumn">
+						{{ t('podcast', 'Name') }}
+					</th>
+					<th class="actionColumn" />
+					<th class="durationColumn">
+						{{ t('podcast', 'Duration') }}
+					</th>
+					<th class="dateColumn">
+						{{ t('podcast', 'Date') }}
+					</th>
+				</tr>
+			</thead>
+			<tbody>
+				<tr v-for="index in 3" :key="index">
+					<td class="iconColumn">
+						<div class="episodeImageEmpty" />
+					</td>
+					<td class="nameColumn">
+						<div
+							class="episodeDescription"
+							style="width: 500px;" />
+						<div
+							class="episodeDescription"
+							style="width: 400px;" />
+					</td>
+					<td class="actionColumn" />
+					<td class="durationColumn" />
+					<td class="dateColumn" />
+				</tr>
+			</tbody>
+		</table>
+	</div>
+</template>
+
+<script>
+
+export default {
+	name: 'ShowEmpty',
+}
+</script>
+
+<style lang="scss">
+
+$base-color: #ddd;
+$shine-color: #e8e8e8;
+$animation-duration: 1.6s;
+$avatar-offset: 52 + 16;
+
+@mixin background-gradient {
+	background-image: linear-gradient(90deg, $base-color 0px, $shine-color 40px, $base-color 80px);
+	background-size: 600px;
+}
+
+.podcastHeaderEmpty {
+	width: 100%;
+	min-height: 300px;
+	display: flex;
+	justify-content: center;
+	padding: 40px 20px;
+	background-color: #f2f2f2;
+}
+
+.podcastImageEmpty {
+	width: 200px;
+	height: 200px;
+	background-color: #ccc;
+	margin-right: 25px;
+	border-radius: 5px;
+
+	@include background-gradient;
+	animation: shine-avatar $animation-duration infinite linear;
+}
+
+.podcastDescriptionEmpty {
+	max-width: 500px;
+}
+
+.podcastDescriptionTitle {
+	width: 500px;
+	height: 35px;
+	border-radius: 5px;
+	margin-bottom: 30px;
+	background-color: #ddd;
+
+	@include background-gradient;
+	animation: shine-lines $animation-duration infinite linear;
+}
+
+.podcastDescriptionSubTitle {
+	height: 20px;
+	border-radius: 5px;
+	margin-bottom: 10px;
+	background-color: #ddd;
+
+	@include background-gradient;
+	animation: shine-lines $animation-duration infinite linear;
+}
+
+.episodeImageEmpty {
+	width: 74px;
+	height: 74px;
+	background-color: #ccc;
+	border-radius: 5px;
+
+	@include background-gradient;
+	animation: shine-avatar $animation-duration infinite linear;
+}
+
+.episodeDescription {
+	height: 20px;
+	border-radius: 5px;
+	margin-bottom: 10px;
+	background-color: #ddd;
+
+	@include background-gradient;
+	animation: shine-lines $animation-duration infinite linear;
+}
+
+@keyframes shine-lines {
+	0% {
+		background-position: -100px;
+	}
+	40%, 100% {
+		background-position: 140px;
+	}
+}
+
+@keyframes shine-avatar {
+	0% {
+		background-position: -100px + $avatar-offset;
+	}
+	40%, 100% {
+		background-position: 140px + $avatar-offset;
+	}
+}
+
+</style>