diff --git a/cypress/integration/history.spec.ts b/cypress/integration/history.spec.ts
index fcb883454..7274c1010 100644
--- a/cypress/integration/history.spec.ts
+++ b/cypress/integration/history.spec.ts
@@ -38,10 +38,10 @@ describe('History', () => {
           .first()
           .as('pin-button')
         cy.get('@pin-button')
-          .should('not.have.class', 'pinned')
+          .should('have.class', 'pinned')
           .click()
         cy.get('@pin-button')
-          .should('have.class', 'pinned')
+          .should('not.have.class', 'pinned')
       })
 
       it('Table', () => {
@@ -51,10 +51,10 @@ describe('History', () => {
           .first()
           .as('pin-button')
         cy.get('@pin-button')
-          .should('not.have.class', 'pinned')
+          .should('have.class', 'pinned')
           .click()
         cy.get('@pin-button')
-          .should('have.class', 'pinned')
+          .should('not.have.class', 'pinned')
       })
     })
 
@@ -71,7 +71,7 @@ describe('History', () => {
         cy.get('.fa-thumb-tack')
           .first()
           .click()
-        cy.get('.modal-dialog')
+        cy.get('.notifications-area .toast')
           .should('be.visible')
       })
 
@@ -81,7 +81,7 @@ describe('History', () => {
         cy.get('.fa-thumb-tack')
           .first()
           .click()
-        cy.get('.modal-dialog')
+        cy.get('.notifications-area .toast')
           .should('be.visible')
       })
     })
diff --git a/public/api/private/history b/public/api/private/history
index 38e501a6e..93f9acf82 100644
--- a/public/api/private/history
+++ b/public/api/private/history
@@ -1,17 +1,19 @@
 [
     {
-        "id": "29QLD0AmT-adevdOPECtqg",
+        "identifier": "29QLD0AmT-adevdOPECtqg",
         "title": "HedgeDoc community call 2020-04-26",
         "lastVisited": "2020-05-16T22:26:56.547Z",
+        "pinStatus": false,
         "tags": [
             "HedgeDoc",
             "Community Call"
         ]
     },
     {
-        "id": "features",
+        "identifier": "features",
         "title": "Features",
         "lastVisited": "2020-05-31T15:20:36.088Z",
+        "pinStatus": true,
         "tags": [
             "features",
             "cool",
@@ -19,15 +21,17 @@
         ]
     },
     {
-        "id": "ODakLc2MQkyyFc_Xmb53sg",
+        "identifier": "ODakLc2MQkyyFc_Xmb53sg",
         "title": "HedgeDoc V2 API",
         "lastVisited": "2020-05-25T19:48:14.025Z",
+        "pinStatus": false,
         "tags": []
     },
     {
-        "id": "l8JuWxApTR6Fqa0LCrpnLg",
+        "identifier": "l8JuWxApTR6Fqa0LCrpnLg",
         "title": "Community call - Let’s meet! (2020-06-06 18:00 UTC / 20:00 CEST)",
         "lastVisited": "2020-05-24T16:04:36.433Z",
+        "pinStatus": false,
         "tags": [
             "agenda",
             "HedgeDoc community",
diff --git a/src/api/history/dto-methods.ts b/src/api/history/dto-methods.ts
new file mode 100644
index 000000000..ccdcce30e
--- /dev/null
+++ b/src/api/history/dto-methods.ts
@@ -0,0 +1,33 @@
+/*
+ * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { HistoryEntry, HistoryEntryOrigin } from '../../redux/history/types'
+import { HistoryEntryDto, HistoryEntryPutDto, HistoryEntryUpdateDto } from './types'
+
+export const historyEntryDtoToHistoryEntry = (entryDto: HistoryEntryDto): HistoryEntry => {
+  return {
+    origin: HistoryEntryOrigin.REMOTE,
+    title: entryDto.title,
+    pinStatus: entryDto.pinStatus,
+    identifier: entryDto.identifier,
+    tags: entryDto.tags,
+    lastVisited: entryDto.lastVisited
+  }
+}
+
+export const historyEntryToHistoryEntryPutDto = (entry: HistoryEntry): HistoryEntryPutDto => {
+  return {
+    pinStatus: entry.pinStatus,
+    lastVisited: entry.lastVisited,
+    note: entry.identifier
+  }
+}
+
+export const historyEntryToHistoryEntryUpdateDto = (entry: HistoryEntry): HistoryEntryUpdateDto => {
+  return {
+    pinStatus: entry.pinStatus
+  }
+}
diff --git a/src/api/history/index.ts b/src/api/history/index.ts
index abb76de9a..2786ec14a 100644
--- a/src/api/history/index.ts
+++ b/src/api/history/index.ts
@@ -4,22 +4,37 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { HistoryEntry } from '../../components/history-page/history-page'
 import { defaultFetchConfig, expectResponseCode, getApiUrl } from '../utils'
+import { HistoryEntryDto, HistoryEntryPutDto, HistoryEntryUpdateDto } from './types'
 
-export const getHistory = async (): Promise<HistoryEntry[]> => {
+export const getHistory = async (): Promise<HistoryEntryDto[]> => {
   const response = await fetch(getApiUrl() + '/history')
   expectResponseCode(response)
-  return await response.json() as Promise<HistoryEntry[]>
+  return await response.json() as Promise<HistoryEntryDto[]>
 }
 
-export const setHistory = async (entries: HistoryEntry[]): Promise<void> => {
+export const postHistory = async (entries: HistoryEntryPutDto[]): Promise<void> => {
   const response = await fetch(getApiUrl() + '/history', {
     ...defaultFetchConfig,
     method: 'POST',
-    body: JSON.stringify({
-      history: entries
-    })
+    body: JSON.stringify(entries)
+  })
+  expectResponseCode(response)
+}
+
+export const updateHistoryEntryPinStatus = async (noteId: string, entry: HistoryEntryUpdateDto): Promise<void> => {
+  const response = await fetch(getApiUrl() + '/history/' + noteId, {
+    ...defaultFetchConfig,
+    method: 'PUT',
+    body: JSON.stringify(entry)
+  })
+  expectResponseCode(response)
+}
+
+export const deleteHistoryEntry = async (noteId: string): Promise<void> => {
+  const response = await fetch(getApiUrl() + '/history/' + noteId, {
+    ...defaultFetchConfig,
+    method: 'DELETE'
   })
   expectResponseCode(response)
 }
@@ -31,21 +46,3 @@ export const deleteHistory = async (): Promise<void> => {
   })
   expectResponseCode(response)
 }
-
-export const updateHistoryEntry = async (noteId: string, entry: HistoryEntry): Promise<HistoryEntry> => {
-  const response = await fetch(getApiUrl() + '/history/' + noteId, {
-    ...defaultFetchConfig,
-    method: 'PUT',
-    body: JSON.stringify(entry)
-  })
-  expectResponseCode(response)
-  return await response.json() as Promise<HistoryEntry>
-}
-
-export const deleteHistoryEntry = async (noteId: string): Promise<void> => {
-  const response = await fetch(getApiUrl() + '/history/' + noteId, {
-    ...defaultFetchConfig,
-    method: 'DELETE'
-  })
-  expectResponseCode(response)
-}
diff --git a/src/api/history/types.d.ts b/src/api/history/types.d.ts
new file mode 100644
index 000000000..5e42df888
--- /dev/null
+++ b/src/api/history/types.d.ts
@@ -0,0 +1,23 @@
+/*
+ * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export interface HistoryEntryPutDto {
+  note: string
+  pinStatus: boolean
+  lastVisited: string
+}
+
+export interface HistoryEntryUpdateDto {
+  pinStatus: boolean
+}
+
+export interface HistoryEntryDto {
+  identifier: string
+  title: string
+  lastVisited: string
+  tags: string[]
+  pinStatus: boolean
+}
diff --git a/src/components/application-loader/initializers/index.ts b/src/components/application-loader/initializers/index.ts
index e1ed6b6b3..0c014aa46 100644
--- a/src/components/application-loader/initializers/index.ts
+++ b/src/components/application-loader/initializers/index.ts
@@ -6,6 +6,7 @@
 
 import { loadAllConfig } from './configLoader'
 import { setUpI18n } from './i18n'
+import { refreshHistoryState } from '../../../redux/history/methods'
 
 const customDelay: () => Promise<void> = async () => {
   if (window.localStorage.getItem('customDelay')) {
@@ -27,6 +28,9 @@ export const createSetUpTaskList = (baseUrl: string): InitTask[] => {
   }, {
     name: 'Load config',
     task: loadAllConfig(baseUrl)
+  }, {
+    name: 'Load history state',
+    task: refreshHistoryState()
   }, {
     name: 'Add Delay',
     task: customDelay()
diff --git a/src/components/history-page/entry-menu/entry-menu.tsx b/src/components/history-page/entry-menu/entry-menu.tsx
index fb671a48e..a72ef4e74 100644
--- a/src/components/history-page/entry-menu/entry-menu.tsx
+++ b/src/components/history-page/entry-menu/entry-menu.tsx
@@ -9,24 +9,28 @@ import { Dropdown } from 'react-bootstrap'
 import { Trans, useTranslation } from 'react-i18next'
 import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
 import { ShowIf } from '../../common/show-if/show-if'
-import { HistoryEntryOrigin } from '../history-page'
 import { DeleteNoteItem } from './delete-note-item'
 import './entry-menu.scss'
 import { RemoveNoteEntryItem } from './remove-note-entry-item'
+import { HistoryEntryOrigin } from '../../../redux/history/types'
+import { useSelector } from 'react-redux'
+import { ApplicationState } from '../../../redux'
 
 export interface EntryMenuProps {
   id: string;
   title: string
-  location: HistoryEntryOrigin
+  origin: HistoryEntryOrigin
   isDark: boolean;
   onRemove: () => void
   onDelete: () => void
   className?: string
 }
 
-export const EntryMenu: React.FC<EntryMenuProps> = ({ id, title, location, isDark, onRemove, onDelete, className }) => {
+export const EntryMenu: React.FC<EntryMenuProps> = ({ id, title, origin, isDark, onRemove, onDelete, className }) => {
   useTranslation()
 
+  const userExists = useSelector((state: ApplicationState) => !!state.user)
+
   return (
     <Dropdown className={ `d-inline-flex ${ className || '' }` }>
       <Dropdown.Toggle variant={ isDark ? 'secondary' : 'light' } id={ `dropdown-card-${ id }` }
@@ -40,13 +44,13 @@ export const EntryMenu: React.FC<EntryMenuProps> = ({ id, title, location, isDar
           <Trans i18nKey="landing.history.menu.recentNotes"/>
         </Dropdown.Header>
 
-        <ShowIf condition={ location === HistoryEntryOrigin.LOCAL }>
+        <ShowIf condition={ origin === HistoryEntryOrigin.LOCAL }>
           <Dropdown.Item disabled>
             <ForkAwesomeIcon icon="laptop" fixedWidth={ true } className="mx-2"/>
             <Trans i18nKey="landing.history.menu.entryLocal"/>
           </Dropdown.Item>
         </ShowIf>
-        <ShowIf condition={ location === HistoryEntryOrigin.REMOTE }>
+        <ShowIf condition={ origin === HistoryEntryOrigin.REMOTE }>
           <Dropdown.Item disabled>
             <ForkAwesomeIcon icon="cloud" fixedWidth={ true } className="mx-2"/>
             <Trans i18nKey="landing.history.menu.entryRemote"/>
@@ -54,9 +58,10 @@ export const EntryMenu: React.FC<EntryMenuProps> = ({ id, title, location, isDar
         </ShowIf>
         <RemoveNoteEntryItem onConfirm={ onRemove } noteTitle={ title }/>
 
-        <Dropdown.Divider/>
-
-        <DeleteNoteItem onConfirm={ onDelete } noteTitle={ title }/>
+        <ShowIf condition={ userExists }>
+          <Dropdown.Divider/>
+          <DeleteNoteItem onConfirm={ onDelete } noteTitle={ title }/>
+        </ShowIf>
       </Dropdown.Menu>
     </Dropdown>
   )
diff --git a/src/components/history-page/history-card/history-card-list.tsx b/src/components/history-page/history-card/history-card-list.tsx
index 7273c49b9..5e4a013b2 100644
--- a/src/components/history-page/history-card/history-card-list.tsx
+++ b/src/components/history-page/history-card/history-card-list.tsx
@@ -7,17 +7,17 @@
 import React from 'react'
 import { Row } from 'react-bootstrap'
 import { Pager } from '../../common/pagination/pager'
-import { HistoryEntriesProps } from '../history-content/history-content'
+import { HistoryEntriesProps, HistoryEventHandlers } from '../history-content/history-content'
 import { HistoryCard } from './history-card'
 
-export const HistoryCardList: React.FC<HistoryEntriesProps> = ({ entries, onPinClick, onRemoveClick, onDeleteClick, pageIndex, onLastPageIndexChange }) => {
+export const HistoryCardList: React.FC<HistoryEntriesProps & HistoryEventHandlers> = ({ entries, onPinClick, onRemoveClick, onDeleteClick, pageIndex, onLastPageIndexChange }) => {
   return (
     <Row className="justify-content-start">
       <Pager numberOfElementsPerPage={ 9 } pageIndex={ pageIndex } onLastPageIndexChange={ onLastPageIndexChange }>
         {
           entries.map((entry) => (
             <HistoryCard
-              key={ entry.id }
+              key={ entry.identifier }
               entry={ entry }
               onPinClick={ onPinClick }
               onRemoveClick={ onRemoveClick }
diff --git a/src/components/history-page/history-card/history-card.tsx b/src/components/history-page/history-card/history-card.tsx
index 307185732..badb9ce67 100644
--- a/src/components/history-page/history-card/history-card.tsx
+++ b/src/components/history-page/history-card/history-card.tsx
@@ -5,26 +5,34 @@
  */
 
 import { DateTime } from 'luxon'
-import React from 'react'
+import React, { useCallback } from 'react'
 import { Badge, Card } from 'react-bootstrap'
 import { Link } from 'react-router-dom'
 import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
 import { EntryMenu } from '../entry-menu/entry-menu'
-import { HistoryEntryProps } from '../history-content/history-content'
+import { HistoryEntryProps, HistoryEventHandlers } from '../history-content/history-content'
 import { PinButton } from '../pin-button/pin-button'
 import { formatHistoryDate } from '../utils'
 import './history-card.scss'
 
-export const HistoryCard: React.FC<HistoryEntryProps> = ({ entry, onPinClick, onRemoveClick, onDeleteClick }) => {
+export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = ({ entry, onPinClick, onRemoveClick, onDeleteClick }) => {
+  const onRemove = useCallback(() => {
+    onRemoveClick(entry.identifier)
+  }, [onRemoveClick, entry.identifier])
+
+  const onDelete = useCallback(() => {
+    onDeleteClick(entry.identifier)
+  }, [onDeleteClick, entry.identifier])
+
   return (
     <div className="p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4">
       <Card className="card-min-height" text={ 'dark' } bg={ 'light' }>
         <Card.Body className="p-2 d-flex flex-row justify-content-between">
           <div className={ 'd-flex flex-column' }>
-            <PinButton isDark={ false } isPinned={ entry.pinned }
-                       onPinClick={ () => onPinClick(entry.id, entry.location) }/>
+            <PinButton isDark={ false } isPinned={ entry.pinStatus }
+                       onPinClick={ () => onPinClick(entry.identifier) }/>
           </div>
-          <Link to={ `/n/${ entry.id }` } className="text-decoration-none flex-fill text-dark">
+          <Link to={ `/n/${ entry.identifier }` } className="text-decoration-none flex-fill text-dark">
             <div className={ 'd-flex flex-column justify-content-between' }>
               <Card.Title className="m-0 mt-1dot5">{ entry.title }</Card.Title>
               <div>
@@ -44,12 +52,12 @@ export const HistoryCard: React.FC<HistoryEntryProps> = ({ entry, onPinClick, on
           </Link>
           <div className={ 'd-flex flex-column' }>
             <EntryMenu
-              id={ entry.id }
+              id={ entry.identifier }
               title={ entry.title }
-              location={ entry.location }
+              origin={ entry.origin }
               isDark={ false }
-              onRemove={ () => onRemoveClick(entry.id, entry.location) }
-              onDelete={ () => onDeleteClick(entry.id, entry.location) }
+              onRemove={ onRemove }
+              onDelete={ onDelete }
             />
           </div>
         </Card.Body>
diff --git a/src/components/history-page/history-content/history-content.tsx b/src/components/history-page/history-content/history-content.tsx
index d5623bc95..845a79aa3 100644
--- a/src/components/history-page/history-content/history-content.tsx
+++ b/src/components/history-page/history-content/history-content.tsx
@@ -4,46 +4,67 @@
  SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import React, { Fragment, useState } from 'react'
+import React, { Fragment, useCallback, useState } from 'react'
 import { Alert, Row } from 'react-bootstrap'
 import { Trans, useTranslation } from 'react-i18next'
 import { PagerPagination } from '../../common/pagination/pager-pagination'
 import { HistoryCardList } from '../history-card/history-card-list'
-import { HistoryEntryOrigin, LocatedHistoryEntry } from '../history-page'
 import { HistoryTable } from '../history-table/history-table'
 import { ViewStateEnum } from '../history-toolbar/history-toolbar'
+import { HistoryEntry } from '../../../redux/history/types'
+import { removeHistoryEntry, toggleHistoryEntryPinning } from '../../../redux/history/methods'
+import { deleteNote } from '../../../api/notes'
+import { showErrorNotification } from '../../../redux/ui-notifications/methods'
 
-type OnEntryClick = (entryId: string, location: HistoryEntryOrigin) => void
+type OnEntryClick = (entryId: string) => void
+
+export interface HistoryEventHandlers {
+  onPinClick: OnEntryClick
+  onRemoveClick: OnEntryClick
+  onDeleteClick: OnEntryClick
+}
 
 export interface HistoryContentProps {
   viewState: ViewStateEnum
-  entries: LocatedHistoryEntry[]
-  onPinClick: OnEntryClick
-  onRemoveClick: OnEntryClick
-  onDeleteClick: OnEntryClick
+  entries: HistoryEntry[]
 }
 
 export interface HistoryEntryProps {
-  entry: LocatedHistoryEntry,
-  onPinClick: OnEntryClick
-  onRemoveClick: OnEntryClick
-  onDeleteClick: OnEntryClick
+  entry: HistoryEntry,
 }
 
 export interface HistoryEntriesProps {
-  entries: LocatedHistoryEntry[]
-  onPinClick: OnEntryClick
-  onRemoveClick: OnEntryClick
-  onDeleteClick: OnEntryClick
+  entries: HistoryEntry[]
   pageIndex: number
   onLastPageIndexChange: (lastPageIndex: number) => void
 }
 
-export const HistoryContent: React.FC<HistoryContentProps> = ({ viewState, entries, onPinClick, onRemoveClick, onDeleteClick }) => {
-  useTranslation()
+export const HistoryContent: React.FC<HistoryContentProps> = ({ viewState, entries }) => {
+  const { t } = useTranslation()
+
   const [pageIndex, setPageIndex] = useState(0)
   const [lastPageIndex, setLastPageIndex] = useState(0)
 
+  const onPinClick = useCallback((noteId: string) => {
+    toggleHistoryEntryPinning(noteId).catch(
+      showErrorNotification(t('landing.history.error.updateEntry.text'))
+    )
+  }, [t])
+
+  const onDeleteClick = useCallback((noteId: string) => {
+    deleteNote(noteId).then(() => {
+      return removeHistoryEntry(noteId)
+    }).catch(
+      showErrorNotification(t('landing.history.error.deleteNote.text'))
+    )
+  }, [t])
+
+  const onRemoveClick = useCallback((noteId: string) => {
+    removeHistoryEntry(noteId).catch(
+      showErrorNotification(t('landing.history.error.deleteEntry.text'))
+    )
+  }, [t])
+
   if (entries.length === 0) {
     return (
       <Row className={ 'justify-content-center' }>
diff --git a/src/components/history-page/history-page.tsx b/src/components/history-page/history-page.tsx
index abdb7d680..1583ae609 100644
--- a/src/components/history-page/history-page.tsx
+++ b/src/components/history-page/history-page.tsx
@@ -4,226 +4,48 @@
  SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import React, { Fragment, useCallback, useEffect, useMemo, useState } from 'react'
+import React, { Fragment, useEffect, useMemo, useState } from 'react'
 import { Row } from 'react-bootstrap'
 import { Trans, useTranslation } from 'react-i18next'
 import { useSelector } from 'react-redux'
-import { deleteHistory, deleteHistoryEntry, getHistory, setHistory, updateHistoryEntry } from '../../api/history'
-import { deleteNote } from '../../api/notes'
 import { ApplicationState } from '../../redux'
-import { download } from '../common/download/download'
-import { ErrorModal } from '../common/modals/error-modal'
 import { HistoryContent } from './history-content/history-content'
 import { HistoryToolbar, HistoryToolbarState, initState as toolbarInitState } from './history-toolbar/history-toolbar'
-
-import {
-  collectEntries,
-  loadHistoryFromLocalStore,
-  mergeEntryArrays,
-  setHistoryToLocalStore,
-  sortAndFilterEntries
-} from './utils'
-
-export interface HistoryEntry {
-  id: string,
-  title: string,
-  lastVisited: string,
-  tags: string[],
-  pinned: boolean
-}
-
-export interface HistoryJson {
-  version: number,
-  entries: HistoryEntry[]
-}
-
-export type LocatedHistoryEntry = HistoryEntry & HistoryEntryLocation
-
-export interface HistoryEntryLocation {
-  location: HistoryEntryOrigin
-}
-
-export enum HistoryEntryOrigin {
-  LOCAL = 'local',
-  REMOTE = 'remote'
-}
+import { sortAndFilterEntries } from './utils'
+import { refreshHistoryState } from '../../redux/history/methods'
+import { HistoryEntry } from '../../redux/history/types'
+import { showErrorNotification } from '../../redux/ui-notifications/methods'
 
 export const HistoryPage: React.FC = () => {
-  useTranslation()
-  const [localHistoryEntries, setLocalHistoryEntries] = useState<HistoryEntry[]>(loadHistoryFromLocalStore)
-  const [remoteHistoryEntries, setRemoteHistoryEntries] = useState<HistoryEntry[]>([])
+  const { t } = useTranslation()
+
+  const allEntries = useSelector((state: ApplicationState) => state.history)
   const [toolbarState, setToolbarState] = useState<HistoryToolbarState>(toolbarInitState)
-  const userExists = useSelector((state: ApplicationState) => !!state.user)
-  const [error, setError] = useState('')
 
-  const historyWrite = useCallback((entries: HistoryEntry[]) => {
-    if (!entries) {
-      return
-    }
-    setHistoryToLocalStore(entries)
-  }, [])
-
-  useEffect(() => {
-    historyWrite(localHistoryEntries)
-  }, [historyWrite, localHistoryEntries])
-
-  const importHistory = useCallback((entries: HistoryEntry[]): void => {
-    if (userExists) {
-      setHistory(entries)
-        .then(() => setRemoteHistoryEntries(entries))
-        .catch(() => setError('setHistory'))
-    } else {
-      setLocalHistoryEntries(entries)
-    }
-  }, [userExists])
-
-  const refreshHistory = useCallback(() => {
-    const localHistory = loadHistoryFromLocalStore()
-    setLocalHistoryEntries(localHistory)
-    if (userExists) {
-      getHistory()
-        .then((remoteHistory) => setRemoteHistoryEntries(remoteHistory))
-        .catch(() => setError('getHistory'))
-    }
-  }, [userExists])
-
-  useEffect(() => {
-    refreshHistory()
-  }, [refreshHistory])
-
-  const exportHistory = useCallback(() => {
-    const dataObject: HistoryJson = {
-      version: 2,
-      entries: mergeEntryArrays(localHistoryEntries, remoteHistoryEntries)
-    }
-    download(JSON.stringify(dataObject), `history_${ (new Date()).getTime() }.json`, 'application/json')
-  }, [localHistoryEntries, remoteHistoryEntries])
-
-  const clearHistory = useCallback(() => {
-    setLocalHistoryEntries([])
-    if (userExists) {
-      deleteHistory()
-        .then(() => setRemoteHistoryEntries([]))
-        .catch(() => setError('deleteHistory'))
-    }
-    historyWrite([])
-  }, [historyWrite, userExists])
-
-  const uploadAll = useCallback((): void => {
-    const newHistory = mergeEntryArrays(localHistoryEntries, remoteHistoryEntries)
-    if (userExists) {
-      setHistory(newHistory)
-        .then(() => {
-          setRemoteHistoryEntries(newHistory)
-          setLocalHistoryEntries([])
-          historyWrite([])
-        })
-        .catch(() => setError('setHistory'))
-    }
-  }, [historyWrite, localHistoryEntries, remoteHistoryEntries, userExists])
-
-  const removeFromHistoryClick = useCallback((entryId: string, location: HistoryEntryOrigin): void => {
-    if (location === HistoryEntryOrigin.LOCAL) {
-      setLocalHistoryEntries((entries) => entries.filter(entry => entry.id !== entryId))
-    } else if (location === HistoryEntryOrigin.REMOTE) {
-      deleteHistoryEntry(entryId)
-        .then(() => setRemoteHistoryEntries((entries) => entries.filter(entry => entry.id !== entryId)))
-        .catch(() => setError('deleteEntry'))
-    }
-  }, [])
-
-  const deleteNoteClick = useCallback((entryId: string, location: HistoryEntryOrigin): void => {
-    if (userExists) {
-      deleteNote(entryId)
-        .then(() => {
-          removeFromHistoryClick(entryId, location)
-        })
-        .catch(() => setError('deleteNote'))
-    }
-  }, [userExists, removeFromHistoryClick])
-
-  const pinClick = useCallback((entryId: string, location: HistoryEntryOrigin): void => {
-    if (location === HistoryEntryOrigin.LOCAL) {
-      setLocalHistoryEntries((entries) => {
-        return entries.map((entry) => {
-          if (entry.id === entryId) {
-            entry.pinned = !entry.pinned
-          }
-          return entry
-        })
-      })
-    } else if (location === HistoryEntryOrigin.REMOTE) {
-      const foundEntry = remoteHistoryEntries.find(entry => entry.id === entryId)
-      if (!foundEntry) {
-        setError('notFoundEntry')
-        return
-      }
-      const changedEntry = {
-        ...foundEntry,
-        pinned: !foundEntry.pinned
-      }
-      updateHistoryEntry(entryId, changedEntry)
-        .then(() => setRemoteHistoryEntries((entries) => (
-            entries.map((entry) => {
-              if (entry.id === entryId) {
-                entry.pinned = !entry.pinned
-              }
-              return entry
-            })
-          )
-        ))
-        .catch(() => setError('updateEntry'))
-    }
-  }, [remoteHistoryEntries])
-
-  const resetError = () => {
-    setError('')
-  }
-
-  const allEntries = useMemo(() => {
-    return collectEntries(localHistoryEntries, remoteHistoryEntries)
-  }, [localHistoryEntries, remoteHistoryEntries])
-
-  const tags = useMemo<string[]>(() => {
-    return allEntries.map(entry => entry.tags)
-                     .reduce((a, b) => ([...a, ...b]), [])
-                     .filter((value, index, array) => {
-                       if (index === 0) {
-                         return true
-                       }
-                       return (value !== array[index - 1])
-                     })
-  }, [allEntries])
-
-  const entriesToShow = useMemo<LocatedHistoryEntry[]>(() =>
+  const entriesToShow = useMemo<HistoryEntry[]>(() =>
       sortAndFilterEntries(allEntries, toolbarState),
     [allEntries, toolbarState])
 
-  return <Fragment>
-    <ErrorModal show={ error !== '' } onHide={ resetError }
-                titleI18nKey={ error !== '' ? `landing.history.error.${ error }.title` : '' }>
-      <h5>
-        <Trans i18nKey={ error !== '' ? `landing.history.error.${ error }.text` : '' }/>
-      </h5>
-    </ErrorModal>
-    <h1 className="mb-4"><Trans i18nKey="landing.navigation.history"/></h1>
-    <Row className={ 'justify-content-center mt-5 mb-3' }>
-      <HistoryToolbar
-        onSettingsChange={ setToolbarState }
-        tags={ tags }
-        onClearHistory={ clearHistory }
-        onRefreshHistory={ refreshHistory }
-        onExportHistory={ exportHistory }
-        onImportHistory={ importHistory }
-        onUploadAll={ uploadAll }
+  useEffect(() => {
+    refreshHistoryState().catch(
+      showErrorNotification(t('landing.history.error.getHistory.text'))
+    )
+  }, [t])
+
+  return (
+    <Fragment>
+      <h1 className="mb-4">
+        <Trans i18nKey="landing.navigation.history"/>
+      </h1>
+      <Row className={ 'justify-content-center mt-5 mb-3' }>
+        <HistoryToolbar
+          onSettingsChange={ setToolbarState }
+        />
+      </Row>
+      <HistoryContent
+        viewState={ toolbarState.viewState }
+        entries={ entriesToShow }
       />
-    </Row>
-    <HistoryContent
-      viewState={ toolbarState.viewState }
-      entries={ entriesToShow }
-      onPinClick={ pinClick }
-      onRemoveClick={ removeFromHistoryClick }
-      onDeleteClick={ deleteNoteClick }
-    />
-  </Fragment>
+    </Fragment>
+  )
 }
diff --git a/src/components/history-page/history-table/history-table-row.tsx b/src/components/history-page/history-table/history-table-row.tsx
index 511a927d5..e43a5ed97 100644
--- a/src/components/history-page/history-table/history-table-row.tsx
+++ b/src/components/history-page/history-table/history-table-row.tsx
@@ -8,15 +8,15 @@ import React from 'react'
 import { Badge } from 'react-bootstrap'
 import { Link } from 'react-router-dom'
 import { EntryMenu } from '../entry-menu/entry-menu'
-import { HistoryEntryProps } from '../history-content/history-content'
+import { HistoryEntryProps, HistoryEventHandlers } from '../history-content/history-content'
 import { PinButton } from '../pin-button/pin-button'
 import { formatHistoryDate } from '../utils'
 
-export const HistoryTableRow: React.FC<HistoryEntryProps> = ({ entry, onPinClick, onRemoveClick, onDeleteClick }) => {
+export const HistoryTableRow: React.FC<HistoryEntryProps & HistoryEventHandlers> = ({ entry, onPinClick, onRemoveClick, onDeleteClick }) => {
   return (
     <tr>
       <td>
-        <Link to={ `/n/${ entry.id }` } className="text-light">
+        <Link to={ `/n/${ entry.identifier }` } className="text-light">
           { entry.title }
         </Link>
       </td>
@@ -28,15 +28,15 @@ export const HistoryTableRow: React.FC<HistoryEntryProps> = ({ entry, onPinClick
         }
       </td>
       <td>
-        <PinButton isDark={ true } isPinned={ entry.pinned } onPinClick={ () => onPinClick(entry.id, entry.location) }
+        <PinButton isDark={ true } isPinned={ entry.pinStatus } onPinClick={ () => onPinClick(entry.identifier) }
                    className={ 'mb-1 mr-1' }/>
         <EntryMenu
-          id={ entry.id }
+          id={ entry.identifier }
           title={ entry.title }
-          location={ entry.location }
+          origin={ entry.origin }
           isDark={ true }
-          onRemove={ () => onRemoveClick(entry.id, entry.location) }
-          onDelete={ () => onDeleteClick(entry.id, entry.location) }
+          onRemove={ () => onRemoveClick(entry.identifier) }
+          onDelete={ () => onDeleteClick(entry.identifier) }
         />
       </td>
     </tr>
diff --git a/src/components/history-page/history-table/history-table.tsx b/src/components/history-page/history-table/history-table.tsx
index d654cbe6f..6da7a8ec6 100644
--- a/src/components/history-page/history-table/history-table.tsx
+++ b/src/components/history-page/history-table/history-table.tsx
@@ -8,11 +8,11 @@ import React from 'react'
 import { Table } from 'react-bootstrap'
 import { Trans, useTranslation } from 'react-i18next'
 import { Pager } from '../../common/pagination/pager'
-import { HistoryEntriesProps } from '../history-content/history-content'
+import { HistoryEntriesProps, HistoryEventHandlers } from '../history-content/history-content'
 import { HistoryTableRow } from './history-table-row'
 import './history-table.scss'
 
-export const HistoryTable: React.FC<HistoryEntriesProps> = ({ entries, onPinClick, onRemoveClick, onDeleteClick, pageIndex, onLastPageIndexChange }) => {
+export const HistoryTable: React.FC<HistoryEntriesProps & HistoryEventHandlers> = ({ entries, onPinClick, onRemoveClick, onDeleteClick, pageIndex, onLastPageIndexChange }) => {
   useTranslation()
   return (
     <Table striped bordered hover size="sm" variant="dark" className={ 'history-table' }>
@@ -29,7 +29,7 @@ export const HistoryTable: React.FC<HistoryEntriesProps> = ({ entries, onPinClic
         {
           entries.map((entry) =>
             <HistoryTableRow
-              key={ entry.id }
+              key={ entry.identifier }
               entry={ entry }
               onPinClick={ onPinClick }
               onRemoveClick={ onRemoveClick }
diff --git a/src/components/history-page/history-toolbar/clear-history-button.tsx b/src/components/history-page/history-toolbar/clear-history-button.tsx
index 250067e0f..35bf9bd71 100644
--- a/src/components/history-page/history-toolbar/clear-history-button.tsx
+++ b/src/components/history-page/history-toolbar/clear-history-button.tsx
@@ -4,33 +4,38 @@
  SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import React, { Fragment, useState } from 'react'
+import React, { Fragment, useCallback, useState } from 'react'
 import { Button } from 'react-bootstrap'
 import { Trans, useTranslation } from 'react-i18next'
 import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
 import { DeletionModal } from '../../common/modals/deletion-modal'
+import { deleteAllHistoryEntries, refreshHistoryState } from '../../../redux/history/methods'
+import { showErrorNotification } from '../../../redux/ui-notifications/methods'
 
-export interface ClearHistoryButtonProps {
-  onClearHistory: () => void
-}
-
-export const ClearHistoryButton: React.FC<ClearHistoryButtonProps> = ({ onClearHistory }) => {
+export const ClearHistoryButton: React.FC = () => {
   const { t } = useTranslation()
   const [show, setShow] = useState(false)
 
   const handleShow = () => setShow(true)
   const handleClose = () => setShow(false)
 
+  const onConfirm = useCallback(() => {
+    deleteAllHistoryEntries().catch(error => {
+      showErrorNotification(t('landing.history.error.deleteEntry.text'))(error)
+      refreshHistoryState().catch(
+        showErrorNotification(t('landing.history.error.getHistory.text'))
+      )
+    })
+    handleClose()
+  }, [t])
+
   return (
     <Fragment>
       <Button variant={ 'light' } title={ t('landing.history.toolbar.clear') } onClick={ handleShow }>
         <ForkAwesomeIcon icon={ 'trash' }/>
       </Button>
       <DeletionModal
-        onConfirm={ () => {
-          onClearHistory()
-          handleClose()
-        } }
+        onConfirm={ onConfirm }
         deletionButtonI18nKey={ 'landing.history.toolbar.clear' }
         show={ show }
         onHide={ handleClose }
diff --git a/src/components/history-page/history-toolbar/export-history-button.tsx b/src/components/history-page/history-toolbar/export-history-button.tsx
index 2f492351c..659c4763c 100644
--- a/src/components/history-page/history-toolbar/export-history-button.tsx
+++ b/src/components/history-page/history-toolbar/export-history-button.tsx
@@ -8,16 +8,13 @@ import React from 'react'
 import { Button } from 'react-bootstrap'
 import { useTranslation } from 'react-i18next'
 import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
+import { downloadHistory } from '../../../redux/history/methods'
 
-export interface ExportHistoryButtonProps {
-  onExportHistory: () => void
-}
-
-export const ExportHistoryButton: React.FC<ExportHistoryButtonProps> = ({ onExportHistory }) => {
+export const ExportHistoryButton: React.FC = () => {
   const { t } = useTranslation()
 
   return (
-    <Button variant={ 'light' } title={ t('landing.history.toolbar.export') } onClick={ onExportHistory }>
+    <Button variant={ 'light' } title={ t('landing.history.toolbar.export') } onClick={ downloadHistory }>
       <ForkAwesomeIcon icon='download'/>
     </Button>
   )
diff --git a/src/components/history-page/history-toolbar/history-toolbar.tsx b/src/components/history-page/history-toolbar/history-toolbar.tsx
index 7b5139b82..9625e9d4f 100644
--- a/src/components/history-page/history-toolbar/history-toolbar.tsx
+++ b/src/components/history-page/history-toolbar/history-toolbar.tsx
@@ -4,7 +4,7 @@
  SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import React, { ChangeEvent, useEffect, useState } from 'react'
+import React, { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'
 import { Button, Form, FormControl, InputGroup, ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
 import { Typeahead } from 'react-bootstrap-typeahead'
 import { Trans, useTranslation } from 'react-i18next'
@@ -12,12 +12,14 @@ import { useSelector } from 'react-redux'
 import { ApplicationState } from '../../../redux'
 import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
 import { ShowIf } from '../../common/show-if/show-if'
-import { HistoryEntry } from '../history-page'
 import { SortButton, SortModeEnum } from '../sort-button/sort-button'
 import { ClearHistoryButton } from './clear-history-button'
 import { ExportHistoryButton } from './export-history-button'
 import { ImportHistoryButton } from './import-history-button'
 import './typeahead-hacks.scss'
+import { HistoryEntryOrigin } from '../../../redux/history/types'
+import { importHistoryEntries, refreshHistoryState, setHistoryEntries } from '../../../redux/history/methods'
+import { showErrorNotification } from '../../../redux/ui-notifications/methods'
 
 export type HistoryToolbarChange = (settings: HistoryToolbarState) => void;
 
@@ -36,12 +38,6 @@ export enum ViewStateEnum {
 
 export interface HistoryToolbarProps {
   onSettingsChange: HistoryToolbarChange
-  tags: string[]
-  onClearHistory: () => void
-  onRefreshHistory: () => void
-  onExportHistory: () => void
-  onImportHistory: (entries: HistoryEntry[]) => void
-  onUploadAll: () => void
 }
 
 export const initState: HistoryToolbarState = {
@@ -52,11 +48,18 @@ export const initState: HistoryToolbarState = {
   selectedTags: []
 }
 
-export const HistoryToolbar: React.FC<HistoryToolbarProps> = ({ onSettingsChange, tags, onClearHistory, onRefreshHistory, onExportHistory, onImportHistory, onUploadAll }) => {
-  const [t] = useTranslation()
+export const HistoryToolbar: React.FC<HistoryToolbarProps> = ({ onSettingsChange }) => {
+  const { t } = useTranslation()
   const [state, setState] = useState<HistoryToolbarState>(initState)
+  const historyEntries = useSelector((state: ApplicationState) => state.history)
   const userExists = useSelector((state: ApplicationState) => !!state.user)
 
+  const tags = useMemo<string[]>(() => {
+    const allTags = historyEntries.map(entry => entry.tags)
+                                  .flat()
+    return [...new Set(allTags)]
+  }, [historyEntries])
+
   const titleSortChanged = (direction: SortModeEnum) => {
     setState(prevState => ({
       ...prevState,
@@ -85,6 +88,33 @@ export const HistoryToolbar: React.FC<HistoryToolbarProps> = ({ onSettingsChange
     setState(prevState => ({ ...prevState, selectedTags: selected }))
   }
 
+  const refreshHistory = useCallback(() => {
+    refreshHistoryState()
+      .catch(
+        showErrorNotification(t('landing.history.error.getHistory.text'))
+      )
+  }, [t])
+
+  const onUploadAllToRemote = useCallback(() => {
+    if (!userExists) {
+      return
+    }
+    const localEntries = historyEntries.filter(entry => entry.origin === HistoryEntryOrigin.LOCAL)
+                                       .map(entry => entry.identifier)
+    historyEntries.forEach(entry => entry.origin = HistoryEntryOrigin.REMOTE)
+    importHistoryEntries(historyEntries)
+      .catch(error => {
+        showErrorNotification(t('landing.history.error.setHistory.text'))(error)
+        historyEntries.forEach(entry => {
+          if (localEntries.includes(entry.identifier)) {
+            entry.origin = HistoryEntryOrigin.LOCAL
+          }
+        })
+        setHistoryEntries(historyEntries)
+        refreshHistory()
+      })
+  }, [userExists, historyEntries, t, refreshHistory])
+
   useEffect(() => {
     onSettingsChange(state)
   }, [onSettingsChange, state])
@@ -113,28 +143,28 @@ export const HistoryToolbar: React.FC<HistoryToolbarProps> = ({ onSettingsChange
                     variant={ 'light' }><Trans i18nKey={ 'landing.history.toolbar.sortByLastVisited' }/></SortButton>
       </InputGroup>
       <InputGroup className={ 'mr-1 mb-1' }>
-        <ExportHistoryButton onExportHistory={ onExportHistory }/>
+        <ExportHistoryButton/>
       </InputGroup>
       <InputGroup className={ 'mr-1 mb-1' }>
-        <ImportHistoryButton onImportHistory={ onImportHistory }/>
+        <ImportHistoryButton/>
       </InputGroup>
       <InputGroup className={ 'mr-1 mb-1' }>
-        <ClearHistoryButton onClearHistory={ onClearHistory }/>
+        <ClearHistoryButton/>
       </InputGroup>
       <InputGroup className={ 'mr-1 mb-1' }>
-        <Button variant={ 'light' } title={ t('landing.history.toolbar.refresh') } onClick={ onRefreshHistory }>
-          <ForkAwesomeIcon icon='refresh'/>
+        <Button variant={ 'light' } title={ t('landing.history.toolbar.refresh') } onClick={ refreshHistory }>
+          <ForkAwesomeIcon icon="refresh"/>
         </Button>
       </InputGroup>
       <ShowIf condition={ userExists }>
         <InputGroup className={ 'mr-1 mb-1' }>
-          <Button variant={ 'light' } title={ t('landing.history.toolbar.uploadAll') } onClick={ onUploadAll }>
-            <ForkAwesomeIcon icon='cloud-upload'/>
+          <Button variant={ 'light' } title={ t('landing.history.toolbar.uploadAll') } onClick={ onUploadAllToRemote }>
+            <ForkAwesomeIcon icon="cloud-upload"/>
           </Button>
         </InputGroup>
       </ShowIf>
       <InputGroup className={ 'mr-1 mb-1' }>
-        <ToggleButtonGroup type="radio" name="options" dir='ltr' value={ state.viewState } className={ 'button-height' }
+        <ToggleButtonGroup type="radio" name="options" dir="ltr" value={ state.viewState } className={ 'button-height' }
                            onChange={ (newViewState: ViewStateEnum) => {
                              toggleViewChanged(newViewState)
                            } }>
diff --git a/src/components/history-page/history-toolbar/import-history-button.tsx b/src/components/history-page/history-toolbar/import-history-button.tsx
index e59d353aa..14fa09d9b 100644
--- a/src/components/history-page/history-toolbar/import-history-button.tsx
+++ b/src/components/history-page/history-toolbar/import-history-button.tsx
@@ -4,34 +4,50 @@
  SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import React, { useRef, useState } from 'react'
+import React, { useCallback, useRef, useState } from 'react'
 import { Button } from 'react-bootstrap'
 import { Trans, useTranslation } from 'react-i18next'
 import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
 import { ErrorModal } from '../../common/modals/error-modal'
-import { HistoryEntry, HistoryJson } from '../history-page'
-import { convertV1History, V1HistoryEntry } from '../utils'
+import { HistoryEntry, HistoryEntryOrigin, HistoryExportJson, V1HistoryEntry } from '../../../redux/history/types'
+import {
+  convertV1History,
+  importHistoryEntries,
+  mergeHistoryEntries,
+  refreshHistoryState
+} from '../../../redux/history/methods'
+import { ApplicationState } from '../../../redux'
+import { useSelector } from 'react-redux'
+import { showErrorNotification } from '../../../redux/ui-notifications/methods'
 
-export interface ImportHistoryButtonProps {
-  onImportHistory: (entries: HistoryEntry[]) => void
-}
-
-export const ImportHistoryButton: React.FC<ImportHistoryButtonProps> = ({ onImportHistory }) => {
+export const ImportHistoryButton: React.FC = () => {
   const { t } = useTranslation()
+  const userExists = useSelector((state: ApplicationState) => !!state.user)
+  const historyState = useSelector((state: ApplicationState) => state.history)
   const uploadInput = useRef<HTMLInputElement>(null)
   const [show, setShow] = useState(false)
   const [fileName, setFilename] = useState('')
   const [i18nKey, setI18nKey] = useState('')
 
-  const handleShow = (key: string) => {
+  const handleShow = useCallback((key: string) => {
     setI18nKey(key)
     setShow(true)
-  }
+  }, [])
 
-  const handleClose = () => {
+  const handleClose = useCallback(() => {
     setI18nKey('')
     setShow(false)
-  }
+  }, [])
+
+  const onImportHistory = useCallback((entries: HistoryEntry[]): void => {
+    entries.forEach(entry => entry.origin = userExists ? HistoryEntryOrigin.REMOTE : HistoryEntryOrigin.LOCAL)
+    importHistoryEntries(mergeHistoryEntries(historyState, entries)).catch(error => {
+      showErrorNotification(t('landing.history.error.setHistory.text'))(error)
+      refreshHistoryState().catch(
+        showErrorNotification(t('landing.history.error.getHistory.text'))
+      )
+    })
+  }, [historyState, userExists, t])
 
   const handleUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
     const { validity, files } = event.target
@@ -47,7 +63,7 @@ export const ImportHistoryButton: React.FC<ImportHistoryButtonProps> = ({ onImpo
         if (event.target && event.target.result) {
           try {
             const result = event.target.result as string
-            const data = JSON.parse(result) as HistoryJson
+            const data = JSON.parse(result) as HistoryExportJson
             if (data) {
               if (data.version) {
                 if (data.version === 2) {
diff --git a/src/components/history-page/utils.ts b/src/components/history-page/utils.ts
index 740271cf4..33cc69e00 100644
--- a/src/components/history-page/utils.ts
+++ b/src/components/history-page/utils.ts
@@ -6,47 +6,25 @@
 
 import { DateTime } from 'luxon'
 import { SortModeEnum } from './sort-button/sort-button'
-import { HistoryEntry, HistoryEntryOrigin, LocatedHistoryEntry } from './history-page'
 import { HistoryToolbarState } from './history-toolbar/history-toolbar'
+import { HistoryEntry } from '../../redux/history/types'
 
-export function collectEntries(localEntries: HistoryEntry[], remoteEntries: HistoryEntry[]): LocatedHistoryEntry[] {
-  const locatedLocalEntries = locateEntries(localEntries, HistoryEntryOrigin.LOCAL)
-  const locatedRemoteEntries = locateEntries(remoteEntries, HistoryEntryOrigin.REMOTE)
-  return mergeEntryArrays(locatedLocalEntries, locatedRemoteEntries)
-}
+export const formatHistoryDate = (date: string): string => DateTime.fromISO(date).toFormat('DDDD T')
 
-export function sortAndFilterEntries(entries: LocatedHistoryEntry[], toolbarState: HistoryToolbarState): LocatedHistoryEntry[] {
+export const sortAndFilterEntries = (entries: HistoryEntry[], toolbarState: HistoryToolbarState): HistoryEntry[] => {
   const filteredBySelectedTagsEntries = filterBySelectedTags(entries, toolbarState.selectedTags)
   const filteredByKeywordSearchEntries = filterByKeywordSearch(filteredBySelectedTagsEntries, toolbarState.keywordSearch)
   return sortEntries(filteredByKeywordSearchEntries, toolbarState)
 }
 
-function locateEntries(entries: HistoryEntry[], location: HistoryEntryOrigin): LocatedHistoryEntry[] {
-  return entries.map(entry => {
-    return {
-      ...entry,
-      location: location
-    }
-  })
-}
-
-export function mergeEntryArrays<T extends HistoryEntry>(localEntries: T[], remoteEntries: T[]): T[] {
-  const filteredLocalEntries = localEntries.filter(localEntry => {
-    const entry = remoteEntries.find(remoteEntry => remoteEntry.id === localEntry.id)
-    return !entry
-  })
-
-  return filteredLocalEntries.concat(remoteEntries)
-}
-
-function filterBySelectedTags(entries: LocatedHistoryEntry[], selectedTags: string[]): LocatedHistoryEntry[] {
+const filterBySelectedTags = (entries: HistoryEntry[], selectedTags: string[]): HistoryEntry[] => {
   return entries.filter(entry => {
       return (selectedTags.length === 0 || arrayCommonCheck(entry.tags, selectedTags))
     }
   )
 }
 
-function arrayCommonCheck<T>(array1: T[], array2: T[]): boolean {
+const arrayCommonCheck = <T> (array1: T[], array2: T[]): boolean => {
   const foundElement = array1.find((element1) =>
     array2.find((element2) =>
       element2 === element1
@@ -55,18 +33,21 @@ function arrayCommonCheck<T>(array1: T[], array2: T[]): boolean {
   return !!foundElement
 }
 
-function filterByKeywordSearch(entries: LocatedHistoryEntry[], keywords: string): LocatedHistoryEntry[] {
+const filterByKeywordSearch = (entries: HistoryEntry[], keywords: string): HistoryEntry[] => {
   const searchTerm = keywords.toLowerCase()
-  return entries.filter(entry => entry.title.toLowerCase()
-                                      .includes(searchTerm))
+  return entries.filter(
+    entry => entry.title
+                  .toLowerCase()
+                  .includes(searchTerm)
+  )
 }
 
-function sortEntries(entries: LocatedHistoryEntry[], viewState: HistoryToolbarState): LocatedHistoryEntry[] {
+const sortEntries = (entries: HistoryEntry[], viewState: HistoryToolbarState): HistoryEntry[] => {
   return entries.sort((firstEntry, secondEntry) => {
-    if (firstEntry.pinned && !secondEntry.pinned) {
+    if (firstEntry.pinStatus && !secondEntry.pinStatus) {
       return -1
     }
-    if (!firstEntry.pinned && secondEntry.pinned) {
+    if (!firstEntry.pinStatus && secondEntry.pinStatus) {
       return 1
     }
 
@@ -86,47 +67,3 @@ function sortEntries(entries: LocatedHistoryEntry[], viewState: HistoryToolbarSt
     return 0
   })
 }
-
-export function formatHistoryDate(date: string): string {
-  return DateTime.fromISO(date)
-                 .toFormat('DDDD T')
-}
-
-export interface V1HistoryEntry {
-  id: string;
-  text: string;
-  time: number;
-  tags: string[];
-  pinned: boolean;
-}
-
-export function convertV1History(oldHistory: V1HistoryEntry[]): HistoryEntry[] {
-  return oldHistory.map((entry: V1HistoryEntry) => {
-    return {
-      id: entry.id,
-      title: entry.text,
-      lastVisited: DateTime.fromMillis(entry.time)
-                           .toISO(),
-      tags: entry.tags,
-      pinned: entry.pinned
-    }
-  })
-}
-
-export function loadHistoryFromLocalStore(): HistoryEntry[] {
-  const historyJsonString = window.localStorage.getItem('history')
-
-  if (!historyJsonString) {
-    // if localStorage["history"] is empty we check the old localStorage["notehistory"]
-    // and convert it to the new format
-    const oldHistoryJsonString = window.localStorage.getItem('notehistory')
-    const oldHistory = oldHistoryJsonString ? JSON.parse(JSON.parse(oldHistoryJsonString)) as V1HistoryEntry[] : []
-    return convertV1History(oldHistory)
-  } else {
-    return JSON.parse(historyJsonString) as HistoryEntry[]
-  }
-}
-
-export function setHistoryToLocalStore(entries: HistoryEntry[]): void {
-  window.localStorage.setItem('history', JSON.stringify(entries))
-}
diff --git a/src/redux/history/methods.ts b/src/redux/history/methods.ts
new file mode 100644
index 000000000..4d51f2160
--- /dev/null
+++ b/src/redux/history/methods.ts
@@ -0,0 +1,197 @@
+/*
+ * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { store } from '../index'
+import {
+  HistoryActionType,
+  HistoryEntry,
+  HistoryEntryOrigin,
+  HistoryExportJson,
+  RemoveEntryAction,
+  SetEntriesAction,
+  UpdateEntryAction,
+  V1HistoryEntry
+} from './types'
+import { download } from '../../components/common/download/download'
+import { DateTime } from 'luxon'
+import {
+  deleteHistory,
+  deleteHistoryEntry,
+  getHistory,
+  postHistory,
+  updateHistoryEntryPinStatus
+} from '../../api/history'
+import {
+  historyEntryDtoToHistoryEntry,
+  historyEntryToHistoryEntryPutDto,
+  historyEntryToHistoryEntryUpdateDto
+} from '../../api/history/dto-methods'
+
+export const setHistoryEntries = (entries: HistoryEntry[]): void => {
+  store.dispatch({
+    type: HistoryActionType.SET_ENTRIES,
+    entries
+  } as SetEntriesAction)
+  storeLocalHistory()
+}
+
+export const importHistoryEntries = (entries: HistoryEntry[]): Promise<void> => {
+  setHistoryEntries(entries)
+  return storeRemoteHistory()
+}
+
+export const deleteAllHistoryEntries = (): Promise<void> => {
+  store.dispatch({
+    type: HistoryActionType.SET_ENTRIES,
+    entries: []
+  })
+  storeLocalHistory()
+  return deleteHistory()
+}
+
+export const updateHistoryEntryRedux = (noteId: string, newEntry: HistoryEntry): void => {
+  store.dispatch({
+    type: HistoryActionType.UPDATE_ENTRY,
+    noteId,
+    newEntry
+  } as UpdateEntryAction)
+}
+
+export const updateLocalHistoryEntry = (noteId: string, newEntry: HistoryEntry): void => {
+  updateHistoryEntryRedux(noteId, newEntry)
+  storeLocalHistory()
+}
+
+export const removeHistoryEntry = async (noteId: string): Promise<void> => {
+  const entryToDelete = store.getState().history.find(entry => entry.identifier === noteId)
+  if (entryToDelete && entryToDelete.origin === HistoryEntryOrigin.REMOTE) {
+    await deleteHistoryEntry(noteId)
+  }
+  store.dispatch({
+    type: HistoryActionType.REMOVE_ENTRY,
+    noteId
+  } as RemoveEntryAction)
+  storeLocalHistory()
+}
+
+export const toggleHistoryEntryPinning = async (noteId: string): Promise<void> => {
+  const state = store.getState().history
+  const entryToUpdate = state.find(entry => entry.identifier === noteId)
+  if (!entryToUpdate) {
+    return Promise.reject(`History entry for note '${ noteId }' not found`)
+  }
+  if (entryToUpdate.pinStatus === undefined) {
+    entryToUpdate.pinStatus = false
+  }
+  entryToUpdate.pinStatus = !entryToUpdate.pinStatus
+  if (entryToUpdate.origin === HistoryEntryOrigin.LOCAL) {
+    updateLocalHistoryEntry(noteId, entryToUpdate)
+  } else {
+    const historyUpdateDto = historyEntryToHistoryEntryUpdateDto(entryToUpdate)
+    updateHistoryEntryRedux(noteId, entryToUpdate)
+    await updateHistoryEntryPinStatus(noteId, historyUpdateDto)
+  }
+}
+
+export const downloadHistory = (): void => {
+  const history = store.getState().history
+  history.forEach((entry: Partial<HistoryEntry>) => {
+    delete entry.origin
+  })
+  const json = JSON.stringify({
+    version: 2,
+    entries: history
+  } as HistoryExportJson)
+  download(json, `history_${ Date.now() }.json`, 'application/json')
+}
+
+export const mergeHistoryEntries = (a: HistoryEntry[], b: HistoryEntry[]): HistoryEntry[] => {
+  const noDuplicates = a.filter(entryA => !b.some(entryB => entryA.identifier === entryB.identifier))
+  return noDuplicates.concat(b)
+}
+
+export const convertV1History = (oldHistory: V1HistoryEntry[]): HistoryEntry[] => {
+  return oldHistory.map(entry => ({
+    identifier: entry.id,
+    title: entry.text,
+    tags: entry.tags,
+    lastVisited: DateTime.fromMillis(entry.time)
+                         .toISO(),
+    pinStatus: entry.pinned,
+    origin: HistoryEntryOrigin.LOCAL
+  }))
+}
+
+export const refreshHistoryState = async (): Promise<void> => {
+  const localEntries = loadLocalHistory()
+  if (!store.getState().user) {
+    setHistoryEntries(localEntries)
+    return
+  }
+  const remoteEntries = await loadRemoteHistory()
+  const allEntries = mergeHistoryEntries(localEntries, remoteEntries)
+  setHistoryEntries(allEntries)
+}
+
+export const storeLocalHistory = (): void => {
+  const history = store.getState().history
+  const localEntries = history.filter(entry => entry.origin === HistoryEntryOrigin.LOCAL)
+  const entriesWithoutOrigin = localEntries.map(entry => ({
+    ...entry,
+    origin: undefined
+  }))
+  window.localStorage.setItem('history', JSON.stringify(entriesWithoutOrigin))
+}
+
+export const storeRemoteHistory = (): Promise<void> => {
+  if (!store.getState().user) {
+    return Promise.resolve()
+  }
+  const history = store.getState().history
+  const remoteEntries = history.filter(entry => entry.origin === HistoryEntryOrigin.REMOTE)
+  const remoteEntryDtos = remoteEntries.map(historyEntryToHistoryEntryPutDto)
+  return postHistory(remoteEntryDtos)
+}
+
+const loadLocalHistory = (): HistoryEntry[] => {
+  const localV1Json = window.localStorage.getItem('notehistory')
+  if (localV1Json) {
+    try {
+      const localV1History = JSON.parse(JSON.parse(localV1Json)) as V1HistoryEntry[]
+      window.localStorage.removeItem('notehistory')
+      return convertV1History(localV1History)
+    } catch (error) {
+      console.error(`Error converting old history entries: ${ String(error) }`)
+      return []
+    }
+  }
+
+  const localJson = window.localStorage.getItem('history')
+  if (!localJson) {
+    return []
+  }
+
+  try {
+    const localHistory = JSON.parse(localJson) as HistoryEntry[]
+    localHistory.forEach(entry => {
+      entry.origin = HistoryEntryOrigin.LOCAL
+    })
+    return localHistory
+  } catch (error) {
+    console.error(`Error parsing local stored history entries: ${ String(error) }`)
+    return []
+  }
+}
+
+const loadRemoteHistory = async (): Promise<HistoryEntry[]> => {
+  try {
+    const remoteHistory = await getHistory()
+    return remoteHistory.map(historyEntryDtoToHistoryEntry)
+  } catch (error) {
+    console.error(`Error fetching history entries from server: ${ String(error) }`)
+    return []
+  }
+}
diff --git a/src/redux/history/reducers.ts b/src/redux/history/reducers.ts
new file mode 100644
index 000000000..1786a01f8
--- /dev/null
+++ b/src/redux/history/reducers.ts
@@ -0,0 +1,35 @@
+/*
+ * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Reducer } from 'redux'
+import {
+  HistoryAction,
+  HistoryActionType,
+  HistoryEntry,
+  RemoveEntryAction,
+  SetEntriesAction,
+  UpdateEntryAction
+} from './types'
+
+// Q: Why is the reducer initialized with an empty array instead of the actual history entries like in the config reducer?
+// A: The history reducer will be created without entries because of async entry retrieval.
+//    Entries will be added after reducer initialization.
+
+export const HistoryReducer: Reducer<HistoryEntry[], HistoryAction> = (state: HistoryEntry[] = [], action: HistoryAction) => {
+  switch (action.type) {
+    case HistoryActionType.SET_ENTRIES:
+      return (action as SetEntriesAction).entries
+    case HistoryActionType.UPDATE_ENTRY:
+      return [
+        ...state.filter(entry => entry.identifier !== (action as UpdateEntryAction).noteId),
+        (action as UpdateEntryAction).newEntry
+      ]
+    case HistoryActionType.REMOVE_ENTRY:
+      return state.filter(entry => entry.identifier !== (action as RemoveEntryAction).noteId)
+    default:
+      return state
+  }
+}
diff --git a/src/redux/history/types.ts b/src/redux/history/types.ts
new file mode 100644
index 000000000..3dc04d809
--- /dev/null
+++ b/src/redux/history/types.ts
@@ -0,0 +1,66 @@
+/*
+ * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Action } from 'redux'
+
+export enum HistoryEntryOrigin {
+  LOCAL,
+  REMOTE
+}
+
+export interface HistoryEntry {
+  identifier: string
+  title: string
+  lastVisited: string
+  tags: string[]
+  pinStatus: boolean
+  origin: HistoryEntryOrigin
+}
+
+export interface V1HistoryEntry {
+  id: string
+  text: string
+  time: number
+  tags: string[]
+  pinned: boolean
+}
+
+export interface HistoryExportJson {
+  version: number,
+  entries: HistoryEntry[]
+}
+
+export enum HistoryActionType {
+  SET_ENTRIES = 'SET_ENTRIES',
+  ADD_ENTRY = 'ADD_ENTRY',
+  UPDATE_ENTRY = 'UPDATE_ENTRY',
+  REMOVE_ENTRY = 'REMOVE_ENTRY'
+}
+
+export interface HistoryAction extends Action<HistoryActionType> {
+  type: HistoryActionType
+}
+
+export interface SetEntriesAction extends HistoryAction {
+  type: HistoryActionType.SET_ENTRIES
+  entries: HistoryEntry[]
+}
+
+export interface AddEntryAction extends HistoryAction {
+  type: HistoryActionType.ADD_ENTRY
+  newEntry: HistoryEntry
+}
+
+export interface UpdateEntryAction extends HistoryAction {
+  type: HistoryActionType.UPDATE_ENTRY
+  noteId: string
+  newEntry: HistoryEntry
+}
+
+export interface RemoveEntryAction extends HistoryEntry {
+  type: HistoryActionType.REMOVE_ENTRY
+  noteId: string
+}
diff --git a/src/redux/index.ts b/src/redux/index.ts
index 3bcd38eed..823e4caf2 100644
--- a/src/redux/index.ts
+++ b/src/redux/index.ts
@@ -21,11 +21,14 @@ import { UserReducer } from './user/reducers'
 import { MaybeUserState } from './user/types'
 import { UiNotificationState } from './ui-notifications/types'
 import { UiNotificationReducer } from './ui-notifications/reducers'
+import { HistoryEntry } from './history/types'
+import { HistoryReducer } from './history/reducers'
 
 export interface ApplicationState {
   user: MaybeUserState;
   config: Config;
   banner: BannerState;
+  history: HistoryEntry[];
   apiUrl: ApiUrlObject;
   editorConfig: EditorConfig;
   darkMode: DarkModeConfig;
@@ -38,6 +41,7 @@ export const allReducers: Reducer<ApplicationState> = combineReducers<Applicatio
   config: ConfigReducer,
   banner: BannerReducer,
   apiUrl: ApiUrlReducer,
+  history: HistoryReducer,
   editorConfig: EditorConfigReducer,
   darkMode: DarkModeConfigReducer,
   noteDetails: NoteDetailsReducer,
diff --git a/src/redux/ui-notifications/methods.ts b/src/redux/ui-notifications/methods.ts
index 6e2cbf2d7..8979df41b 100644
--- a/src/redux/ui-notifications/methods.ts
+++ b/src/redux/ui-notifications/methods.ts
@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import i18n from 'i18next'
 import { store } from '../index'
 import {
   DismissUiNotificationAction,
@@ -37,3 +38,10 @@ export const dismissUiNotification = (notificationId: number): void => {
     notificationId
   } as DismissUiNotificationAction)
 }
+
+// Promises catch errors as any.
+// eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/explicit-module-boundary-types
+export const showErrorNotification = (message: string) => (error: any): void => {
+  console.error(message, error)
+  dispatchUiNotification(i18n.t('common.errorOccurred'), message, DEFAULT_DURATION_IN_SECONDS, 'exclamation-triangle')
+}
diff --git a/tsconfig.json b/tsconfig.json
index 27186b9da..5027a9a9c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
 {
     "compilerOptions": {
-        "target": "es5",
+        "target": "es6",
         "lib": [
             "dom",
             "dom.iterable",