diff --git a/src/components/interactions/interactions.js b/src/components/interactions/interactions.js
index c5ceb63d..95087eac 100644
--- a/src/components/interactions/interactions.js
+++ b/src/components/interactions/interactions.js
@@ -5,6 +5,8 @@ const tabModeDict = {
   mentions: ['mention'],
   'likes+repeats': ['repeat', 'like'],
   follows: ['follow'],
+  reactions: ['pleroma:emoji_reaction'],
+  reports: ['pleroma:report'],
   moves: ['move']
 }
 
@@ -12,7 +14,8 @@ const Interactions = {
   data () {
     return {
       allowFollowingMove: this.$store.state.users.currentUser.allow_following_move,
-      filterMode: tabModeDict['mentions']
+      filterMode: tabModeDict['mentions'],
+      canSeeReports: ['moderator', 'admin'].includes(this.$store.state.users.currentUser.role)
     }
   },
   methods: {
diff --git a/src/components/interactions/interactions.vue b/src/components/interactions/interactions.vue
index 57d5d87c..b7291c02 100644
--- a/src/components/interactions/interactions.vue
+++ b/src/components/interactions/interactions.vue
@@ -21,6 +21,15 @@
         key="follows"
         :label="$t('interactions.follows')"
       />
+      <span
+        key="reactions"
+        :label="$t('interactions.emoji_reactions')"
+      />
+      <span
+        v-if="canSeeReports"
+        key="reports"
+        :label="$t('interactions.reports')"
+      />
       <span
         v-if="!allowFollowingMove"
         key="moves"
diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js
index 398bb7a9..8f74c0e6 100644
--- a/src/components/notification/notification.js
+++ b/src/components/notification/notification.js
@@ -4,6 +4,7 @@ import Status from '../status/status.vue'
 import UserAvatar from '../user_avatar/user_avatar.vue'
 import UserCard from '../user_card/user_card.vue'
 import Timeago from '../timeago/timeago.vue'
+import Report from '../report/report.vue'
 import RichContent from 'src/components/rich_content/rich_content.jsx'
 import { isStatusNotification } from '../../services/notification_utils/notification_utils.js'
 import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
@@ -46,6 +47,7 @@ const Notification = {
     UserCard,
     Timeago,
     Status,
+    Report,
     RichContent
   },
   methods: {
diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue
index 9ecb034f..1ababfc0 100644
--- a/src/components/notification/notification.vue
+++ b/src/components/notification/notification.vue
@@ -120,6 +120,9 @@
                 </i18n-t>
               </small>
             </span>
+            <span v-if="notification.type === 'pleroma:report'">
+              <small>{{ $t('notifications.submitted_report') }}</small>
+            </span>
           </div>
           <div
             v-if="isStatusNotification"
@@ -202,6 +205,10 @@
             @{{ notification.target.screen_name_ui }}
           </router-link>
         </div>
+        <Report
+          v-else-if="notification.type === 'pleroma:report'"
+          :report-id="notification.report.id"
+        />
         <template v-else>
           <StatusContent
             class="faint"
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss
index a285027d..c3ceb89a 100644
--- a/src/components/notifications/notifications.scss
+++ b/src/components/notifications/notifications.scss
@@ -59,8 +59,10 @@
       height: 32px;
     }
 
-    --link: var(--faintLink);
-    --text: var(--faint);
+    .faint {
+      --link: var(--faintLink);
+      --text: var(--faint);
+    }
   }
 
   .follow-request-accept {
diff --git a/src/components/report/report.js b/src/components/report/report.js
new file mode 100644
index 00000000..76055764
--- /dev/null
+++ b/src/components/report/report.js
@@ -0,0 +1,34 @@
+import Select from '../select/select.vue'
+import StatusContent from '../status_content/status_content.vue'
+import Timeago from '../timeago/timeago.vue'
+import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
+
+const Report = {
+  props: [
+    'reportId'
+  ],
+  components: {
+    Select,
+    StatusContent,
+    Timeago
+  },
+  computed: {
+    report () {
+      return this.$store.state.reports.reports[this.reportId] || {}
+    },
+    state: {
+      get: function () { return this.report.state },
+      set: function (val) { this.setReportState(val) }
+    }
+  },
+  methods: {
+    generateUserProfileLink (user) {
+      return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
+    },
+    setReportState (state) {
+      return this.$store.dispatch('setReportState', { id: this.report.id, state })
+    }
+  }
+}
+
+export default Report
diff --git a/src/components/report/report.scss b/src/components/report/report.scss
new file mode 100644
index 00000000..578b4eb1
--- /dev/null
+++ b/src/components/report/report.scss
@@ -0,0 +1,43 @@
+@import '../../_variables.scss';
+
+.Report {
+  .report-content {
+    margin: 0.5em 0 1em;
+  }
+
+  .report-state {
+    margin: 0.5em 0 1em;
+  }
+
+  .reported-status {
+    border: 1px solid $fallback--faint;
+    border-color: var(--faint, $fallback--faint);
+    border-radius: $fallback--inputRadius;
+    border-radius: var(--inputRadius, $fallback--inputRadius);
+    color: $fallback--text;
+    color: var(--text, $fallback--text);
+    display: block;
+    padding: 0.5em;
+    margin: 0.5em 0;
+
+    .status-content {
+      pointer-events: none;
+    }
+
+    .reported-status-heading {
+      display: flex;
+      width: 100%;
+      justify-content: space-between;
+      margin-bottom: 0.2em;
+    }
+
+    .reported-status-name {
+      font-weight: bold;
+    }
+  }
+
+  .note {
+    width: 100%;
+    margin-bottom: 0.5em;
+  }
+}
diff --git a/src/components/report/report.vue b/src/components/report/report.vue
new file mode 100644
index 00000000..1f19cc25
--- /dev/null
+++ b/src/components/report/report.vue
@@ -0,0 +1,74 @@
+<template>
+  <div class="Report">
+    <div class="reported-user">
+      <span>{{ $t('report.reported_user') }}</span>
+      <router-link :to="generateUserProfileLink(report.acct)">
+        @{{ report.acct.screen_name }}
+      </router-link>
+    </div>
+    <div class="reporter">
+      <span>{{ $t('report.reporter') }}</span>
+      <router-link :to="generateUserProfileLink(report.actor)">
+        @{{ report.actor.screen_name }}
+      </router-link>
+    </div>
+    <div class="report-state">
+      <span>{{ $t('report.state') }}</span>
+      <Select
+        :id="report-state"
+        v-model="state"
+        class="form-control"
+      >
+        <option
+          v-for="state in ['open', 'closed', 'resolved']"
+          :key="state"
+          :value="state"
+        >
+          {{ $t('report.state_' + state) }}
+        </option>
+      </Select>
+    </div>
+    <RichContent
+      class="report-content"
+      :html="report.content"
+      :emoji="[]"
+    />
+    <div v-if="report.statuses.length">
+      <small>{{ $t('report.reported_statuses') }}</small>
+      <router-link
+        v-for="status in report.statuses"
+        :key="status.id"
+        :to="{ name: 'conversation', params: { id: status.id } }"
+        class="reported-status"
+      >
+        <div class="reported-status-heading">
+          <span class="reported-status-name">{{ status.user.name }}</span>
+          <Timeago
+            :time="status.created_at"
+            :auto-update="240"
+            class="faint"
+          />
+        </div>
+        <status-content :status="status" />
+      </router-link>
+    </div>
+    <div v-if="report.notes.length">
+      <small>{{ $t('report.notes') }}</small>
+      <div
+        v-for="note in report.notes"
+        :key="note.id"
+        class="note"
+      >
+        <span>{{ note.content }}</span>
+        <Timeago
+          :time="note.created_at"
+          :auto-update="240"
+          class="faint"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script src="./report.js"></script>
+<style src="./report.scss" lang="scss"></style>
diff --git a/src/components/user_reporting_modal/user_reporting_modal.js b/src/components/user_reporting_modal/user_reporting_modal.js
index 8d171b2d..85ffc661 100644
--- a/src/components/user_reporting_modal/user_reporting_modal.js
+++ b/src/components/user_reporting_modal/user_reporting_modal.js
@@ -1,4 +1,3 @@
-
 import Status from '../status/status.vue'
 import List from '../list/list.vue'
 import Checkbox from '../checkbox/checkbox.vue'
@@ -21,14 +20,17 @@ const UserReportingModal = {
     }
   },
   computed: {
+    reportModal () {
+      return this.$store.state.reports.reportModal
+    },
     isLoggedIn () {
       return !!this.$store.state.users.currentUser
     },
     isOpen () {
-      return this.isLoggedIn && this.$store.state.reports.modalActivated
+      return this.isLoggedIn && this.reportModal.activated
     },
     userId () {
-      return this.$store.state.reports.userId
+      return this.reportModal.userId
     },
     user () {
       return this.$store.getters.findUser(this.userId)
@@ -37,10 +39,10 @@ const UserReportingModal = {
       return !this.user.is_local && this.user.screen_name.substr(this.user.screen_name.indexOf('@') + 1)
     },
     statuses () {
-      return this.$store.state.reports.statuses
+      return this.reportModal.statuses
     },
     preTickedIds () {
-      return this.$store.state.reports.preTickedIds
+      return this.reportModal.preTickedIds
     }
   },
   watch: {
diff --git a/src/i18n/en.json b/src/i18n/en.json
index f8336e5c..5e7ee494 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -66,6 +66,7 @@
     "more": "More",
     "loading": "Loading…",
     "generic_error": "An error occured",
+    "generic_error_message": "An error occured: {0}",
     "error_retry": "Please try again",
     "retry": "Try again",
     "optional": "optional",
@@ -160,7 +161,8 @@
     "repeated_you": "repeated your status",
     "no_more_notifications": "No more notifications",
     "migrated_to": "migrated to",
-    "reacted_with": "reacted with {0}"
+    "reacted_with": "reacted with {0}",
+    "submitted_report": "submitted a report"
   },
   "polls": {
     "add_poll": "Add poll",
@@ -195,6 +197,8 @@
   "interactions": {
     "favs_repeats": "Repeats and favorites",
     "follows": "New follows",
+    "emoji_reactions": "Emoji Reactions",
+    "reports": "Reports",
     "moves": "User migrates",
     "load_older": "Load older interactions"
   },
@@ -261,6 +265,16 @@
     "searching_for": "Searching for",
     "error": "Not found."
   },
+  "report": {
+    "reporter": "Reporter:",
+    "reported_user": "Reported user:",
+    "reported_statuses": "Reported statuses:",
+    "notes": "Notes:",
+    "state": "State:",
+    "state_open": "Open",
+    "state_closed": "Closed",
+    "state_resolved": "Resolved"
+  },
   "selectable_list": {
     "select_all": "Select all"
   },
diff --git a/src/i18n/nl.json b/src/i18n/nl.json
index b113ffe4..fd61572c 100644
--- a/src/i18n/nl.json
+++ b/src/i18n/nl.json
@@ -745,6 +745,8 @@
     "favs_repeats": "Herhalingen en favorieten",
     "follows": "Nieuwe gevolgden",
     "moves": "Gebruikermigraties",
+    "emoji_reactions": "Emoji Reacties",
+    "reports": "Rapportages",
     "load_older": "Oudere interacties laden"
   },
   "remote_user_resolver": {
@@ -752,6 +754,17 @@
     "error": "Niet gevonden.",
     "remote_user_resolver": "Externe gebruikers-zoeker"
   },
+  "report": {
+    "reporter": "Reporteerder:",
+    "reported_user": "Gerapporteerde gebruiker:",
+    "reported_statuses": "Gerapporteerde statussen:",
+    "notes": "Notas:",
+    "state": "Status:",
+    "state_open": "Open",
+    "state_closed": "Gesloten",
+    "state_resolved": "Opgelost"
+  },
+
   "selectable_list": {
     "select_all": "Alles selecteren"
   },
diff --git a/src/modules/config.js b/src/modules/config.js
index ff5ef270..561cacd4 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -54,6 +54,7 @@ export const defaultState = {
     moves: true,
     emojiReactions: true,
     followRequest: true,
+    reports: true,
     chatMention: true
   },
   webPushNotifications: false,
diff --git a/src/modules/reports.js b/src/modules/reports.js
index fea83e5f..925792c0 100644
--- a/src/modules/reports.js
+++ b/src/modules/reports.js
@@ -2,20 +2,29 @@ import filter from 'lodash/filter'
 
 const reports = {
   state: {
-    userId: null,
-    statuses: [],
-    preTickedIds: [],
-    modalActivated: false
+    reportModal: {
+      userId: null,
+      statuses: [],
+      preTickedIds: [],
+      activated: false
+    },
+    reports: {}
   },
   mutations: {
     openUserReportingModal (state, { userId, statuses, preTickedIds }) {
-      state.userId = userId
-      state.statuses = statuses
-      state.preTickedIds = preTickedIds
-      state.modalActivated = true
+      state.reportModal.userId = userId
+      state.reportModal.statuses = statuses
+      state.reportModal.preTickedIds = preTickedIds
+      state.reportModal.activated = true
     },
     closeUserReportingModal (state) {
-      state.modalActivated = false
+      state.reportModal.activated = false
+    },
+    setReportState (reportsState, { id, state }) {
+      reportsState.reports[id].state = state
+    },
+    addReport (state, report) {
+      state.reports[report.id] = report
     }
   },
   actions: {
@@ -31,6 +40,23 @@ const reports = {
     },
     closeUserReportingModal ({ commit }) {
       commit('closeUserReportingModal')
+    },
+    setReportState ({ commit, dispatch, rootState }, { id, state }) {
+      const oldState = rootState.reports.reports[id].state
+      commit('setReportState', { id, state })
+      rootState.api.backendInteractor.setReportState({ id, state }).catch(e => {
+        console.error('Failed to set report state', e)
+        dispatch('pushGlobalNotice', {
+          level: 'error',
+          messageKey: 'general.generic_error_message',
+          messageArgs: [e.message],
+          timeout: 5000
+        })
+        commit('setReportState', { id, state: oldState })
+      })
+    },
+    addReport ({ commit }, report) {
+      commit('addReport', report)
     }
   }
 }
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index a13930e9..66cc82bc 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -336,6 +336,10 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
       notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item
     }
 
+    if (notification.type === 'pleroma:report') {
+      dispatch('addReport', notification.report)
+    }
+
     if (notification.type === 'pleroma:emoji_reaction') {
       dispatch('fetchEmojiReactionsBy', notification.status.id)
     }
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 436b8b0a..c17d0476 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -87,6 +87,7 @@ const PLEROMA_CHAT_URL = id => `/api/v1/pleroma/chats/by-account-id/${id}`
 const PLEROMA_CHAT_MESSAGES_URL = id => `/api/v1/pleroma/chats/${id}/messages`
 const PLEROMA_CHAT_READ_URL = id => `/api/v1/pleroma/chats/${id}/read`
 const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) => `/api/v1/pleroma/chats/${chatId}/messages/${messageId}`
+const PLEROMA_ADMIN_REPORTS = '/api/pleroma/admin/reports'
 
 const oldfetch = window.fetch
 
@@ -497,7 +498,8 @@ const fetchTimeline = ({
   userId = false,
   tag = false,
   withMuted = false,
-  replyVisibility = 'all'
+  replyVisibility = 'all',
+  includeTypes = []
 }) => {
   const timelineUrls = {
     public: MASTODON_PUBLIC_TIMELINE,
@@ -544,6 +546,11 @@ const fetchTimeline = ({
   if (replyVisibility !== 'all') {
     params.push(['reply_visibility', replyVisibility])
   }
+  if (includeTypes.length > 0) {
+    includeTypes.forEach(type => {
+      params.push(['include_types[]', type])
+    })
+  }
 
   params.push(['limit', 20])
 
@@ -1266,6 +1273,38 @@ const deleteChatMessage = ({ chatId, messageId, credentials }) => {
   })
 }
 
+const setReportState = ({ id, state, credentials }) => {
+  // TODO: Can't use promisedRequest because on OK this does not return json
+  // See https://git.pleroma.social/pleroma/pleroma-fe/-/merge_requests/1322
+  return fetch(PLEROMA_ADMIN_REPORTS, {
+    headers: {
+      ...authHeaders(credentials),
+      'Accept': 'application/json',
+      'Content-Type': 'application/json'
+    },
+    method: 'PATCH',
+    body: JSON.stringify({
+      reports: [{
+        id,
+        state
+      }]
+    })
+  })
+    .then(data => {
+      if (data.status >= 500) {
+        throw Error(data.statusText)
+      } else if (data.status >= 400) {
+        return data.json()
+      }
+      return data
+    })
+    .then(data => {
+      if (data.errors) {
+        throw Error(data.errors[0].message)
+      }
+    })
+}
+
 const apiService = {
   verifyCredentials,
   fetchTimeline,
@@ -1351,7 +1390,8 @@ const apiService = {
   chatMessages,
   sendChatMessage,
   readChat,
-  deleteChatMessage
+  deleteChatMessage,
+  setReportState
 }
 
 export default apiService
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index f219c161..0005e95a 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -387,6 +387,13 @@ export const parseNotification = (data) => {
       : parseUser(data.target)
     output.from_profile = parseUser(data.account)
     output.emoji = data.emoji
+    if (data.report) {
+      output.report = data.report
+      output.report.content = data.report.content
+      output.report.acct = parseUser(data.report.account)
+      output.report.actor = parseUser(data.report.actor)
+      output.report.statuses = data.report.statuses.map(parseStatus)
+    }
   } else {
     const parsedNotice = parseStatus(data.notice)
     output.type = data.ntype
diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js
index 6fef1022..8c70ea53 100644
--- a/src/services/notification_utils/notification_utils.js
+++ b/src/services/notification_utils/notification_utils.js
@@ -14,7 +14,8 @@ export const visibleTypes = store => {
     rootState.config.notificationVisibility.follows && 'follow',
     rootState.config.notificationVisibility.followRequest && 'follow_request',
     rootState.config.notificationVisibility.moves && 'move',
-    rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction'
+    rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction',
+    rootState.config.notificationVisibility.reports && 'pleroma:report'
   ].filter(_ => _))
 }
 
@@ -98,6 +99,9 @@ export const prepareNotificationObject = (notification, i18n) => {
     case 'follow_request':
       i18nString = 'follow_request'
       break
+    case 'pleroma:report':
+      i18nString = 'submitted_report'
+      break
   }
 
   if (notification.type === 'pleroma:emoji_reaction') {
diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js
index f83f871e..cb241e33 100644
--- a/src/services/notifications_fetcher/notifications_fetcher.service.js
+++ b/src/services/notifications_fetcher/notifications_fetcher.service.js
@@ -1,6 +1,18 @@
 import apiService from '../api/api.service.js'
 import { promiseInterval } from '../promise_interval/promise_interval.js'
 
+// For using include_types when fetching notifications.
+// Note: chat_mention excluded as pleroma-fe polls them separately
+const mastoApiNotificationTypes = [
+  'mention',
+  'favourite',
+  'reblog',
+  'follow',
+  'move',
+  'pleroma:emoji_reaction',
+  'pleroma:report'
+]
+
 const update = ({ store, notifications, older }) => {
   store.dispatch('addNewNotifications', { notifications, older })
 }
@@ -12,6 +24,7 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
   const timelineData = rootState.statuses.notifications
   const hideMutedPosts = getters.mergedConfig.hideMutedPosts
 
+  args['includeTypes'] = mastoApiNotificationTypes
   args['withMuted'] = !hideMutedPosts
 
   args['timeline'] = 'notifications'
@@ -63,6 +76,7 @@ const fetchNotifications = ({ store, args, older }) => {
         messageArgs: [error.message],
         timeout: 5000
       })
+      console.error(error)
     })
 }