diff --git a/changes/changelog.d/719.enhancement b/changes/changelog.d/719.enhancement
new file mode 100644
index 0000000000000000000000000000000000000000..e4c5b35bebdbe2d0d98793649ed71639a7ddd071
--- /dev/null
+++ b/changes/changelog.d/719.enhancement
@@ -0,0 +1 @@
+Added a "load more" button on artist pages to load more tracks/albums (#719)
diff --git a/front/src/components/audio/track/Table.vue b/front/src/components/audio/track/Table.vue
index 702ffe687617df4cbc2b8ab34e1384c7e28f6584..31327ee3a5e3286cec4eded21ccc801d73e0b1eb 100644
--- a/front/src/components/audio/track/Table.vue
+++ b/front/src/components/audio/track/Table.vue
@@ -19,14 +19,18 @@
           :track="track"
           :artist="artist"
           :key="index + '-' + track.id"
-          v-for="(track, index) in tracks"></track-row>
+          v-for="(track, index) in allTracks"></track-row>
       </tbody>
     </table>
+    <button :class="['ui', {loading: isLoadingMore}, 'button']" v-if="loadMoreUrl" @click="loadMore(loadMoreUrl)">
+      <translate translate-context="Content/*/Button.Label">Load more…</translate>
+    </button>
   </div>
 </template>
 
 <script>
 import backend from '@/audio/backend'
+import axios from 'axios'
 
 import TrackRow from '@/components/audio/track/Row'
 import Modal from '@/components/semantic/Modal'
@@ -35,6 +39,7 @@ export default {
   props: {
     tracks: {type: Array, required: true},
     playable: {type: Boolean, required: false, default: false},
+    nextUrl: {type: String, required: false, default: null},
     artist: {type: Object, required: false},
     displayPosition: {type: Boolean, default: false}
   },
@@ -44,7 +49,29 @@ export default {
   },
   data () {
     return {
-      backend: backend
+      backend: backend,
+      loadMoreUrl: this.nextUrl,
+      isLoadingMore: false,
+      additionalTracks: []
+    }
+  },
+  computed: {
+    allTracks () {
+      return this.tracks.concat(this.additionalTracks)
+    }
+  },
+  methods: {
+    loadMore (url) {
+      let self = this
+      self.isLoadingMore = true
+      axios.get(url).then((response) => {
+        self.additionalTracks = self.additionalTracks.concat(response.data.results)
+        self.loadMoreUrl = response.data.next
+        self.isLoadingMore = false
+      }, (error) => {
+        self.isLoadingMore = false
+
+      })
     }
   }
 }
diff --git a/front/src/components/library/ArtistBase.vue b/front/src/components/library/ArtistBase.vue
index d4efcb82ec101214830506fbe1f23057dbd32dcd..71f3abd5ca2a31247d950786ce7bd41f400541d1 100644
--- a/front/src/components/library/ArtistBase.vue
+++ b/front/src/components/library/ArtistBase.vue
@@ -3,7 +3,7 @@
     <div v-if="isLoading" class="ui vertical segment">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
-    <template v-if="object">
+    <template v-if="object && !isLoading">
       <section :class="['ui', 'head', {'with-background': cover}, 'vertical', 'center', 'aligned', 'stripe', 'segment']" :style="headerStyle" v-title="object.name">
         <div class="segment-content">
           <h2 class="ui center aligned icon header">
@@ -98,7 +98,15 @@
           </div>
         </div>
       </section>
-      <router-view v-if="object" :tracks="tracks" :albums="albums" :is-loading-albums="isLoadingAlbums" @libraries-loaded="libraries = $event" :object="object" object-type="artist" :key="$route.fullPath"></router-view>
+      <router-view
+        :tracks="tracks"
+        :next-tracks-url="nextTracksUrl"
+        :next-albums-url="nextAlbumsUrl"
+        :albums="albums"
+        :is-loading-albums="isLoadingAlbums"
+        @libraries-loaded="libraries = $event"
+        :object="object" object-type="artist"
+        :key="$route.fullPath"></router-view>
     </template>
   </main>
 </template>
@@ -132,38 +140,45 @@ export default {
       libraries: [],
       showEmbedModal: false,
       tracks: [],
+      nextAlbumsUrl: null,
+      nextTracksUrl: null,
+      totalAlbums: null,
+      totalTracks: null,
     }
   },
-  created() {
-    this.fetchData()
+  async created() {
+    await this.fetchData()
   },
   methods: {
-    fetchData() {
+    async fetchData() {
       var self = this
       this.isLoading = true
       logger.default.debug('Fetching artist "' + this.id + '"')
-      axios.get("tracks/", { params: { artist: this.id, hidden: '' } }).then(response => {
+      let trackPromise = axios.get("tracks/", { params: { artist: this.id, hidden: '' } }).then(response => {
         self.tracks = response.data.results
+        self.nextTracksUrl = response.data.next
         self.totalTracks = response.data.count
       })
-      axios.get("artists/" + this.id + "/").then(response => {
-        self.object = response.data
-        self.isLoading = false
-        self.isLoadingAlbums = true
-        axios
-          .get("albums/", {
-            params: { artist: self.id, ordering: "-release_date", hidden: '' }
-          })
-          .then(response => {
-            self.totalAlbums = response.data.count
-            let parsed = JSON.parse(JSON.stringify(response.data.results))
-            self.albums = parsed.map(album => {
-              return backend.Album.clean(album)
-            })
+      let albumPromise = axios.get("albums/", {
+        params: { artist: self.id, ordering: "-release_date", hidden: '' }
+      }).then(response => {
+        self.nextAlbumsUrl = response.data.next
+        self.totalAlbums = response.data.count
+        let parsed = JSON.parse(JSON.stringify(response.data.results))
+        self.albums = parsed.map(album => {
+          return backend.Album.clean(album)
+        })
 
-            self.isLoadingAlbums = false
-          })
       })
+
+      let artistPromise = axios.get("artists/" + this.id + "/").then(response => {
+        self.object = response.data
+      })
+      await trackPromise
+      await albumPromise
+      await artistPromise
+      self.isLoadingAlbums = false
+      self.isLoading = false
     }
   },
   computed: {
diff --git a/front/src/components/library/ArtistDetail.vue b/front/src/components/library/ArtistDetail.vue
index 50e1856e91cab720459674997c1b33581a316949..1dfbdd0d244720ec21fe731ae01e9629c30f6ffb 100644
--- a/front/src/components/library/ArtistDetail.vue
+++ b/front/src/components/library/ArtistDetail.vue
@@ -21,15 +21,19 @@
       <h2>
         <translate translate-context="Content/Artist/Title">Albums by this artist</translate>
       </h2>
-      <div class="ui cards" >
-        <album-card :mode="'rich'" :album="album" :key="album.id" v-for="album in albums"></album-card>
+      <div class="ui cards">
+        <album-card :mode="'rich'" :album="album" :key="album.id" v-for="album in allAlbums"></album-card>
       </div>
+      <div class="ui hidden divider"></div>
+      <button :class="['ui', {loading: isLoadingMoreAlbums}, 'button']" v-if="nextAlbumsUrl && loadMoreAlbumsUrl" @click="loadMoreAlbums(loadMoreAlbumsUrl)">
+        <translate translate-context="Content/*/Button.Label">Load more…</translate>
+      </button>
     </section>
     <section v-if="tracks.length > 0" class="ui vertical stripe segment">
       <h2>
         <translate translate-context="Content/Artist/Title">Tracks by this artist</translate>
       </h2>
-      <track-table :display-position="true" :tracks="tracks"></track-table>
+      <track-table :display-position="true" :tracks="tracks" :next-url="nextTracksUrl"></track-table>
     </section>
     <section class="ui vertical stripe segment">
       <h2>
@@ -52,23 +56,42 @@ import TrackTable from "@/components/audio/track/Table"
 import LibraryWidget from "@/components/federation/LibraryWidget"
 
 export default {
-  props: ["object", "tracks", "albums", "isLoadingAlbums"],
+  props: ["object", "tracks", "albums", "isLoadingAlbums", "nextTracksUrl", "nextAlbumsUrl"],
   components: {
     AlbumCard,
     TrackTable,
     LibraryWidget,
   },
+  data () {
+    return {
+      loadMoreAlbumsUrl: this.nextAlbumsUrl,
+      additionalAlbums: [],
+      isLoadingMoreAlbums: false
+    }
+  },
   computed: {
     contentFilter () {
       let self = this
       return this.$store.getters['moderation/artistFilters']().filter((e) => {
         return e.target.id === this.object.id
       })[0]
+    },
+     allAlbums () {
+      return this.albums.concat(this.additionalAlbums)
     }
   },
-  watch: {
-    id() {
-      this.fetchData()
+  methods: {
+    loadMoreAlbums (url) {
+      let self = this
+      self.isLoadingMoreAlbums = true
+      axios.get(url).then((response) => {
+        self.additionalAlbums = self.additionalAlbums.concat(response.data.results)
+        self.loadMoreAlbumsUrl = response.data.next
+        self.isLoadingMoreAlbums = false
+      }, (error) => {
+        self.isLoadingMoreAlbums = false
+
+      })
     }
   }
 }