diff --git a/app/migrations/0021_dislikecomment_likecomment.py b/app/migrations/0021_dislikecomment_likecomment.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ce9111268da53f8e2a88fdf877c4250fb354695
--- /dev/null
+++ b/app/migrations/0021_dislikecomment_likecomment.py
@@ -0,0 +1,33 @@
+# Generated by Django 3.1 on 2020-10-09 16:19
+
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('app', '0020_merge_20201009_2039'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='LikeComment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('session_id', models.CharField(max_length=32)),
+                ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='DislikeComment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
+                ('session_id', models.CharField(max_length=32)),
+                ('comment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.comment')),
+            ],
+        ),
+    ]
diff --git a/app/models.py b/app/models.py
index aa54208425b22632be3f49ce1214851651e4359d..7edbd131100bd12ee005e2f4c1da3440a32a6fb8 100644
--- a/app/models.py
+++ b/app/models.py
@@ -108,6 +108,28 @@ class Comment(models.Model):
     def __str__(self):
         return self.username
 
+    @property
+    def like_count(self):
+        count = LikeComment.objects.filter(comment=self).count()
+        return count
+    
+    @property
+    def dislike_count(self):
+        count = DislikeComment.objects.filter(comment=self).count()
+        return count
+
+
+class LikeComment(models.Model):
+    comment = models.ForeignKey(Comment, models.SET_NULL, null=True)
+    timestamp = models.DateTimeField(default=timezone.now)
+    session_id = models.CharField(max_length=32, blank=False)
+
+
+class DislikeComment(models.Model):
+    comment = models.ForeignKey(Comment, models.SET_NULL, null=True)
+    timestamp = models.DateTimeField(default=timezone.now)
+    session_id = models.CharField(max_length=32, blank=False)
+
 
 class Like(models.Model):
     materi = models.ForeignKey(Materi, models.SET_NULL, null=True)
diff --git a/app/templates/app/detail_materi.html b/app/templates/app/detail_materi.html
index 191fe2d5609640b111399aadae506fb5b5a4cda8..ccf5f3d89474fb00db7f07edefc485127f559bb6 100644
--- a/app/templates/app/detail_materi.html
+++ b/app/templates/app/detail_materi.html
@@ -288,11 +288,35 @@
                     {% else %}
                     <span style="background-color: #{{comment.profile}}" class="profile p-1 bd-highligh"></span>
                     {% endif %}
-                    <p class="p-1 bd-highligh m-0"><strong>{{comment.user.name}}</strong></p>
-                    <p class="p-1 bd-highligh m-0">•</p>
-                    <p class="timestamp p-1 bd-highligh m-0 text-muted">
-                        {{ comment.timestamp|naturaltime }}
-                    </p>
+                    <div class="d-flex flex-row justify-content-end">
+                        <p class="p-1 bd-highligh m-0"><strong>{{comment.user.name}}</strong></p>
+                        <p class="p-1 bd-highligh m-0">•</p>
+                        <p class="timestamp p-1 bd-highligh m-0 text-muted">
+                            {{ comment.timestamp|naturaltime }}
+                        </p>
+                        <div>
+                            <button id="thumb-like-comment-{{ comment.id }}" class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded" onClick="postLikeComment({{ comment.id }})">
+                                <div class="d-flex flex-row">
+                                    {% if has_liked.comment.id %}
+                                    <i id="thumb-like-comment-icon-{{ comment.id }}" aria-hidden="true" class="fas fa-thumbs-up"></i>
+                                    {% else %}
+                                    <i id="thumb-like-comment-icon-{{ comment.id }}" aria-hidden="true" class="far fa-thumbs-up"></i>
+                                    {% endif %}
+                                    <div id="like-comment-{{ comment.id }}">{{ comment.like_count }}</div>
+                                </div
+                            </button>
+                            <button id="thumb-dislike-comment-{{ comment.id }}" class="btn btn-link btn-book shadow-sm p-2 mr-2 bg-white rounded" onClick="postDislikeComment({{ comment.id }})">
+                                <div class="d-flex flex-row">
+                                    {% if has_disliked.comment.id %}
+                                    <i id="thumb-dislike-comment-icon-{{ comment.id }}" aria-hidden="true" class="fas fa-thumbs-down"></i>
+                                    {% else %}
+                                    <i id="thumb-dislike-comment-icon-{{ comment.id }}" aria-hidden="true" class="far fa-thumbs-down"></i>
+                                    {% endif %}
+                                    <div id="dislike-comment-{{ comment.id }}">{{ comment.dislike_count }}</div>
+                                </div>
+                            </button>
+                        </div>
+                    </div>
                     {% if user.is_admin %}
                     <a class="ml-auto p-1 bd-highlight close"
                         href="{% url 'delete-comment' materi_data.id comment.id %}">
@@ -426,6 +450,46 @@
         });
     });
 
+    function postLikeComment(comment_id) {
+        $.ajaxSetup({
+            beforeSend: function (xhr, settings) {
+                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
+                }
+            }
+        });
+        $.ajax({
+            type: 'POST',
+            url: "{% url 'comment-like-toggle' %}",
+            data: {
+                'comment_id': comment_id,
+                'session_id': "{{ session_id }}"
+            },
+            success: likeComment,
+            dataType: 'html'
+        });
+    }
+
+    function postDislikeComment(comment_id) {
+        $.ajaxSetup({
+            beforeSend: function (xhr, settings) {
+                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
+                }
+            }
+        });
+        $.ajax({
+            type: 'POST',
+            url: "{% url 'comment-dislike-toggle' %}",
+            data: {
+                'comment_id': comment_id,
+                'session_id': "{{ session_id }}"
+            },
+            success: dislikeComment,
+            dataType: 'html'
+        });
+    }
+
     function postAddRating(rating_score) {
         $.ajaxSetup({
             beforeSend: function (xhr, settings) {
@@ -463,6 +527,32 @@
             document.getElementById("thumb").firstChild.data = " Disukai"
         }
     }
+
+    function likeComment(data, jqXHR) {
+        var data = $.parseJSON(data)
+        likeIcon = $('#thumb-like-comment-icon-' + data['comment_id'])
+        likeValue = $('#like-comment-' + data['comment_id'])
+        if (data['liked']) {
+            likeIcon.removeClass("fas fa-thumbs-up").addClass('far fa-thumbs-up')
+            likeValue.text(parseInt(likeValue.text())-1)
+        } else {
+            likeIcon.removeClass("far fa-thumbs-up").addClass('fas fa-thumbs-up')
+            likeValue.text(parseInt(likeValue.text())+1)
+        }
+    }
+
+    function dislikeComment(data, jqXHR) {
+        var data = $.parseJSON(data)
+        dislikeIcon = $('#thumb-dislike-comment-icon-' + data['comment_id'])
+        dislikeValue = $('#dislike-comment-' + data['comment_id'])
+        if (data['disliked']) {
+            dislikeIcon.removeClass("fas fa-thumbs-down").addClass('far fa-thumbs-down')
+            dislikeValue.text(parseInt(dislikeValue.text())-1)
+        } else {
+            dislikeIcon.removeClass("far fa-thumbs-down").addClass('fas fa-thumbs-down')
+            dislikeValue.text(parseInt(dislikeValue.text())+1)
+        }
+    }
         
     function getCitation(text){
         var $temp = $("<input>");
diff --git a/app/tests.py b/app/tests.py
index b0bd85ccad56f05f799e14205e34a2e133bb5730..731ce8fccee78e00aaefb5830bae2f831f331ad3 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -28,9 +28,11 @@ from digipus.settings import TIME_ZONE
 from .models import (
     Category,
     Comment,
+    DislikeComment,
     DownloadStatistics,
     Materi,
     Like,
+    LikeComment,
     Rating,
     ReqMaterial,
     RatingContributor,
@@ -363,6 +365,96 @@ class DetailMateriTest(TestCase):
         response = self.client.get(url)
         self.assertContains(response, "Anonymous")
 
+    def test_comment_disliked_by_anonymous(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+        comment = Comment.objects.get(comment="This is new comment by Anonymous").id
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+
+        payload = {"comment": comment, "session_id": session_id}
+        ajax_response = self.client.post("/comment/dislike/", payload)
+        num_of_comment_dislikes = DislikeComment.objects.filter(comment=comment).count()
+        self.assertEqual(num_of_comment_dislikes, 0)
+
+    def test_comment_liked_by_anonymous(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+        comment = Comment.objects.get(comment="This is new comment by Anonymous").id
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+
+        payload = {"comment": comment, "session_id": session_id}
+        ajax_response = self.client.post("/comment/like/", payload)
+        num_of_comment_likes = LikeComment.objects.filter(comment=comment).count()
+        self.assertEqual(num_of_comment_likes, 0)
+
+    def test_comment_undisliked_by_anonymous(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+        comment = Comment.objects.get(comment="This is new comment by Anonymous")
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+        
+        payload = {"comment": comment, "session_id": session_id}
+        ajax_response = self.client.post("/comment/dislike/", payload)
+        
+        ajax_response = self.client.post("/comment/dislike/", payload)
+        num_of_comment_dislikes = DislikeComment.objects.filter(comment=comment, session_id=session_id).count()
+        self.assertEqual(num_of_comment_dislikes, 0)
+
+    def test_comment_unliked_by_anonymous(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+        comment = Comment.objects.get(comment="This is new comment by Anonymous")
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+        
+        payload = {"comment": comment, "session_id": session_id}
+        ajax_response = self.client.post("/comment/like/", payload)
+        
+        ajax_response = self.client.post("/comment/like/", payload)
+        num_of_comment_likes = LikeComment.objects.filter(comment=comment, session_id=session_id).count()
+        self.assertEqual(num_of_comment_likes, 0)
+
+    def test_comment_new_does_not_have_dislike(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+
+        comment = Comment.objects.get(comment="This is new comment by Anonymous")
+        comment_dislike_counter = DislikeComment.objects.filter(comment=comment, session_id=session_id).count()
+        self.assertEqual(comment_dislike_counter, 0)
+
+    def test_comment_new_does_not_have_like(self):
+        url_materi = self.url
+        self.client.get("/logout/")
+        self.client.login(**self.anonymous_credential)
+
+        response = self.client.get(url_materi)
+        session_id = response.context["session_id"]
+        self.client.post(url_materi, {"comment": "This is new comment by Anonymous"})
+
+        comment = Comment.objects.get(comment="This is new comment by Anonymous")
+        comment_like_counter = LikeComment.objects.filter(comment=comment, session_id=session_id).count()
+        self.assertEqual(comment_like_counter, 0)
+
     def test_detail_materi_contains_form_comment(self):
         self.client.login(**self.contributor_credential)
         response = self.client.get(self.url)
diff --git a/app/urls.py b/app/urls.py
index 8cc94d38e80b5fc56f3b5a2d2dab1318408af9bc..baa6bccb6630849347f02cd47363536d7cc68d83 100644
--- a/app/urls.py
+++ b/app/urls.py
@@ -13,6 +13,8 @@ urlpatterns = [
     path("materi/like/", views.toggle_like, name="PostLikeToggle"),
     path("delete/<int:pk_materi>/<int:pk_comment>",
          views.delete_comment, name="delete-comment"),
+    path("comment/like/", views.toggle_like_comment, name="comment-like-toggle"),
+    path("comment/dislike/", views.toggle_dislike_comment, name="comment-dislike-toggle"),
     path("materi/<int:pk>/delete", views.delete_materi, name="detele-materi"),
     path("materi/<int:pk>/unduh", views.download_materi, name="download-materi"),
     path("materi/<int:pk>/view", views.view_materi, name="view-materi"),
diff --git a/app/views.py b/app/views.py
index 034e74eb13eee27adcf8b3fca9a2ea038a374c05..a42118b080da8daf030d1b703c2f292072f7e4b6 100644
--- a/app/views.py
+++ b/app/views.py
@@ -20,8 +20,10 @@ from app.forms import SuntingProfilForm, UploadMateriForm, RatingContributorForm
 from app.models import (
     Category,
     Comment,
+    DislikeComment,
     Materi,
     Like,
+    LikeComment,
     ViewStatistics,
     DownloadStatistics,
     ReqMaterial,
@@ -169,7 +171,14 @@ class DetailMateri(TemplateView):
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
         query_set_for_comment = Comment.objects.filter(materi=context["materi_data"])
+        has_liked = {}
+        has_disliked = {}
+        for comment in query_set_for_comment:
+            has_liked[comment.id] = LikeComment.objects.filter(comment=comment, session_id=self.request.session.session_key).exists()
+            has_disliked[comment.id] = DislikeComment.objects.filter(comment=comment, session_id=self.request.session.session_key).exists()
         context["comment_data"] = query_set_for_comment
+        context["has_liked"] = has_liked
+        context["has_disliked"] = has_disliked
         return self.render_to_response(context=context)
 
     def get_user_name(self, request):
@@ -227,6 +236,41 @@ def delete_comment(request, pk_materi, pk_comment):
     comment.delete()
     return HttpResponseRedirect(url)
 
+def toggle_like_comment(request):
+    if request.method == "POST":
+        comment_id = request.POST.get("comment_id", None)
+        session_id = request.POST.get("session_id", None)
+        if comment_id is None or session_id is None:
+            return JsonResponse({"success": False, "msg": "Missing parameter", "comment_id": comment_id})
+        comment = get_object_or_404(Comment, pk=comment_id)
+        has_liked = LikeComment.objects.filter(comment=comment, session_id=session_id).exists()
+        if has_liked:
+            like = get_object_or_404(LikeComment, comment=comment, session_id=session_id)
+            like.delete()
+            return JsonResponse({"success": True, "liked": True, "comment_id": comment_id})
+        else:
+            LikeComment(comment=comment, session_id=session_id).save()
+            return JsonResponse({"success": True, "liked": False, "comment_id": comment_id})
+    else:
+        return JsonResponse({"success": False, "msg": "Unsuported method", "comment_id": comment_id})
+
+def toggle_dislike_comment(request):
+    if request.method == "POST":
+        comment_id = request.POST.get("comment_id", None)
+        session_id = request.POST.get("session_id", None)
+        if comment_id is None or session_id is None:
+            return JsonResponse({"success": False, "msg": "Missing parameter", "comment_id": comment_id})
+        comment = get_object_or_404(Comment, pk=comment_id)
+        has_disliked = DislikeComment.objects.filter(comment=comment, session_id=session_id).exists()
+        if has_disliked:
+            dislike = get_object_or_404(DislikeComment, comment=comment, session_id=session_id)
+            dislike.delete()
+            return JsonResponse({"success": True, "disliked": True, "comment_id": comment_id})
+        else:
+            DislikeComment(comment=comment, session_id=session_id).save()
+            return JsonResponse({"success": True, "disliked": False, "comment_id": comment_id})
+    else:
+        return JsonResponse({"success": False, "msg": "Unsuported method", "comment_id": comment_id})
 
 def get_citation_ieee(request, materi):
     current_date = datetime.datetime.now()