diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.html
index 9e28766880..626408b739 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.html
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.html
@@ -5,8 +5,11 @@
     <div wicket:id="feedback"></div>
     <form wicket:id="form">
         <div wicket:id="editing" class="mb-3"></div>
-        <div class="position-sticky bottom-0 bg-white p-3 border line-length-limit">
+        <div class="position-sticky bottom-0 bg-white p-3 border line-length-limit hstack">
             <button type="submit" class="btn btn-primary">Save</button>
+            <span wicket:id="unsaved_changes_alert" class="text-danger flex-grow-1 text-center" role="alert">
+                <wicket:message key="unsaved_changes"/>
+            </span>
         </div>
     </form>
 </wicket:extend>
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.java
index da4a0b42a7..c255d1be51 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.java
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/AdminGradingTemplateEditPage.java
@@ -2,9 +2,10 @@ package se.su.dsv.scipro.admin.pages.grading;
 
 import jakarta.inject.Inject;
 import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.panel.FeedbackPanel;
-import org.apache.wicket.model.Model;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import se.su.dsv.scipro.admin.pages.AbstractAdminProjectPage;
 import se.su.dsv.scipro.grading.GradingReportTemplateService;
@@ -12,9 +13,9 @@ import se.su.dsv.scipro.grading.GradingReportTemplateUpdate;
 import se.su.dsv.scipro.grading.LocalizedString;
 import se.su.dsv.scipro.report.DuplicateDateException;
 import se.su.dsv.scipro.report.GradingReportTemplate;
-import se.su.dsv.scipro.report.ValidDateMustBeInTheFutureException;
 import se.su.dsv.scipro.report.NoSuchTemplateException;
 import se.su.dsv.scipro.report.TemplateLockedException;
+import se.su.dsv.scipro.report.ValidDateMustBeInTheFutureException;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -25,6 +26,8 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
             0,
             new LocalizedString("", ""));
 
+    private final WebMarkupContainer unsavedChangesAlert;
+
     @Inject
     GradingReportTemplateService gradingReportTemplateService;
 
@@ -47,7 +50,8 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
                     GradingReportTemplateUpdate update = toUpdate(
                             editingGradingTemplate);
 
-                    gradingReportTemplateService.update(id, update);
+                    GradingReportTemplate newTemplate = gradingReportTemplateService.update(id, update);
+                    editingGradingTemplate = new EditingGradingTemplate(newTemplate);
                     success(getString("template_updated"));
                 } catch (ValidDateMustBeInTheFutureException e) {
                     error(getString("valid_from_must_be_in_the_future", () -> e));
@@ -61,7 +65,23 @@ public class AdminGradingTemplateEditPage extends AbstractAdminProjectPage imple
             }
         };
 
-        form.add(new EditingGradingTemplateComponentPanel("editing", Model.of(editingGradingTemplate)));
+        unsavedChangesAlert = new WebMarkupContainer("unsaved_changes_alert") {
+            @Override
+            protected void onConfigure() {
+                super.onConfigure();
+                setVisible(editingGradingTemplate.hasChanges());
+            }
+        };
+        form.add(unsavedChangesAlert);
+        unsavedChangesAlert.setOutputMarkupPlaceholderTag(true);
+
+        form.add(new EditingGradingTemplateComponentPanel("editing", () -> editingGradingTemplate) {
+            @Override
+            protected void onTemplateChanged(AjaxRequestTarget target) {
+                super.onTemplateChanged(target);
+                target.add(unsavedChangesAlert);
+            }
+        });
         add(form);
     }
 
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplate.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplate.java
index 7e4e935ad5..04f6402920 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplate.java
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplate.java
@@ -8,8 +8,10 @@ import java.io.Serializable;
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 class EditingGradingTemplate implements Serializable {
+    private EditingGradingTemplate original;
     private String note;
     private LocalDate validFrom;
     private List<Criteria> criteria;
@@ -21,6 +23,16 @@ class EditingGradingTemplate implements Serializable {
     }
 
     EditingGradingTemplate(GradingReportTemplate template) {
+        this(template, null);
+        this.original = new EditingGradingTemplate(template, null);
+    }
+
+    /**
+     * Private constructor for creating a new instance of EditingGradingTemplate
+     * to be able to track changes made.
+     * @param doNotCreateOriginal Only exists to differentiate the signature from the public constructor
+     */
+    private EditingGradingTemplate(GradingReportTemplate template, @SuppressWarnings("unused") Void doNotCreateOriginal) {
         this.note = template.getNote();
         this.validFrom = template.getValidFrom();
         this.gradeLimits = new GradeLimits(template);
@@ -61,10 +73,28 @@ class EditingGradingTemplate implements Serializable {
                 .sum();
     }
 
+    public Boolean hasChanges() {
+        return !Objects.equals(original, this);
+    }
+
     public void addCriteria() {
         this.criteria.add(new Criteria());
     }
 
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof EditingGradingTemplate that
+                && Objects.equals(note, that.note)
+                && Objects.equals(validFrom, that.validFrom)
+                && Objects.equals(criteria, that.criteria)
+                && Objects.equals(gradeLimits, that.gradeLimits);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(note, validFrom, criteria, gradeLimits);
+    }
+
     class Criteria implements Serializable {
         enum Flag {
             OPPOSITION, REFLECTION
@@ -150,6 +180,22 @@ class EditingGradingTemplate implements Serializable {
             this.pointsRequiredToPass = pointsRequiredToPass;
         }
 
+        @Override
+        public boolean equals(Object o) {
+            return o instanceof Criteria criterion
+                    && pointsRequiredToPass == criterion.pointsRequiredToPass
+                    && Objects.equals(titleSv, criterion.titleSv)
+                    && Objects.equals(titleEn, criterion.titleEn)
+                    && Objects.equals(points, criterion.points)
+                    && flag == criterion.flag
+                    && type == criterion.type;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(titleSv, titleEn, points, flag, type, pointsRequiredToPass);
+        }
+
         class Point implements Serializable {
             private String requirementEn;
             private String requirementSv;
@@ -179,6 +225,18 @@ class EditingGradingTemplate implements Serializable {
             public void setRequirementSv(String requirementSv) {
                 this.requirementSv = requirementSv;
             }
+
+            @Override
+            public boolean equals(Object o) {
+                return o instanceof Point point
+                        && Objects.equals(requirementEn, point.requirementEn)
+                        && Objects.equals(requirementSv, point.requirementSv);
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(requirementEn, requirementSv);
+            }
         }
     }
 }
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java
index fb6b0af006..5980e6834c 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/EditingGradingTemplateComponentPanel.java
@@ -64,6 +64,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
                     public void onClick(AjaxRequestTarget target) {
                         editingGradingTemplateModel.getObject().getCriteria().remove(item.getModelObject());
                         target.add(EditingGradingTemplateComponentPanel.this);
+                        onTemplateChanged(target);
                     }
                 });
                 item.add(new CriteriaEditingPanel("criteria", item.getModel()));
@@ -75,6 +76,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
             public void onClick(AjaxRequestTarget target) {
                 editingGradingTemplateModel.getObject().addCriteria();
                 target.add(EditingGradingTemplateComponentPanel.this);
+                onTemplateChanged(target);
             }
         });
     }
@@ -117,6 +119,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
                         EditingGradingTemplate.Criteria.Type objectSelected)
                 {
                     // auto save
+                    onTemplateChanged(target);
                 }
             };
             typeChoice.setRequired(true);
@@ -149,6 +152,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
                         EditingGradingTemplate.Criteria.Flag objectSelected)
                 {
                     // auto save
+                    onTemplateChanged(target);
                 }
             };
             flagChoice.setNullValid(true);
@@ -176,6 +180,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
                     EditingGradingTemplate.Criteria.Point newPoint = criteria.new Point();
                     criteria.getPoints().add(newPoint);
                     target.add(CriteriaEditingPanel.this);
+                    onTemplateChanged(target);
                 }
             });
         }
@@ -210,13 +215,14 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
                         EditingGradingTemplate.Criteria criteria = CriteriaEditingPanel.this.getModelObject();
                         criteria.getPoints().remove(model.getObject());
                         target.add(CriteriaEditingPanel.this);
+                        onTemplateChanged(target);
                     }
                 });
             }
         }
     }
 
-    private static class GradeLimitsPanel extends GenericWebMarkupContainer<GradeLimits> {
+    private class GradeLimitsPanel extends GenericWebMarkupContainer<GradeLimits> {
         public GradeLimitsPanel(String id, IModel<GradeLimits> model) {
             super(id, model);
 
@@ -248,6 +254,7 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
                     GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject();
                     gradeLimits.addNewLimit();
                     target.add(GradeLimitsPanel.this);
+                    onTemplateChanged(target);
                 }
             });
         }
@@ -278,13 +285,14 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
                         GradeLimits gradeLimits = GradeLimitsPanel.this.getModelObject();
                         gradeLimits.getGradeLimits().remove(model.getObject());
                         target.add(GradeLimitsPanel.this);
+                        onTemplateChanged(target);
                     }
                 });
             }
         }
     }
 
-    private static class AutoSave extends AjaxFormComponentUpdatingBehavior {
+    private class AutoSave extends AjaxFormComponentUpdatingBehavior {
         public AutoSave() {
             super("input");
         }
@@ -292,6 +300,11 @@ class EditingGradingTemplateComponentPanel extends GenericPanel<EditingGradingTe
         @Override
         protected void onUpdate(AjaxRequestTarget target) {
             // just trigger the ajax call is enough to update the model object
+            onTemplateChanged(target);
         }
     }
+
+    protected void onTemplateChanged(AjaxRequestTarget target) {
+        // do nothing
+    }
 }
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/GradeLimits.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/GradeLimits.java
index 29bd5877d2..dae5812f7a 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/GradeLimits.java
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/GradeLimits.java
@@ -5,6 +5,7 @@ import se.su.dsv.scipro.report.GradingReportTemplate;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 class GradeLimits implements Serializable {
     private List<GradeLimit> gradeLimits;
@@ -41,6 +42,19 @@ class GradeLimits implements Serializable {
         return gradeLimits;
     }
 
+    @Override
+    public boolean equals(Object o) {
+
+        return o instanceof GradeLimits that
+                && Objects.equals(gradeLimits, that.gradeLimits)
+                && Objects.equals(failingGrade, that.failingGrade);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(gradeLimits, failingGrade);
+    }
+
     class GradeLimit implements Serializable {
         private String grade;
         private int lowerLimit;
@@ -60,5 +74,17 @@ class GradeLimits implements Serializable {
         public void setLowerLimit(int lowerLimit) {
             this.lowerLimit = lowerLimit;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            return o instanceof GradeLimit that
+                    && lowerLimit == that.lowerLimit
+                    && Objects.equals(grade, that.grade);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(grade, lowerLimit);
+        }
     }
 }
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/wicket-package.utf8.properties b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/wicket-package.utf8.properties
index f761f20e4e..25726d2d64 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/wicket-package.utf8.properties
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/grading/wicket-package.utf8.properties
@@ -3,3 +3,4 @@ valid_from_must_be_in_the_future=The templates valid date must be in the future.
 another_template_exists_for_the_given_date_date=There is already another ${projectType.name} template that becomes valid at ${validFrom}, please pick another date.
 template_is_locked=You can not edit templates that have become current. The template you are trying to edit became current at ${validFrom}.
 template_created=New template for ${name} created.
+unsaved_changes=The grading template has been changed, unsaved changes will be lost if you do not save.