diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2eb229b1ed7a1747e31374e16a0fa400b86259c9..6fb68fdc81cf6f6a86971be70c321eaea3750f1f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added (for new features, dependecies etc.)
 
 * `form_panel` module for conveniently creating a panel for web forms.
+* `restore_old_version` function to base functionality (caosdb.js)
+* buttons to the version history modal that allow restoring older versions
 
 ### Changed (for changes in existing functionality)
 
diff --git a/misc/entity_state_test_data.py b/misc/entity_state_test_data.py
index 72ffe278d0ad90cb5223966bcd52c79cd420f160..400b73749011834fb5390be2e00eebdde1a91062 100755
--- a/misc/entity_state_test_data.py
+++ b/misc/entity_state_test_data.py
@@ -153,9 +153,8 @@ def setup_state_model():
         "Transition").add_property("from", "under review").add_property("to", "unpublished").insert()
 
     # 1->1
-    db.Record("Edit").add_parent(
-        "Transition",
-        description="Edit this entity. The changes are not publicly available until this entity will have been reviewed and published.").add_property(
+    db.Record("Edit", description="Edit this entity. The changes are not publicly available until this entity will have been reviewed and published.").add_parent(
+        "Transition").add_property(
         "from",
         "unpublished").add_property(
             "to",
diff --git a/src/core/js/caosdb.js b/src/core/js/caosdb.js
index 90d6bfd23b989bbff133a63d65e1e68d99f4b3bb..7c3c4300f971eae16d30748e6787843334708baa 100644
--- a/src/core/js/caosdb.js
+++ b/src/core/js/caosdb.js
@@ -1071,7 +1071,7 @@ function createFileXML(name, id, parents,
  * Update, Response, Delete.
  *
  * @param {string} root - The name of the newly created document root node.
- * @param {(Document|XMLDocumentFragment)} xmls The xml documents.
+ * @param {Document[]|XMLDocumentFragment[]} xmls The xml documents.
  * @return {Document} A new xml document.
  */
 function wrapXML(root, xmls) {
@@ -1164,6 +1164,36 @@ async function update(xml) {
     return await transaction.updateEntitiesXml(wrapped);
 }
 
+
+/**
+ * Restore an old version of an entity using an xml representation.
+ * First, the old version is retrieved and the current version is set to the
+ * old one.
+ * @param versionid The version id (e.g. 123@abbabbaeff23322) of the version of
+ * the entity which shall be restored.
+ */
+async function restore_old_version(versionid){
+    // retrieve entity
+    var ent = await transaction.retrieveEntityById(versionid);
+    if (ent === undefined){
+        throw new Error(`Entity with version id ${versionid} could not be retrieved.`);
+    }
+    // remove unwanted tags (Version and Permissions)
+    ent.getElementsByTagName("Version")[0].remove();
+    var permissions = ent.getElementsByTagName("Permissions");
+    for (let i = permissions.length-1; i >=0 ; i--) {
+        permissions[i].remove();
+    }
+
+    // use XML to update entity/restore old version
+    const doc = _createDocument("Request");
+    doc.firstElementChild.appendChild(ent);
+    reps = await transaction.updateEntitiesXml(doc);
+    if (reps.getElementsByTagName("Error").length>0) {
+        throw new Error(`Could not restore the Entity to the version ${versionid}.`);
+    }
+}
+
 /**
  * Insert an entity in xml representation.
  *
diff --git a/src/core/js/webcaosdb.js b/src/core/js/webcaosdb.js
index efa28c9b39921df3e02a25ec95d4601f752fa288..03cc7bfbb399e898a88cb99dc0a05cc90289f96a 100644
--- a/src/core/js/webcaosdb.js
+++ b/src/core/js/webcaosdb.js
@@ -359,6 +359,8 @@ this.caosdb_utils = new function () {
  * connection module contains all ajax calls.
  */
 this.connection = new function () {
+    const logger = log.getLogger("connection");
+
     this._init = function () {
         /**
          * Send a get request.
@@ -376,7 +378,7 @@ this.connection = new function () {
                 if (error.status == 414) {
                     throw new Error("UriTooLongException for GET " + uri);
                 } else if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error("GET " + uri + " returned with HTTP status " + error.status + " - " + error.statusText);
                 } else {
@@ -400,7 +402,7 @@ this.connection = new function () {
                 });
             } catch (error) {
                 if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error("PUT " + uri + " returned with HTTP status " + error.status + " - " + error.statusText);
                 } else {
@@ -435,7 +437,7 @@ this.connection = new function () {
                 });
             } catch (error) {
                 if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error(
                         "POST scripting returned with HTTP status " + error.status +
@@ -461,7 +463,7 @@ this.connection = new function () {
                 });
             } catch (error) {
                 if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error("POST " + uri + " returned with HTTP status " + error.status + " - " + error.statusText);
                 } else {
@@ -486,7 +488,7 @@ this.connection = new function () {
                 });
             } catch (error) {
                 if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error("DELETE " + "Entity/" + idline + " returned with HTTP status " + error.status + " - " + error.statusText);
                 } else {
@@ -985,6 +987,9 @@ this.transaction = new function () {
  */
 var version_history = new function () {
 
+    const logger = log.getLogger("version_history");
+    this.logger = logger;
+
     this._get = connection.get;
     /**
      * Retrieve the version history of an entity and return a table with the
@@ -1035,6 +1040,7 @@ var version_history = new function () {
                         .retrieve_history(entity_id_version);
                     sparse.replaceWith(history_table);
                     version_history.init_export_history_buttons(entity);
+                    version_history.init_restore_version_buttons(entity);
                 });
         }
     }
@@ -1072,7 +1078,7 @@ var version_history = new function () {
         for (let version_info of $(entity)
                 .find(".caosdb-f-entity-version-info")) {
             $(version_info).find(".caosdb-f-entity-version-export-history-btn")
-                .click(async () => {
+                .click(() => {
                     const html_table = $(version_info).find("table")[0];
                     const history_tsv = this.get_history_tsv(html_table);
                     version_history._download_tsv(history_tsv);
@@ -1080,6 +1086,72 @@ var version_history = new function () {
         }
     }
 
+    /**
+     * Initialize the restore old version buttons of `entity`.
+     *
+     * The buttons are only visible when the user is allowed to update the
+     * entity.
+     *
+     * The causes a retrieve of the specified version of the entity and then an
+     * update that restores that version.
+     *
+     * @param {HTMLElement} [entity] - if undefined, the export buttons of all
+     *     page entities are initialized.
+     */
+    this.init_restore_version_buttons = function (entity) {
+        var entities = [entity] || $(".caosdb-entity-panel");
+
+        for (let _entity of entities) {
+            // initialize buttons only if the user is allowed to update the entity
+            if (hasEntityPermission(_entity, "UPDATE:*") || hasEntityPermission(_entity, "UPDATE:DESCRIPTION")) {
+                for (let version_info of
+                        $(_entity).find(".caosdb-f-entity-version-info")) {
+                    // find the restore button
+                    $(version_info).find(".caosdb-f-entity-version-restore-btn")
+                        .toggleClass("d-none", false) // show button
+                        .click(async (eve) => {
+                            // the version id is stored in the restore button's
+                            // data-version-id attribute
+                            const versionid = eve.delegateTarget.getAttribute("data-version-id")
+                            const reload = () => {
+                                window.location.reload();
+                            }
+                            const _alert = form_elements.make_alert({
+                                title: "Warning",
+                                message: "You are going to restore this version of the entity.",
+                                proceed_callback: async () => {
+                                    try {
+                                        await restore_old_version(versionid);
+                                        $(_alert).remove();
+                                        // reload after sucessful update
+                                        $(version_info).find(".modal-body").prepend(
+                                            $(`<div class="alert alert-success" role="alert">Restore successful! <p>You are being forwarded to the latest version of this entity or you can click <a href="#" onclick="window.location.reload()">here</a>.</p></div>`));
+                                        setTimeout(reload, 5000);
+                                    } catch (e) {
+                                        logger.error(e);
+                                        // print errors in an alert div
+                                        $(version_info).find(".modal-body").prepend(
+                                            $(`<div class="alert alert-danger alert-dismissible " role="alert"> <button class="btn-close" data-bs-dismiss="alert" aria-label="close"></button> Restore failed! <p>${e.message}</p></div>`));
+
+                                    }
+                                },
+                                cancel_callback: () => {
+                                    // do nothing
+                                    $(_alert).remove();
+                                    $(version_info).find("table").show();
+                                },
+                                proceed_text: "Yes, restore!",
+                                remember_my_decision_id: "restore_entity",
+                            });
+
+                            $(version_info).find("table").after(_alert).hide();
+                            $(_alert).addClass("text-end");
+                        });
+                }
+            }
+        }
+    }
+
     this._download_tsv = function (tsv_link) {
         window.location.href = tsv_link;
     }
@@ -1088,6 +1160,7 @@ var version_history = new function () {
     this.init = function () {
         this.init_load_history_buttons();
         this.init_export_history_buttons();
+        this.init_restore_version_buttons();
     }
 }
 
diff --git a/src/core/xsl/entity.xsl b/src/core/xsl/entity.xsl
index 92f08ea70645a8282f97f364c9fc4143f37afd6a..d8ba00a9810e9861bfba741055ea113c7f2b88bc 100644
--- a/src/core/xsl/entity.xsl
+++ b/src/core/xsl/entity.xsl
@@ -644,11 +644,14 @@
     <div class="modal-body">
       <table class="table table-hover">
         <thead>
-          <tr><th><div class="export-data">Entity ID</div></th>
+          <tr>
+            <th></th>
+            <th class="invisible"><div class="export-data">Entity ID</div></th>
             <th class="export-data">Version ID</th>
             <th class="export-data">Date</th>
             <th class="export-data">User</th>
             <th class="invisible"><div class="export-data">URI</div></th>
+            <th></th>
           </tr></thead>
         <tbody>
           <xsl:apply-templates mode="entity-version-modal-successor" select="Successor">
@@ -664,6 +667,13 @@
               <xsl:value-of select="@username"/>@<xsl:value-of select="@realm"/>
             </td>
             <td class="invisible"><div class="export-data"><xsl:value-of select="concat($entitypath, $entityId, '@', @id)"/></div></td>
+            <td>
+              <xsl:if test="not(@head='true')">
+              <button type="button" class="caosdb-f-entity-version-restore-btn btn btn-secondary d-none" title="Restore this version of the entity.">
+                <xsl:attribute name="data-version-id"><xsl:value-of select="$entityId"/>@<xsl:value-of select="@id"/></xsl:attribute>
+                <i class="bi-arrow-counterclockwise"></i></button>
+              </xsl:if>
+            </td>
           </tr>
           <xsl:apply-templates mode="entity-version-modal-predecessor" select="Predecessor">
             <xsl:with-param name="entityId" select="$entityId"/>
@@ -672,7 +682,7 @@
       </table>
     </div>
     <div class="modal-footer">
-      <button type="button" class="caosdb-f-entity-version-export-history-btn btn btn-secondary">Export history</button>
+      <button type="button" class="caosdb-f-entity-version-export-history-btn btn btn-secondary" title="Export this history table as a CSV file.">Export history</button>
     </div>
   </xsl:template>
 
@@ -745,6 +755,14 @@
         <xsl:value-of select="@username"/>@<xsl:value-of select="@realm"/>
       </td>
       <td class="invisible"><div class="export-data"><xsl:value-of select="concat($entitypath, $entityId, '@', @id)"/></div></td>
+      <td>
+        <!-- include button if it is not head, i.e. Predecessors are always old and Successors if they do have a Successor Member -->
+        <xsl:if test="(name()='Predecessor' or Successor)">
+        <button type="button" class="caosdb-f-entity-version-restore-btn btn btn-secondary d-none" title="Restore this version of the entity.">
+          <xsl:attribute name="data-version-id"><xsl:value-of select="$entityId"/>@<xsl:value-of select="@id"/></xsl:attribute>
+          <i class="bi-arrow-counterclockwise"></i></button>
+        </xsl:if>
+      </td>
     </tr>
   </xsl:template>
 
diff --git a/test/core/js/modules/webcaosdb.js.js b/test/core/js/modules/webcaosdb.js.js
index d2ef27952e41142a62eb70e144571bc9d30c52d2..5f45c32adb9d171c5d362467d0bba0840f02836f 100644
--- a/test/core/js/modules/webcaosdb.js.js
+++ b/test/core/js/modules/webcaosdb.js.js
@@ -1827,6 +1827,7 @@ QUnit.test("available", function (assert) {
     assert.equal(typeof version_history.init, "function");
     assert.equal(typeof version_history.get_history_tsv, "function");
     assert.equal(typeof version_history.init_export_history_buttons, "function");
+    assert.equal(typeof version_history.init_restore_version_buttons, "function");
     assert.equal(typeof version_history.init_load_history_buttons, "function");
     assert.equal(typeof version_history.retrieve_history, "function");
 })
@@ -1887,6 +1888,76 @@ QUnit.test("init_load_history_buttons and init_load_history_buttons", async func
     $(html).remove();
 });
 
+QUnit.test("available", function (assert) {
+    assert.equal(typeof restore_old_version, "function");
+})
+
+QUnit.test("init_restore_version_buttons", async function (assert) {
+    var xml_str = `<Response username="user1" realm="Realm1" srid="bc2f8f6b-71d6-49ca-890c-eebea3e38e18" timestamp="1606253365632" baseuri="https://localhost:10443" count="1">
+  <UserInfo username="user1" realm="Realm1">
+    <Roles>
+      <Role>role1</Role>
+    </Roles>
+  </UserInfo>
+  <Record id="8610" name="TestRecord1-6thVersion" description="This is the 6th version.">
+    <Permissions>
+      <Permission name="RETRIEVE:HISTORY" />
+      <Permission name="UPDATE:*" />
+    </Permissions>
+    <Version id="efa5ac7126c722b3f43284e150d070d6deac0ba6" >
+      <Predecessor id="f09114b227d88f23d4e23645ae471d688b1e82f7" />
+      <Successor id="5759d2bccec3662424db5bb005acea4456a299ef" />
+    </Version>
+    <Parent id="8609" name="TestRT" />
+  </Record>
+</Response>
+`;
+    var done = assert.async(1);
+    var xml = str2xml(xml_str);
+    version_history._get = async function (entity) {
+        assert.equal(entity, "Entity/8610@efa5ac7126c722b3f43284e150d070d6deac0ba6?H");
+        done();
+        $(xml).find("Version").attr("completeHistory", "true");
+        return xml;
+    }
+    var html = await transformation.transformEntities(xml);
+    var load_button = $(html).find(".caosdb-f-entity-version-load-history-btn");
+    $("body").append(html);
+
+    assert.notOk(load_button.is(":visible"), "load_button hidden");
+    load_button.click(); // nothing happens
+
+    version_history.init_load_history_buttons();
+    assert.ok(load_button.is(":visible"), "load_button is not hidden anymore");
+
+    //console.log(xml2str(restore_button[0]));
+    //assert.ok(restore_button.hasClass("d-none"), "restore_button is hidden");
+
+
+    // load_button triggers retrieval of history
+    load_button.click();
+    await sleep(500);
+
+    //console.log(xml2str(restore_button[0]));
+    //version_history.init_restore_version_buttons();
+
+    var restore_button = $("body").find(".caosdb-f-entity-version-restore-btn");
+    assert.ok(!restore_button.hasClass("d-none"), "restore_button is not hidden anymore");
+
+    // restore_button triggers retrieval of history
+    localStorage["form_elements.alert_decision.restore_entity"] = "proceed";
+    restore_button.first().click();
+    localStorage.removeItem("form_elements.alert_decision.restore_entity");
+    await sleep(500);
+
+    // restore is not possible in the unit test
+    alertdiv = $(html).find(".alert-danger");
+    assert.equal(alertdiv.length, 1, "on alert div");
+    assert.ok(alertdiv.text().indexOf("Restore failed") > 0, "Restore failed");
+
+    $(html).remove();
+});
+
 
 /* SETUP tests for user_management */
 QUnit.module("webcaosdb.js - user_management", {