diff --git a/api/funkwhale_api/favorites/activities.py b/api/funkwhale_api/favorites/activities.py
index 294194e061614a338137a3519aa547823a2d1cf0..74d02eafed516bc1c08f30034c01cbf3be05ee4a 100644
--- a/api/funkwhale_api/favorites/activities.py
+++ b/api/funkwhale_api/favorites/activities.py
@@ -4,6 +4,7 @@ from funkwhale_api.common import channels
 from . import serializers
 
 record.registry.register_serializer(serializers.TrackFavoriteActivitySerializer)
+record.registry.register_serializer(serializers.AlbumFavoriteActivitySerializer)
 
 
 @record.registry.register_consumer("favorites.TrackFavorite")
@@ -14,3 +15,13 @@ def broadcast_track_favorite_to_instance_activity(data, obj):
     channels.group_send(
         "instance_activity", {"type": "event.send", "text": "", "data": data}
     )
+
+
+@record.registry.register_consumer("favorites.AlbumFavorite")
+def broadcast_album_favorite_to_instance_activity(data, obj):
+    if obj.user.privacy_level not in ["instance", "everyone"]:
+        return
+
+    channels.group_send(
+        "instance_activity", {"type": "event.send", "text": "", "data": data}
+    )
diff --git a/api/funkwhale_api/favorites/admin.py b/api/funkwhale_api/favorites/admin.py
index 05530b0c67dd0a9c42808ff2e24d721b32f911cf..bfbf96e4b3cc658cadae02c83ee5fded07031587 100644
--- a/api/funkwhale_api/favorites/admin.py
+++ b/api/funkwhale_api/favorites/admin.py
@@ -7,3 +7,9 @@ from . import models
 class TrackFavoriteAdmin(admin.ModelAdmin):
     list_display = ["user", "track", "creation_date"]
     list_select_related = ["user", "track"]
+
+
+@admin.register(models.AlbumFavorite)
+class AlbumFavoriteAdmin(admin.ModelAdmin):
+    list_display = ["user", "album", "creation_date"]
+    list_select_related = ["user", "album"]
diff --git a/api/funkwhale_api/favorites/factories.py b/api/funkwhale_api/favorites/factories.py
index fcc2f820425c80130a93b7c45c1dca7650da5c88..b663f140f8a1c155f553f85d7c46cfd30ae5c1c9 100644
--- a/api/funkwhale_api/favorites/factories.py
+++ b/api/funkwhale_api/favorites/factories.py
@@ -1,6 +1,7 @@
 import factory
 
 from funkwhale_api.factories import registry, NoUpdateOnCreate
+from funkwhale_api.music.factories import AlbumFactory
 from funkwhale_api.music.factories import TrackFactory
 from funkwhale_api.users.factories import UserFactory
 
@@ -12,3 +13,12 @@ class TrackFavorite(NoUpdateOnCreate, factory.django.DjangoModelFactory):
 
     class Meta:
         model = "favorites.TrackFavorite"
+
+
+@registry.register
+class AlbumFavorite(NoUpdateOnCreate, factory.django.DjangoModelFactory):
+    album = factory.SubFactory(AlbumFactory)
+    user = factory.SubFactory(UserFactory)
+
+    class Meta:
+        model = "favorites.AlbumFavorite"
diff --git a/api/funkwhale_api/favorites/filters.py b/api/funkwhale_api/favorites/filters.py
index 32c07a646bd80ca5675fc61fc2a45e25c31e8308..8e8193ce4cb6e41fce265b1aaecbb9d473fa68d0 100644
--- a/api/funkwhale_api/favorites/filters.py
+++ b/api/funkwhale_api/favorites/filters.py
@@ -17,3 +17,18 @@ class TrackFavoriteFilter(moderation_filters.HiddenContentFilterSet):
         hidden_content_fields_mapping = moderation_filters.USER_FILTER_CONFIG[
             "TRACK_FAVORITE"
         ]
+
+
+class AlbumFavoriteFilter(moderation_filters.HiddenContentFilterSet):
+    q = fields.SearchFilter(
+        # TODO: not sure which values I should put there
+        search_fields=["track__title", "track__artist__name", "track__album__title"]
+    )
+    scope = common_filters.ActorScopeFilter(actor_field="user__actor", distinct=True)
+
+    class Meta:
+        model = models.AlbumFavorite
+        fields = []
+        hidden_content_fields_mapping = moderation_filters.USER_FILTER_CONFIG[
+            "ALBUM_FAVORITE"
+        ]
diff --git a/api/funkwhale_api/favorites/migrations/0002_add_album_favorites.py b/api/funkwhale_api/favorites/migrations/0002_add_album_favorites.py
new file mode 100644
index 0000000000000000000000000000000000000000..4cda9313b617a58184e8a4dd89354669c7f1d96c
--- /dev/null
+++ b/api/funkwhale_api/favorites/migrations/0002_add_album_favorites.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.utils.timezone
+from django.conf import settings
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("favorites", "0001_initial"),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="AlbumFavorite",
+            fields=[
+                (
+                    "id",
+                    models.AutoField(
+                        serialize=False,
+                        auto_created=True,
+                        verbose_name="ID",
+                        primary_key=True,
+                    ),
+                ),
+                (
+                    "creation_date",
+                    models.DateTimeField(default=django.utils.timezone.now),
+                ),
+                (
+                    "album",
+                    models.ForeignKey(
+                        related_name="album_favorites",
+                        to="music.Album",
+                        on_delete=models.CASCADE,
+                    ),
+                ),
+                (
+                    "user",
+                    models.ForeignKey(
+                        related_name="album_favorites",
+                        to=settings.AUTH_USER_MODEL,
+                        on_delete=models.CASCADE,
+                    ),
+                ),
+            ],
+            options={"ordering": ("-creation_date",)},
+        ),
+        migrations.AlterUniqueTogether(
+            name="albumfavorite", unique_together=set([("album", "user")])
+        ),
+    ]
diff --git a/api/funkwhale_api/favorites/models.py b/api/funkwhale_api/favorites/models.py
index a6a80cebdd0e70d8d5a67293ef4fcbcad3d0e463..6f0e80478e3e48a3858fb0cc3aede8cb05e869b4 100644
--- a/api/funkwhale_api/favorites/models.py
+++ b/api/funkwhale_api/favorites/models.py
@@ -1,7 +1,7 @@
 from django.db import models
 from django.utils import timezone
 
-from funkwhale_api.music.models import Track
+from funkwhale_api.music.models import Track, Album
 
 
 class TrackFavorite(models.Model):
@@ -24,3 +24,25 @@ class TrackFavorite(models.Model):
 
     def get_activity_url(self):
         return "{}/favorites/tracks/{}".format(self.user.get_activity_url(), self.pk)
+
+
+class AlbumFavorite(models.Model):
+    creation_date = models.DateTimeField(default=timezone.now)
+    user = models.ForeignKey(
+        "users.User", related_name="album_favorites", on_delete=models.CASCADE
+    )
+    album = models.ForeignKey(
+        Album, related_name="album_favorites", on_delete=models.CASCADE
+    )
+
+    class Meta:
+        unique_together = ("album", "user")
+        ordering = ("-creation_date",)
+
+    @classmethod
+    def add(cls, album, user):
+        favorite, created = cls.objects.get_or_create(user=user, album=album)
+        return favorite
+
+    def get_activity_url(self):
+        return "{}/favorites/albums/{}".format(self.user.get_activity_url(), self.pk)
diff --git a/api/funkwhale_api/favorites/serializers.py b/api/funkwhale_api/favorites/serializers.py
index dd28dcd07cbaff216d9988f9641d890f069b0d50..e58f947d72428083514331d9f860c097d3da2f03 100644
--- a/api/funkwhale_api/favorites/serializers.py
+++ b/api/funkwhale_api/favorites/serializers.py
@@ -2,6 +2,7 @@ from rest_framework import serializers
 
 from funkwhale_api.activity import serializers as activity_serializers
 from funkwhale_api.federation import serializers as federation_serializers
+from funkwhale_api.music.serializers import AlbumActivitySerializer, AlbumSerializer
 from funkwhale_api.music.serializers import TrackActivitySerializer, TrackSerializer
 from funkwhale_api.users.serializers import UserActivitySerializer, UserBasicSerializer
 
@@ -45,3 +46,42 @@ class UserTrackFavoriteWriteSerializer(serializers.ModelSerializer):
     class Meta:
         model = models.TrackFavorite
         fields = ("id", "track", "creation_date")
+
+
+class AlbumFavoriteActivitySerializer(activity_serializers.ModelSerializer):
+    type = serializers.SerializerMethodField()
+    object = AlbumActivitySerializer(source="album")
+    actor = UserActivitySerializer(source="user")
+    published = serializers.DateTimeField(source="creation_date")
+
+    class Meta:
+        model = models.AlbumFavorite
+        fields = ["id", "local_id", "object", "type", "actor", "published"]
+
+    def get_actor(self, obj):
+        return UserActivitySerializer(obj.user).data
+
+    def get_type(self, obj):
+        return "Like"
+
+
+class UserAlbumFavoriteSerializer(serializers.ModelSerializer):
+    album = AlbumSerializer(read_only=True)
+    user = UserBasicSerializer(read_only=True)
+    actor = serializers.SerializerMethodField()
+
+    class Meta:
+        model = models.AlbumFavorite
+        fields = ("id", "user", "album", "creation_date", "actor")
+        actor = serializers.SerializerMethodField()
+
+    def get_actor(self, obj):
+        actor = obj.user.actor
+        if actor:
+            return federation_serializers.APIActorSerializer(actor).data
+
+
+class UserAlbumFavoriteWriteSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = models.AlbumFavorite
+        fields = ("id", "album", "creation_date")
diff --git a/api/funkwhale_api/favorites/urls.py b/api/funkwhale_api/favorites/urls.py
index 51f3078038e882aeb90a41bf1f4a1c870e7d9d56..30cc0d347ed1d9fe0cc7382ee2a0ebba319a3aba 100644
--- a/api/funkwhale_api/favorites/urls.py
+++ b/api/funkwhale_api/favorites/urls.py
@@ -4,5 +4,6 @@ from . import views
 
 router = routers.OptionalSlashRouter()
 router.register(r"tracks", views.TrackFavoriteViewSet, "tracks")
+router.register(r"albums", views.AlbumFavoriteViewSet, "albums")
 
 urlpatterns = router.urls
diff --git a/api/funkwhale_api/favorites/views.py b/api/funkwhale_api/favorites/views.py
index db0c909001edef6f854216a9a22895a2a42c0fb0..3b69842a023e52f6af728d577dc9b7a6c39dfe83 100644
--- a/api/funkwhale_api/favorites/views.py
+++ b/api/funkwhale_api/favorites/views.py
@@ -6,7 +6,7 @@ from django.db.models import Prefetch
 
 from funkwhale_api.activity import record
 from funkwhale_api.common import fields, permissions
-from funkwhale_api.music.models import Track
+from funkwhale_api.music.models import Track, Album
 from funkwhale_api.music import utils as music_utils
 from funkwhale_api.users.oauth import permissions as oauth_permissions
 
@@ -92,3 +92,85 @@ class TrackFavoriteViewSet(
         )
         payload = {"results": favorites, "count": len(favorites)}
         return Response(payload, status=200)
+
+
+class AlbumFavoriteViewSet(
+    mixins.CreateModelMixin,
+    mixins.DestroyModelMixin,
+    mixins.ListModelMixin,
+    viewsets.GenericViewSet,
+):
+
+    filterset_class = filters.AlbumFavoriteFilter
+    serializer_class = serializers.UserAlbumFavoriteSerializer
+    queryset = models.AlbumFavorite.objects.all().select_related(
+        "user__actor__attachment_icon"
+    )
+    permission_classes = [
+        oauth_permissions.ScopePermission,
+        permissions.OwnerPermission,
+    ]
+    required_scope = "favorites"
+    anonymous_policy = "setting"
+    owner_checks = ["write"]
+
+    def get_serializer_class(self):
+        if self.request.method.lower() in ["head", "get", "options"]:
+            return serializers.UserAlbumFavoriteSerializer
+        return serializers.UserAlbumFavoriteWriteSerializer
+
+    def create(self, request, *args, **kwargs):
+        serializer = self.get_serializer(data=request.data)
+        serializer.is_valid(raise_exception=True)
+        instance = self.perform_create(serializer)
+        serializer = self.get_serializer(instance=instance)
+        headers = self.get_success_headers(serializer.data)
+        record.send(instance)
+        return Response(
+            serializer.data, status=status.HTTP_201_CREATED, headers=headers
+        )
+
+    def get_queryset(self):
+        queryset = super().get_queryset()
+        queryset = queryset.filter(
+            fields.privacy_level_query(self.request.user, "user__privacy_level")
+        )
+        # TODO: not sure which values I should put there
+        albums = Album.objects.with_playable_uploads(
+            music_utils.get_actor_from_request(self.request)
+        ).select_related(
+            "artist", "album__artist", "attributed_to", "album__attachment_cover"
+        )
+        queryset = queryset.prefetch_related(Prefetch("album", queryset=albums))
+        return queryset
+
+    def perform_create(self, serializer):
+        album = Album.objects.get(pk=serializer.data["album"])
+        favorite = models.AlbumFavorite.add(album=album, user=self.request.user)
+        return favorite
+
+    @action(methods=["delete", "post"], detail=False)
+    def remove(self, request, *args, **kwargs):
+        try:
+            pk = int(request.data["album"])
+            favorite = request.user.album_favorites.get(album__pk=pk)
+        except (AttributeError, ValueError, models.AlbumFavorite.DoesNotExist):
+            return Response({}, status=400)
+        favorite.delete()
+        return Response([], status=status.HTTP_204_NO_CONTENT)
+
+    @action(methods=["get"], detail=False)
+    def all(self, request, *args, **kwargs):
+        """
+        Return all the favorite albums of the current user, with only limited
+        data to have a performant endpoint and avoid lots of queries just to
+        display favorites status in the UI
+        """
+        if not request.user.is_authenticated:
+            return Response({"results": [], "count": 0}, status=200)
+
+        favorites = list(
+            request.user.album_favorites.values("id", "album").order_by("id")
+        )
+        payload = {"results": favorites, "count": len(favorites)}
+        return Response(payload, status=200)
diff --git a/api/funkwhale_api/moderation/filters.py b/api/funkwhale_api/moderation/filters.py
index 629ae685f95f36b75abb9a5e172f2f0de37620c4..02631fd247aafd2ebd232c323293a3bd22b3242b 100644
--- a/api/funkwhale_api/moderation/filters.py
+++ b/api/funkwhale_api/moderation/filters.py
@@ -12,6 +12,10 @@ USER_FILTER_CONFIG = {
     "TRACK_FAVORITE": {
         "target_artist": ["track__album__artist__pk", "track__artist__pk"]
     },
+    "ALBUM_FAVORITE": {
+        # TODO: not sure which values I should put there
+        "target_artist": ["track__album__artist__pk", "track__artist__pk"]
+    },
 }
 
 
diff --git a/api/funkwhale_api/music/models.py b/api/funkwhale_api/music/models.py
index 82464e40ac16b61528dc503b2440324b5b6edef4..b3e814b68acd9e21992f3816370164cb5e2a0ae1 100644
--- a/api/funkwhale_api/music/models.py
+++ b/api/funkwhale_api/music/models.py
@@ -410,6 +410,11 @@ class Album(APIModelMixin):
     def get_moderation_url(self):
         return "/manage/library/albums/{}".format(self.pk)
 
+    def get_activity_url(self):
+        if self.mbid:
+            return "https://musicbrainz.org/recording/{}".format(self.mbid)
+        return settings.FUNKWHALE_URL + "/albums/{}".format(self.pk)
+
     @classmethod
     def get_or_create_from_title(cls, title, **kwargs):
         kwargs.update({"title": title})
diff --git a/api/funkwhale_api/music/serializers.py b/api/funkwhale_api/music/serializers.py
index df4e701335e4ab7fedcdda4ffe5274ea6f7d61db..416a587e1815e1dacd5422d76a927e81f9545866 100644
--- a/api/funkwhale_api/music/serializers.py
+++ b/api/funkwhale_api/music/serializers.py
@@ -613,6 +613,19 @@ class TrackActivitySerializer(activity_serializers.ModelSerializer):
             return o.album.title
 
 
+class AlbumActivitySerializer(activity_serializers.ModelSerializer):
+    type = serializers.SerializerMethodField()
+    name = serializers.CharField(source="title")
+    artist = serializers.CharField(source="artist.name")
+
+    class Meta:
+        model = models.Album
+        fields = ["id", "local_id", "name", "type", "artist", "album"]
+
+    def get_type(self, obj):
+        return "Audio"
+
+
 def get_embed_url(type, id):
     return settings.FUNKWHALE_EMBED_URL + "?type={}&id={}".format(type, id)
 
diff --git a/front/src/components/favorites/AlbumFavoriteIcon.vue b/front/src/components/favorites/AlbumFavoriteIcon.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ca74474c5f276fc17c75214aeae45d182bfdc804
--- /dev/null
+++ b/front/src/components/favorites/AlbumFavoriteIcon.vue
@@ -0,0 +1,53 @@
+<template>
+  <button
+    v-if="button"
+    :class="['ui', 'pink', {'inverted': isFavorite}, {'favorited': isFavorite}, 'icon', 'labeled', 'button']"
+    @click.stop="$store.dispatch('albumFavorites/toggle', album.id)"
+  >
+    <i class="heart icon" />
+    <translate
+      v-if="isFavorite"
+      translate-context="Content/Track/Button.Message"
+    >
+      In favorites
+    </translate>
+    <translate
+      v-else
+      translate-context="Content/Track/*/Verb"
+    >
+      Add to favorites
+    </translate>
+  </button>
+  <button
+    v-else
+    :class="['ui', 'favorite-icon', {'pink': isFavorite}, {'favorited': isFavorite}, 'basic', 'circular', 'icon', {'really': !border}, 'button']"
+    :aria-label="title"
+    :title="title"
+    @click.stop="$store.dispatch('albumFavorites/toggle', album.id)"
+  >
+    <i :class="['heart', {'pink': isFavorite}, 'basic', 'icon']" />
+  </button>
+</template>
+
+<script>
+export default {
+  props: {
+    album: { type: Object, default: () => { return {} } },
+    button: { type: Boolean, default: false },
+    border: { type: Boolean, default: false }
+  },
+  computed: {
+    title () {
+      if (this.isFavorite) {
+        return this.$pgettext('Content/Track/Icon.Tooltip/Verb', 'Remove from favorites')
+      } else {
+        return this.$pgettext('Content/Track/*/Verb', 'Add to favorites')
+      }
+    },
+    isFavorite () {
+      return this.$store.getters['albumFavorites/isFavorite'](this.album.id)
+    }
+  }
+
+}
+</script>
diff --git a/front/src/components/library/AlbumBase.vue b/front/src/components/library/AlbumBase.vue
index 7662e8af96d5f59d60c5c2125d3cbc3d4fa399b9..8307ae1ad646e0433b3e291f1b1d83a560429dd9 100644
--- a/front/src/components/library/AlbumBase.vue
+++ b/front/src/components/library/AlbumBase.vue
@@ -173,6 +173,10 @@
                 :album="object"
                 :is-playable="object.is_playable"
               />
+              <album-favorite-icon
+                v-if="$store.state.auth.authenticated"
+                :album="object"
+              />
               <div class="ui horizontal hidden divider" />
               <album-dropdown
                 :object="object"
@@ -254,6 +258,7 @@ import PlayButton from '@/components/audio/PlayButton.vue'
 import TagsList from '@/components/tags/List.vue'
 import ArtistLabel from '@/components/audio/ArtistLabel.vue'
 import AlbumDropdown from './AlbumDropdown.vue'
+import AlbumFavoriteIcon from '@/components/favorites/AlbumFavoriteIcon.vue'
 
 function groupByDisc (initial) {
   function inner (acc, track) {
@@ -271,6 +276,7 @@ function groupByDisc (initial) {
 export default {
   components: {
     PlayButton,
+    AlbumFavoriteIcon,
     TagsList,
     ArtistLabel,
     AlbumDropdown
diff --git a/front/src/store/albumFavorites.js b/front/src/store/albumFavorites.js
new file mode 100644
index 0000000000000000000000000000000000000000..945adb1a92052250f28252f0ef093f78ecb605f6
--- /dev/null
+++ b/front/src/store/albumFavorites.js
@@ -0,0 +1,72 @@
+import axios from 'axios'
+import logger from '@/logging'
+
+export default {
+  namespaced: true,
+  state: {
+    albums: [],
+    count: 0
+  },
+  mutations: {
+    album: (state, { id, value }) => {
+      if (value) {
+        if (state.albums.indexOf(id) === -1) {
+          state.albums.push(id)
+        }
+      } else {
+        const i = state.albums.indexOf(id)
+        if (i > -1) {
+          state.albums.splice(i, 1)
+        }
+      }
+      state.count = state.albums.length
+    },
+    reset (state) {
+      state.albums = []
+      state.count = 0
+    }
+  },
+  getters: {
+    isFavorite: (state) => (id) => {
+      return state.albums.indexOf(id) > -1
+    }
+  },
+  actions: {
+    set ({ commit, state }, { id, value }) {
+      commit('album', { id, value })
+      if (value) {
+        return axios.post('favorites/albums/', { album: id }).then((response) => {
+          logger.default.info('Successfully added album to favorites')
+        }, (response) => {
+          logger.default.info('Error while adding album to favorites')
+          commit('album', { id, value: !value })
+        })
+      } else {
+        return axios.post('favorites/albums/remove/', { album: id }).then((response) => {
+          logger.default.info('Successfully removed album from favorites')
+        }, (response) => {
+          logger.default.info('Error while removing album from favorites')
+          commit('album', { id, value: !value })
+        })
+      }
+    },
+    toggle ({ getters, dispatch }, id) {
+      dispatch('set', { id, value: !getters.isFavorite(id) })
+    },
+    fetch ({ dispatch, state, commit, rootState }, url) {
+      // will fetch favorites by batches from API to have them locally
+      const params = {
+        user: rootState.auth.profile.id,
+        page_size: 50,
+        ordering: '-creation_date'
+      }
+      const promise = axios.get('favorites/albums/all/', { params: params })
+      return promise.then((response) => {
+        logger.default.info('Fetched a batch of ' + response.data.results.length + ' favorites')
+        response.data.results.forEach(result => {
+          commit('album', { id: result.album, value: true })
+        })
+      })
+    }
+  }
+}
diff --git a/front/src/store/index.js b/front/src/store/index.js
index a7954420346599c335856b82980522dc6239730c..5c7631447d62786a783c99f5896b1a8f35a8275e 100644
--- a/front/src/store/index.js
+++ b/front/src/store/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue'
 import Vuex from 'vuex'
 import createPersistedState from 'vuex-persistedstate'
 
+import albumFavorites from './albumFavorites'
 import favorites from './favorites'
 import channels from './channels'
 import libraries from './libraries'
@@ -22,6 +23,7 @@ export default new Vuex.Store({
     auth,
     channels,
     libraries,
+    albumFavorites,
     favorites,
     instance,
     moderation,