diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index 3f7cc7503385fae3cbf5b030386c510844ed74e0..6a2299bb4c619175082d1490ead7eeffd088fbb5 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -54,6 +54,7 @@ THIRD_PARTY_APPS = (
     'rest_auth.registration',
     'mptt',
     'dynamic_preferences',
+    'django_filters',
 )
 
 # Apps specific for this project go here.
@@ -298,7 +299,7 @@ REST_FRAMEWORK = {
     'DEFAULT_PERMISSION_CLASSES': (
         'rest_framework.permissions.IsAuthenticated',
     ),
-    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
+    'DEFAULT_PAGINATION_CLASS': 'funkwhale_api.common.pagination.FunkwhalePagination',
     'PAGE_SIZE': 25,
 
     'DEFAULT_AUTHENTICATION_CLASSES': (
@@ -309,6 +310,7 @@ REST_FRAMEWORK = {
     ),
     'DEFAULT_FILTER_BACKENDS': (
         'rest_framework.filters.OrderingFilter',
+        'django_filters.rest_framework.DjangoFilterBackend',
     )
 }
 
diff --git a/api/funkwhale_api/__init__.py b/api/funkwhale_api/__init__.py
index 0168bc25af3889c3fd06ef2ae9ba55e813da09a9..d961973d34db055563899a8dc956514abec7f967 100644
--- a/api/funkwhale_api/__init__.py
+++ b/api/funkwhale_api/__init__.py
@@ -1,3 +1,3 @@
 # -*- coding: utf-8 -*-
-__version__ = '0.2.2'
+__version__ = '0.2.3'
 __version_info__ = tuple([int(num) if num.isdigit() else num for num in __version__.replace('-', '.', 1).split('.')])
diff --git a/api/funkwhale_api/common/pagination.py b/api/funkwhale_api/common/pagination.py
new file mode 100644
index 0000000000000000000000000000000000000000..224c470dc1e8abb6de2d279eb85b174e1a2c76ef
--- /dev/null
+++ b/api/funkwhale_api/common/pagination.py
@@ -0,0 +1,6 @@
+from rest_framework.pagination import PageNumberPagination
+
+
+class FunkwhalePagination(PageNumberPagination):
+    page_size_query_param = 'page_size'
+    max_page_size = 25
diff --git a/api/funkwhale_api/music/fake_data.py b/api/funkwhale_api/music/fake_data.py
new file mode 100644
index 0000000000000000000000000000000000000000..ef5eaf4be811f580d82378c17ab838694df52ea7
--- /dev/null
+++ b/api/funkwhale_api/music/fake_data.py
@@ -0,0 +1,22 @@
+"""
+Populates the database with fake data
+"""
+import random
+
+from funkwhale_api.music import models
+from funkwhale_api.music.tests import factories
+
+
+def create_data(count=25):
+    artists = factories.ArtistFactory.create_batch(size=count)
+    for artist in artists:
+        print('Creating data for', artist)
+        albums = factories.AlbumFactory.create_batch(
+            artist=artist, size=random.randint(1, 5))
+        for album in albums:
+            factories.TrackFileFactory.create_batch(
+                track__album=album, size=random.randint(3, 18))
+
+
+if __name__ == '__main__':
+    main()
diff --git a/api/funkwhale_api/music/filters.py b/api/funkwhale_api/music/filters.py
new file mode 100644
index 0000000000000000000000000000000000000000..ba3fa453d77443eb75247b3987849db972963e1c
--- /dev/null
+++ b/api/funkwhale_api/music/filters.py
@@ -0,0 +1,12 @@
+import django_filters
+
+from . import models
+
+
+class ArtistFilter(django_filters.FilterSet):
+
+    class Meta:
+        model = models.Artist
+        fields = {
+            'name': ['exact', 'iexact', 'startswith']
+        }
diff --git a/api/funkwhale_api/music/tests/factories.py b/api/funkwhale_api/music/tests/factories.py
index d4ec048101984419b5b1459943be5998f43f261a..ea680b3bd5d4da58e63f5ee4c861fec7972117f4 100644
--- a/api/funkwhale_api/music/tests/factories.py
+++ b/api/funkwhale_api/music/tests/factories.py
@@ -5,7 +5,7 @@ SAMPLES_PATH = os.path.dirname(os.path.abspath(__file__))
 
 
 class ArtistFactory(factory.django.DjangoModelFactory):
-    name = factory.Sequence(lambda n: 'artist-{0}'.format(n))
+    name = factory.Faker('name')
     mbid = factory.Faker('uuid4')
 
     class Meta:
@@ -13,7 +13,7 @@ class ArtistFactory(factory.django.DjangoModelFactory):
 
 
 class AlbumFactory(factory.django.DjangoModelFactory):
-    title = factory.Sequence(lambda n: 'album-{0}'.format(n))
+    title = factory.Faker('sentence', nb_words=3)
     mbid = factory.Faker('uuid4')
     release_date = factory.Faker('date')
     cover = factory.django.ImageField()
@@ -24,7 +24,7 @@ class AlbumFactory(factory.django.DjangoModelFactory):
 
 
 class TrackFactory(factory.django.DjangoModelFactory):
-    title = factory.Sequence(lambda n: 'track-{0}'.format(n))
+    title = factory.Faker('sentence', nb_words=3)
     mbid = factory.Faker('uuid4')
     album = factory.SubFactory(AlbumFactory)
     artist = factory.SelfAttribute('album.artist')
diff --git a/api/funkwhale_api/music/tests/test_api.py b/api/funkwhale_api/music/tests/test_api.py
index 21a567084e25a4d3ade7a27fe878d227839f4bbf..b7c25424f322605bc47662743d921e0419ed3d3e 100644
--- a/api/funkwhale_api/music/tests/test_api.py
+++ b/api/funkwhale_api/music/tests/test_api.py
@@ -182,6 +182,21 @@ class TestAPI(TMPDirTestCaseMixin, TestCase):
 
         self.assertJSONEqual(expected, json.loads(response.content.decode('utf-8')))
 
+    def test_can_search_artist_by_name_start(self):
+        artist1 = factories.ArtistFactory(name='alpha')
+        artist2 = factories.ArtistFactory(name='beta')
+        results = {
+            'next': None,
+            'previous': None,
+            'count': 1,
+            'results': [serializers.ArtistSerializerNested(artist1).data]
+        }
+        expected = json.dumps(results)
+        url = self.reverse('api:v1:artists-list')
+        response = self.client.get(url, {'name__startswith': 'a'})
+
+        self.assertJSONEqual(expected, json.loads(response.content.decode('utf-8')))
+
     def test_can_search_tracks(self):
         artist1 = models.Artist.objects.create(name='Test1')
         artist2 = models.Artist.objects.create(name='Test2')
diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py
index 98319255270bfdae84f70e3335fc4917f650885b..d149b5d1b9b937955d89debc33ecf2689d64c9e5 100644
--- a/api/funkwhale_api/music/views.py
+++ b/api/funkwhale_api/music/views.py
@@ -21,8 +21,10 @@ from taggit.models import Tag
 from . import models
 from . import serializers
 from . import importers
+from . import filters
 from . import utils
 
+
 class SearchMixin(object):
     search_fields = []
 
@@ -52,7 +54,8 @@ class ArtistViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
     serializer_class = serializers.ArtistSerializerNested
     permission_classes = [ConditionalAuthentication]
     search_fields = ['name']
-    ordering_fields = ('creation_date',)
+    ordering_fields = ('creation_date', 'name')
+    filter_class = filters.ArtistFilter
 
 
 class AlbumViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
diff --git a/api/requirements/base.txt b/api/requirements/base.txt
index e7bc870cfd85efeff22033e71f583536592b7ab9..3a11afadf509cfa04aae62a9ca566d23f28f2646 100644
--- a/api/requirements/base.txt
+++ b/api/requirements/base.txt
@@ -45,6 +45,7 @@ django-taggit==0.22.1
 persisting-theory==0.2.1
 django-versatileimagefield==1.7.1
 django-cachalot==1.5.0
+django-filter==1.1
 django-rest-auth==0.9.1
 beautifulsoup4==4.6.0
 Markdown==2.6.8
diff --git a/front/package.json b/front/package.json
index 55e773fba7b52c6d61655d4e1e80c71349203457..bad90430f4144be99d4b17ac11e82073a6705ff0 100644
--- a/front/package.json
+++ b/front/package.json
@@ -16,9 +16,10 @@
   "dependencies": {
     "dateformat": "^2.0.0",
     "js-logger": "^1.3.0",
+    "lodash": "^4.17.4",
     "semantic-ui-css": "^2.2.10",
     "vue": "^2.3.3",
-    "vue-global-events": "^1.0.2",
+    "vue-lazyload": "^1.1.4",
     "vue-resource": "^1.3.4",
     "vue-router": "^2.3.1",
     "vuedraggable": "^2.14.1"
diff --git a/front/src/components/Pagination.vue b/front/src/components/Pagination.vue
new file mode 100644
index 0000000000000000000000000000000000000000..3ac7c59af5817413c13a97e569065ca48cb95b91
--- /dev/null
+++ b/front/src/components/Pagination.vue
@@ -0,0 +1,52 @@
+<template>
+  <div class="ui pagination borderless menu">
+    <a
+      @click="selectPage(1)"
+      :class="[{'disabled': current === 1}, 'item']"><i class="angle double left icon"></i></a>
+    <a
+      @click="selectPage(current - 1)"
+      :class="[{'disabled': current - 1 < 1}, 'item']"><i class="angle left icon"></i></a>
+    <a
+      v-for="page in pages"
+      @click="selectPage(page)"
+      :class="[{'active': page === current}, 'item']">
+      {{ page }}
+    </a>
+    <a
+      @click="selectPage(current + 1)"
+      :class="[{'disabled': current + 1 > maxPage}, 'item']"><i class="angle right icon"></i></a>
+      <a
+        @click="selectPage(maxPage)"
+        :class="[{'disabled': current === maxPage}, 'item']"><i class="angle double right icon"></i></a>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash'
+
+export default {
+  props: {
+    current: {type: Number, default: 1},
+    paginateBy: {type: Number, default: 25},
+    total: {type: Number}
+  },
+  computed: {
+    pages: function () {
+      return _.range(1, this.maxPage + 1)
+    },
+    maxPage: function () {
+      return Math.ceil(this.total / this.paginateBy)
+    }
+  },
+  methods: {
+    selectPage: function (page) {
+      if (this.current !== page) {
+        this.$emit('page-changed', page)
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+</style>
diff --git a/front/src/components/Sidebar.vue b/front/src/components/Sidebar.vue
index 0ce16a75f94a6b597a2ea0e3d120c336533232c7..68927a37b09a5e302a9dfb7ce9be44b9e7750b96 100644
--- a/front/src/components/Sidebar.vue
+++ b/front/src/components/Sidebar.vue
@@ -94,7 +94,7 @@
 </template>
 
 <script>
-import GlobalEvents from 'vue-global-events'
+import GlobalEvents from '@/components/utils/global-events'
 
 import Player from '@/components/audio/Player'
 import favoriteTracks from '@/favorites/tracks'
diff --git a/front/src/components/audio/Player.vue b/front/src/components/audio/Player.vue
index 92ea9d0a48297ac1be4f65a894c584fd801e6282..423c9d12f2d5cf89abf5b1f876a595925ee21370 100644
--- a/front/src/components/audio/Player.vue
+++ b/front/src/components/audio/Player.vue
@@ -7,14 +7,14 @@
           <img v-else src="../../assets/audio/default-cover.png">
         </div>
         <div class="middle aligned content">
-          <router-link class="small header discrete link track" :to="{name: 'library.track', params: {id: queue.currentTrack.id }}">
+          <router-link class="small header discrete link track" :to="{name: 'library.tracks.detail', params: {id: queue.currentTrack.id }}">
             {{ queue.currentTrack.title }}
           </router-link>
           <div class="meta">
-            <router-link class="artist" :to="{name: 'library.artist', params: {id: queue.currentTrack.artist.id }}">
+            <router-link class="artist" :to="{name: 'library.artists.detail', params: {id: queue.currentTrack.artist.id }}">
               {{ queue.currentTrack.artist.name }}
             </router-link> /
-            <router-link class="album" :to="{name: 'library.album', params: {id: queue.currentTrack.album.id }}">
+            <router-link class="album" :to="{name: 'library.albums.detail', params: {id: queue.currentTrack.album.id }}">
               {{ queue.currentTrack.album.title }}
             </router-link>
           </div>
@@ -68,7 +68,7 @@
 </template>
 
 <script>
-import GlobalEvents from 'vue-global-events'
+import GlobalEvents from '@/components/utils/global-events'
 
 import queue from '@/audio/queue'
 import Track from '@/audio/track'
diff --git a/front/src/components/audio/SearchBar.vue b/front/src/components/audio/SearchBar.vue
index 6f4629296d4eecfc16b1ba802669bafd01135378..2324c88392f258f95b7915cce04ea1b9166d5e80 100644
--- a/front/src/components/audio/SearchBar.vue
+++ b/front/src/components/audio/SearchBar.vue
@@ -35,7 +35,7 @@ export default {
           let categories = [
             {
               code: 'artists',
-              route: 'library.artist',
+              route: 'library.artists.detail',
               name: 'Artist',
               getTitle (r) {
                 return r.name
@@ -46,7 +46,7 @@ export default {
             },
             {
               code: 'albums',
-              route: 'library.album',
+              route: 'library.albums.detail',
               name: 'Album',
               getTitle (r) {
                 return r.title
@@ -57,7 +57,7 @@ export default {
             },
             {
               code: 'tracks',
-              route: 'library.track',
+              route: 'library.tracks.detail',
               name: 'Track',
               getTitle (r) {
                 return r.title
diff --git a/front/src/components/audio/album/Card.vue b/front/src/components/audio/album/Card.vue
index 7fd60d963b512ae637faa7c7dc8acbb2f9021d80..ce5e832e2794a4288a673460244434f30945615f 100644
--- a/front/src/components/audio/album/Card.vue
+++ b/front/src/components/audio/album/Card.vue
@@ -2,14 +2,14 @@
     <div class="ui card">
       <div class="content">
         <div class="right floated tiny ui image">
-          <img v-if="album.cover" :src="backend.absoluteUrl(album.cover)">
+          <img v-if="album.cover" v-lazy="backend.absoluteUrl(album.cover)">
           <img v-else src="../../../assets/audio/default-cover.png">
         </div>
         <div class="header">
-          <router-link class="discrete link" :to="{name: 'library.album', params: {id: album.id }}">{{ album.title }}</router-link>
+          <router-link class="discrete link" :to="{name: 'library.albums.detail', params: {id: album.id }}">{{ album.title }}</router-link>
         </div>
         <div class="meta">
-          By <router-link :to="{name: 'library.artist', params: {id: album.artist.id }}">
+          By <router-link :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
             {{ album.artist.name }}
           </router-link>
         </div>
@@ -21,7 +21,7 @@
                   <play-button class="basic icon" :track="track" :discrete="true"></play-button>
                 </td>
                 <td colspan="6">
-                  <router-link class="track discrete link" :to="{name: 'library.track', params: {id: track.id }}">
+                  <router-link class="track discrete link" :to="{name: 'library.tracks.detail', params: {id: track.id }}">
                     <template v-if="track.position">
                       {{ track.position }}.
                     </template>
diff --git a/front/src/components/audio/artist/Card.vue b/front/src/components/audio/artist/Card.vue
index a9701c07ebadda7e7003e01c05fd09a9e19c9743..8a02163fbb17258badb78fe6f4d133e5096f6749 100644
--- a/front/src/components/audio/artist/Card.vue
+++ b/front/src/components/audio/artist/Card.vue
@@ -2,7 +2,7 @@
   <div class="ui card">
     <div class="content">
         <div class="header">
-          <router-link class="discrete link" :to="{name: 'library.artist', params: {id: artist.id }}">
+          <router-link class="discrete link" :to="{name: 'library.artists.detail', params: {id: artist.id }}">
             {{ artist.name }}
           </router-link>
         </div>
@@ -15,7 +15,7 @@
                   <img class="ui mini image" v-else src="../../../assets/audio/default-cover.png">
                 </td>
                 <td colspan="4">
-                  <router-link class="discrete link":to="{name: 'library.album', params: {id: album.id }}">
+                  <router-link class="discrete link":to="{name: 'library.albums.detail', params: {id: album.id }}">
                     <strong>{{ album.title }}</strong>
                   </router-link><br />
                   {{ album.tracks.length }} tracks
diff --git a/front/src/components/audio/track/Table.vue b/front/src/components/audio/track/Table.vue
index e9beaa05a5ac8f0bb6285597a9316eb8afab4e4f..055c986d2fdf416d01215fa328ba59bea3a34c07 100644
--- a/front/src/components/audio/track/Table.vue
+++ b/front/src/components/audio/track/Table.vue
@@ -16,11 +16,11 @@
           <play-button class="basic icon" :discrete="true" :track="track"></play-button>
         </td>
         <td>
-          <img class="ui mini image" v-if="track.album.cover" :src="backend.absoluteUrl(track.album.cover)">
+          <img class="ui mini image" v-if="track.album.cover" v-lazy="backend.absoluteUrl(track.album.cover)">
           <img class="ui mini image" v-else src="../../..//assets/audio/default-cover.png">
         </td>
         <td colspan="6">
-          <router-link class="track" :to="{name: 'library.track', params: {id: track.id }}">
+          <router-link class="track" :to="{name: 'library.tracks.detail', params: {id: track.id }}">
             <template v-if="displayPosition && track.position">
               {{ track.position }}.
             </template>
@@ -28,12 +28,12 @@
           </router-link>
         </td>
         <td colspan="6">
-          <router-link class="artist discrete link" :to="{name: 'library.artist', params: {id: track.artist.id }}">
+          <router-link class="artist discrete link" :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">
             {{ track.artist.name }}
           </router-link>
         </td>
         <td colspan="6">
-          <router-link class="album discrete link" :to="{name: 'library.album', params: {id: track.album.id }}">
+          <router-link class="album discrete link" :to="{name: 'library.albums.detail', params: {id: track.album.id }}">
             {{ track.album.title }}
           </router-link>
         </td>
diff --git a/front/src/components/favorites/List.vue b/front/src/components/favorites/List.vue
index f7972a07c9243863e025af3f2103e0af6e47c891..63c3ba79d18c7360cdef97f8739614cfb1f1f163 100644
--- a/front/src/components/favorites/List.vue
+++ b/front/src/components/favorites/List.vue
@@ -12,11 +12,16 @@
 
     </div>
     <div class="ui vertical stripe segment">
-      <button class="ui left floated labeled icon button" @click="fetchFavorites(previousLink)" :disabled="!previousLink"><i class="left arrow icon"></i> Previous</button>
-      <button class="ui right floated right labeled icon button" @click="fetchFavorites(nextLink)" :disabled="!nextLink">Next <i class="right arrow icon"></i></button>
-      <div class="ui hidden clearing divider"></div>
-      <div class="ui hidden clearing divider"></div>
       <track-table v-if="results" :tracks="results.results"></track-table>
+      <div class="ui center aligned basic segment">
+        <pagination
+          v-if="results && results.count > 0"
+          @page-changed="selectPage"
+          :current="page"
+          :paginate-by="paginateBy"
+          :total="results.count"
+          ></pagination>
+      </div>
     </div>
   </div>
 </template>
@@ -28,13 +33,15 @@ import config from '@/config'
 import favoriteTracks from '@/favorites/tracks'
 import TrackTable from '@/components/audio/track/Table'
 import RadioButton from '@/components/radios/Button'
+import Pagination from '@/components/Pagination'
 
 const FAVORITES_URL = config.API_URL + 'tracks/'
 
 export default {
   components: {
     TrackTable,
-    RadioButton
+    RadioButton,
+    Pagination
   },
   data () {
     return {
@@ -42,6 +49,8 @@ export default {
       isLoading: false,
       nextLink: null,
       previousLink: null,
+      page: 1,
+      paginateBy: 25,
       favoriteTracks
     }
   },
@@ -53,7 +62,9 @@ export default {
       var self = this
       this.isLoading = true
       let params = {
-        favorites: 'true'
+        favorites: 'true',
+        page: this.page,
+        page_size: this.paginateBy
       }
       logger.default.time('Loading user favorites')
       this.$http.get(url, {params: params}).then((response) => {
@@ -68,6 +79,14 @@ export default {
         logger.default.timeEnd('Loading user favorites')
         self.isLoading = false
       })
+    },
+    selectPage: function (page) {
+      this.page = page
+    }
+  },
+  watch: {
+    page: function () {
+      this.fetchFavorites(FAVORITES_URL)
     }
   }
 }
diff --git a/front/src/components/library/Album.vue b/front/src/components/library/Album.vue
index cf3403400b8a07afc56cd0ca0edd7874ca8bacf6..dcfea560095064e20384dd1e77dc37707c7f42e3 100644
--- a/front/src/components/library/Album.vue
+++ b/front/src/components/library/Album.vue
@@ -12,7 +12,7 @@
               {{ album.title }}
               <div class="sub header">
                 Album containing {{ album.tracks.length }} tracks,
-                by <router-link :to="{name: 'library.artist', params: {id: album.artist.id }}">
+                by <router-link :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
                   {{ album.artist.name }}
                 </router-link>
               </div>
diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue
new file mode 100644
index 0000000000000000000000000000000000000000..07727a0595efcc550f61ec0738b9d7070d0b146c
--- /dev/null
+++ b/front/src/components/library/Artists.vue
@@ -0,0 +1,86 @@
+<template>
+  <div>
+    <div v-if="isLoading" class="ui vertical segment">
+      <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
+    </div>
+    <div v-if="result" class="ui vertical stripe segment">
+      <h2 class="ui header">Browsing artists</h2>
+      <div class="ui stackable three column grid">
+        <div
+          v-if="result.results.length > 0"
+          v-for="artist in result.results"
+          :key="artist.id"
+          class="column">
+          <artist-card class="fluid" :artist="artist"></artist-card>
+        </div>
+      </div>
+      <div class="ui center aligned basic segment">
+        <pagination
+          v-if="result && result.results.length > 0"
+          @page-changed="selectPage"
+          :current="page"
+          :paginate-by="paginateBy"
+          :total="result.count"
+          ></pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+
+import config from '@/config'
+import logger from '@/logging'
+import ArtistCard from '@/components/audio/artist/Card'
+import Pagination from '@/components/Pagination'
+
+const FETCH_URL = config.API_URL + 'artists/'
+
+export default {
+  components: {
+    ArtistCard,
+    Pagination
+  },
+  data () {
+    return {
+      isLoading: true,
+      result: null,
+      page: 1,
+      orderBy: 'name',
+      paginateBy: 12
+    }
+  },
+  created () {
+    this.fetchData()
+  },
+  methods: {
+    fetchData () {
+      var self = this
+      this.isLoading = true
+      let url = FETCH_URL
+      let params = {
+        page: this.page,
+        page_size: this.paginateBy,
+        order_by: 'name'
+      }
+      logger.default.debug('Fetching artists')
+      this.$http.get(url, {params: params}).then((response) => {
+        self.result = response.data
+        self.isLoading = false
+      })
+    },
+    selectPage: function (page) {
+      this.page = page
+    }
+  },
+  watch: {
+    page () {
+      this.fetchData()
+    }
+  }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+</style>
diff --git a/front/src/components/library/Home.vue b/front/src/components/library/Home.vue
index 651f7cb63bb90a0c66b1b9b97bf983d469c40f58..624da62c567e75fee65577726b5aaa0d5b2f32bd 100644
--- a/front/src/components/library/Home.vue
+++ b/front/src/components/library/Home.vue
@@ -8,7 +8,7 @@
         <div class="column">
           <h2 class="ui header">Latest artists</h2>
           <div :class="['ui', {'active': isLoadingArtists}, 'inline', 'loader']"></div>
-          <div v-if="artists.length > 0" v-for="artist in artists.slice(0, 3)" :key="artist" class="ui cards">
+          <div v-if="artists.length > 0" v-for="artist in artists.slice(0, 3)" :key="artist.id" class="ui cards">
             <artist-card :artist="artist"></artist-card>
           </div>
         </div>
diff --git a/front/src/components/library/Library.vue b/front/src/components/library/Library.vue
index 56b750a4afcfe2870a5d7a7961c259f9da62105a..da9ac19b3ad7cef127384b446296d76e9a39a638 100644
--- a/front/src/components/library/Library.vue
+++ b/front/src/components/library/Library.vue
@@ -2,8 +2,11 @@
   <div class="main library pusher">
     <div class="ui secondary pointing menu">
       <router-link class="ui item" to="/library" exact>Browse</router-link>
-      <router-link v-if="auth.user.availablePermissions['import.launch']" class="ui item" to="/library/import/launch" exact>Import</router-link>
-      <router-link v-if="auth.user.availablePermissions['import.launch']" class="ui item" to="/library/import/batches">Import batches</router-link>
+      <router-link class="ui item" to="/library/artists" exact>Artists</router-link>
+      <div class="ui secondary right menu">
+        <router-link v-if="auth.user.availablePermissions['import.launch']" class="ui item" to="/library/import/launch" exact>Import</router-link>
+        <router-link v-if="auth.user.availablePermissions['import.launch']" class="ui item" to="/library/import/batches">Import batches</router-link>
+      </div>
     </div>
     <router-view></router-view>
   </div>
diff --git a/front/src/components/library/Track.vue b/front/src/components/library/Track.vue
index 3c627c13c257d6c71d2ac03f80ff9f573c5bff58..36a76e822c2ac0218d8c23a1fb2cca8f21db4f29 100644
--- a/front/src/components/library/Track.vue
+++ b/front/src/components/library/Track.vue
@@ -12,10 +12,10 @@
               {{ track.title }}
               <div class="sub header">
                 From album
-                <router-link :to="{name: 'library.album', params: {id: track.album.id }}">
+                <router-link :to="{name: 'library.albums.detail', params: {id: track.album.id }}">
                   {{ track.album.title }}
                 </router-link>
-                by <router-link :to="{name: 'library.artist', params: {id: track.artist.id }}">
+                by <router-link :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">
                   {{ track.artist.name }}
                 </router-link>
               </div>
diff --git a/front/src/components/utils/global-events.vue b/front/src/components/utils/global-events.vue
new file mode 100644
index 0000000000000000000000000000000000000000..2905e3a7d337dddc3b66ecc528e0d4f9c607dd9f
--- /dev/null
+++ b/front/src/components/utils/global-events.vue
@@ -0,0 +1,52 @@
+<script>
+import $ from 'jquery'
+
+const modifiersRE = /^[~!&]*/
+const nonEventNameCharsRE = /\W+/
+const names = {
+  '!': 'capture',
+  '~': 'once',
+  '&': 'passive'
+}
+
+function extractEventOptions (eventDescriptor) {
+  const [modifiers] = eventDescriptor.match(modifiersRE)
+  return modifiers.split('').reduce((options, modifier) => {
+    options[names[modifier]] = true
+    return options
+  }, {})
+}
+
+export default {
+  render: h => h(),
+
+  mounted () {
+    this._listeners = Object.create(null)
+    Object.keys(this.$listeners).forEach(event => {
+      const handler = this.$listeners[event]
+      let wrapper = function (event) {
+        // we check here the event is not triggered from an input
+        // to avoid collisions
+        if (!$(event.target).is(':input, [contenteditable]')) {
+          handler(event)
+        }
+      }
+      document.addEventListener(
+        event.replace(nonEventNameCharsRE, ''),
+        wrapper,
+        extractEventOptions(event)
+      )
+      this._listeners[event] = handler
+    })
+  },
+
+  beforeDestroy () {
+    for (const event in this._listeners) {
+      document.removeEventListener(
+        event.replace(nonEventNameCharsRE, ''),
+        this._listeners[event]
+      )
+    }
+  }
+}
+</script>
diff --git a/front/src/main.js b/front/src/main.js
index 4ffbdb2f7b787a0dacb6c53eba914f190a0640fa..a214c3881e1535178a46a6b1101e2fac17685706 100644
--- a/front/src/main.js
+++ b/front/src/main.js
@@ -10,6 +10,7 @@ import App from './App'
 import router from './router'
 import VueResource from 'vue-resource'
 import auth from './auth'
+import VueLazyload from 'vue-lazyload'
 
 window.$ = window.jQuery = require('jquery')
 
@@ -19,6 +20,7 @@ window.$ = window.jQuery = require('jquery')
 require('semantic-ui-css/semantic.js')
 
 Vue.use(VueResource)
+Vue.use(VueLazyload)
 Vue.config.productionTip = false
 
 Vue.http.interceptors.push(function (request, next) {
diff --git a/front/src/router/index.js b/front/src/router/index.js
index b3d90731f41cf11f614612b1a3cc954625fd975a..e546172b5f058e0c2ae880be68c82296fc0f41a3 100644
--- a/front/src/router/index.js
+++ b/front/src/router/index.js
@@ -7,6 +7,7 @@ import Logout from '@/components/auth/Logout'
 import Library from '@/components/library/Library'
 import LibraryHome from '@/components/library/Home'
 import LibraryArtist from '@/components/library/Artist'
+import LibraryArtists from '@/components/library/Artists'
 import LibraryAlbum from '@/components/library/Album'
 import LibraryTrack from '@/components/library/Track'
 import LibraryImport from '@/components/library/import/Main'
@@ -51,9 +52,10 @@ export default new Router({
       component: Library,
       children: [
         { path: '', component: LibraryHome },
-        { path: 'artist/:id', name: 'library.artist', component: LibraryArtist, props: true },
-        { path: 'album/:id', name: 'library.album', component: LibraryAlbum, props: true },
-        { path: 'track/:id', name: 'library.track', component: LibraryTrack, props: true },
+        { path: 'artists/', name: 'library.artists.browse', component: LibraryArtists },
+        { path: 'artists/:id', name: 'library.artists.detail', component: LibraryArtist, props: true },
+        { path: 'albums/:id', name: 'library.albums.detail', component: LibraryAlbum, props: true },
+        { path: 'tracks/:id', name: 'library.tracks.detail', component: LibraryTrack, props: true },
         {
           path: 'import/launch',
           name: 'library.import.launch',