diff --git a/src/public/javascripts/services/sidebar.js b/src/public/javascripts/services/sidebar.js
index 7e57d44b8..e7edd6f19 100644
--- a/src/public/javascripts/services/sidebar.js
+++ b/src/public/javascripts/services/sidebar.js
@@ -48,8 +48,7 @@ class Sidebar {
         this.widgets = [];
         this.$widgetContainer.empty();
 
-        //const widgetClasses = [AttributesWidget, LinkMapWidget, NoteRevisionsWidget, NoteInfoWidget];
-        const widgetClasses = [AttributesWidget];
+        const widgetClasses = [AttributesWidget, LinkMapWidget, NoteRevisionsWidget, NoteInfoWidget];
 
         for (const widgetClass of widgetClasses) {
             const widget = new widgetClass(this.ctx);
diff --git a/src/public/javascripts/widgets/link_map.js b/src/public/javascripts/widgets/link_map.js
index a6bac5f8e..4452649b3 100644
--- a/src/public/javascripts/widgets/link_map.js
+++ b/src/public/javascripts/widgets/link_map.js
@@ -3,6 +3,7 @@ import linkMapDialog from "../dialogs/link_map.js";
 import server from "../services/server.js";
 import treeCache from "../services/tree_cache.js";
 import linkService from "../services/link.js";
+import StandardWidget from "./standard_widget.js";
 
 let linkMapContainerIdCtr = 1;
 
@@ -22,21 +23,15 @@ const linkOverlays = [
     } ]
 ];
 
-class LinkMapWidget {
+class LinkMapWidget extends StandardWidget {
     /**
      * @param {TabContext} ctx
-     * @param {jQuery} $widget
+     * @param {object} state
      */
-    constructor(ctx, $widget) {
-        this.ctx = ctx;
-        this.$widget = $widget;
-        this.$widget.on('show.bs.collapse', () => this.renderBody());
-        this.$widget.on('show.bs.collapse', () => this.ctx.stateChanged());
-        this.$widget.on('hide.bs.collapse', () => this.ctx.stateChanged());
-        this.$title = this.$widget.find('.widget-title');
+    constructor(ctx, state) {
+        super(ctx, state,'link-map');
+
         this.$title.text("Link map");
-        this.$headerActions = this.$widget.find('.widget-header-actions');
-        this.$bodyWrapper = this.$widget.find('.body-wrapper');
 
         const $showFullButton = $("").append("show full").addClass('widget-header-action');
         $showFullButton.click(() => {
@@ -46,15 +41,10 @@ class LinkMapWidget {
         this.$headerActions.append($showFullButton);
     }
 
-    async renderBody() {
-        if (!this.isVisible()) {
-            return;
-        }
+    async doRenderBody() {
+        this.$body.html(TPL);
 
-        const $body = this.$widget.find('.card-body');
-        $body.html(TPL);
-
-        this.$linkMapContainer = $body.find('.link-map-container');
+        this.$linkMapContainer = this.$body.find('.link-map-container');
         this.$linkMapContainer.attr("id", "link-map-container-" + linkMapContainerIdCtr++);
 
         await libraryLoader.requireLibrary(libraryLoader.LINK_MAP);
@@ -233,17 +223,6 @@ class LinkMapWidget {
     noteIdToId(noteId) {
         return "link-map-note-" + noteId;
     }
-
-    getWidgetState() {
-        return {
-            id: 'attributes',
-            visible: this.isVisible()
-        };
-    }
-
-    isVisible() {
-        return this.$bodyWrapper.is(":visible");
-    }
 }
 
 export default LinkMapWidget;
\ No newline at end of file
diff --git a/src/public/javascripts/widgets/note_info.js b/src/public/javascripts/widgets/note_info.js
index 705b59648..0d001d95a 100644
--- a/src/public/javascripts/widgets/note_info.js
+++ b/src/public/javascripts/widgets/note_info.js
@@ -1,3 +1,5 @@
+import StandardWidget from "./standard_widget.js";
+
 const TPL = `
 
     
@@ -23,36 +25,25 @@ const TPL = `
 
 `;
 
-class NoteInfoWidget {
+class NoteInfoWidget extends StandardWidget {
     /**
      * @param {TabContext} ctx
-     * @param {jQuery} $widget
+     * @param {object} state
      */
-    constructor(ctx, $widget) {
-        this.ctx = ctx;
-        this.$widget = $widget;
-        this.$widget.on('show.bs.collapse', () => this.renderBody());
-        this.$widget.on('show.bs.collapse', () => this.ctx.stateChanged());
-        this.$widget.on('hide.bs.collapse', () => this.ctx.stateChanged());
-        this.$title = this.$widget.find('.widget-title');
+    constructor(ctx, state) {
+        super(ctx, state, 'note-info');
+
         this.$title.text("Note info");
-        this.$bodyWrapper = this.$widget.find('.body-wrapper');
     }
 
-    async renderBody() {
-        if (!this.isVisible()) {
-            return;
-        }
+    async doRenderBody() {
+        this.$body.html(TPL);
 
-        const $body = this.$widget.find('.card-body');
-
-        $body.html(TPL);
-
-        const $noteId = $body.find(".note-info-note-id");
-        const $dateCreated = $body.find(".note-info-date-created");
-        const $dateModified = $body.find(".note-info-date-modified");
-        const $type = $body.find(".note-info-type");
-        const $mime = $body.find(".note-info-mime");
+        const $noteId = this.$body.find(".note-info-note-id");
+        const $dateCreated = this.$body.find(".note-info-date-created");
+        const $dateModified = this.$body.find(".note-info-date-modified");
+        const $type = this.$body.find(".note-info-type");
+        const $mime = this.$body.find(".note-info-mime");
 
         const note = this.ctx.note;
 
@@ -65,20 +56,9 @@ class NoteInfoWidget {
 
     syncDataReceived(syncData) {
         if (syncData.find(sd => sd.entityName === 'notes' && sd.entityId === this.ctx.note.noteId)) {
-            this.renderBody();
+            this.doRenderBody();
         }
     }
-
-    getWidgetState() {
-        return {
-            id: 'attributes',
-            visible: this.isVisible()
-        };
-    }
-
-    isVisible() {
-        return this.$bodyWrapper.is(":visible");
-    }
 }
 
 export default NoteInfoWidget;
\ No newline at end of file
diff --git a/src/public/javascripts/widgets/note_revisions.js b/src/public/javascripts/widgets/note_revisions.js
index 9f210d778..e16ecd30c 100644
--- a/src/public/javascripts/widgets/note_revisions.js
+++ b/src/public/javascripts/widgets/note_revisions.js
@@ -1,42 +1,33 @@
 import server from "../services/server.js";
+import StandardWidget from "./standard_widget.js";
 
 const TPL = `
 
 `;
 
-class NoteRevisionsWidget {
+class NoteRevisionsWidget extends StandardWidget {
     /**
      * @param {TabContext} ctx
-     * @param {jQuery} $widget
+     * @param {object} state
      */
-    constructor(ctx, $widget) {
-        this.ctx = ctx;
-        this.$widget = $widget;
-        this.$widget.on('show.bs.collapse', () => this.renderBody());
-        this.$widget.on('show.bs.collapse', () => this.ctx.stateChanged());
-        this.$widget.on('hide.bs.collapse', () => this.ctx.stateChanged());
-        this.$title = this.$widget.find('.widget-title');
+    constructor(ctx, state) {
+        super(ctx, state, 'note-revisions');
+
         this.$title.text("Note revisions");
-        this.$bodyWrapper = this.$widget.find('.body-wrapper');
     }
 
-    async renderBody() {
-        if (!this.isVisible()) {
-            return;
-        }
-
-        const $body = this.$widget.find('.card-body');
+    async doRenderBody() {
         const revisionItems = await server.get(`notes/${this.ctx.note.noteId}/revisions`);
 
         if (revisionItems.length === 0) {
-            $body.text("No revisions yet...");
+            this.$body.text("No revisions yet...");
             return;
         }
 
-        $body.html(TPL);
+        this.$body.html(TPL);
 
-        const $list = $body.find('.note-revision-list');
+        const $list = this.$body.find('.note-revision-list');
 
         for (const item of revisionItems) {
             $list.append($('').append($("", {
@@ -50,20 +41,9 @@ class NoteRevisionsWidget {
 
     syncDataReceived(syncData) {
         if (syncData.find(sd => sd.entityName === 'note_revisions' && sd.noteId === this.ctx.note.noteId)) {
-            this.renderBody();
+            this.doRenderBody();
         }
     }
-
-    getWidgetState() {
-        return {
-            id: 'attributes',
-            visible: this.isVisible()
-        };
-    }
-
-    isVisible() {
-        return this.$bodyWrapper.is(":visible");
-    }
 }
 
 export default NoteRevisionsWidget;
\ No newline at end of file
diff --git a/src/public/javascripts/widgets/standard_widget.js b/src/public/javascripts/widgets/standard_widget.js
index 09d21110d..cef7e60e5 100644
--- a/src/public/javascripts/widgets/standard_widget.js
+++ b/src/public/javascripts/widgets/standard_widget.js
@@ -8,7 +8,7 @@ const WIDGET_TPL = `
         
     
 
-    
@@ -18,17 +18,19 @@ class StandardWidget {
     /**
      * @param {TabContext} ctx
      * @param {object} state
-     * @param {string} widgetId
+     * @param {string} widgetName
      */
-    constructor(ctx, state, widgetId) {
+    constructor(ctx, state, widgetName) {
         this.ctx = ctx;
-        this.widgetId = widgetId;
+        this.widgetName = widgetName;
+
+        const widgetId = `tab-${ctx.tabId}-widget-${widgetName}`;
 
         this.$widget = $(WIDGET_TPL);
-        this.$widget.find('[data-target]').attr('data-target', "#widget-" + widgetId);
+        this.$widget.find('[data-target]').attr('data-target', "#" + widgetId);
 
         this.$bodyWrapper = this.$widget.find('.body-wrapper');
-        this.$bodyWrapper.attr('id', "widget-" + widgetId);
+        this.$bodyWrapper.attr('id', widgetId);
 
         if (state && state.visible) {
             this.$bodyWrapper.addClass("show");
@@ -62,7 +64,7 @@ class StandardWidget {
 
     getWidgetState() {
         return {
-            id: this.widgetId,
+            name: this.widgetName,
             visible: this.isVisible()
         };
     }