diff --git a/CHANGELOG.md b/CHANGELOG.md
index f4733791995a7f6b1fb7bac31bd449c7717c46ee..00ad896e0b47dc07a2f4700c4c1eeb18c02dcdf8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,7 +14,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Actions in users module (ActivateUsers, AddRight, DeactivateUsers, DeleteRight, DeleteUsers) now accept an array of users instead of one user
 
 ### Added
+
 - Optimistic update for actions in users module and fetching users after api function finished its execution
+- Relay management
 
 ## [1.2.0] - 2019-09-27
 
diff --git a/src/api/relays.js b/src/api/relays.js
new file mode 100644
index 0000000000000000000000000000000000000000..3be0188db3b15f0d802950a6ff313e6140d0e6ef
--- /dev/null
+++ b/src/api/relays.js
@@ -0,0 +1,34 @@
+import request from '@/utils/request'
+import { getToken } from '@/utils/auth'
+import { baseName } from './utils'
+
+export async function fetchRelays(authHost, token) {
+  return await request({
+    baseURL: baseName(authHost),
+    url: '/api/pleroma/admin/relay',
+    method: 'get',
+    headers: authHeaders(token)
+  })
+}
+
+export async function addRelay(relay, authHost, token) {
+  return await request({
+    baseURL: baseName(authHost),
+    url: '/api/pleroma/admin/relay',
+    method: 'post',
+    headers: authHeaders(token),
+    data: { relay_url: relay }
+  })
+}
+
+export async function deleteRelay(relay, authHost, token) {
+  return await request({
+    baseURL: baseName(authHost),
+    url: '/api/pleroma/admin/relay',
+    method: 'delete',
+    headers: authHeaders(token),
+    data: { relay_url: `https://${relay}/actor` }
+  })
+}
+
+const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}
diff --git a/src/lang/en.js b/src/lang/en.js
index 46b4b6417131859b6b8eda254ad1bf905bccd851..8575144aa6cbed6b5413ac7878d75faf37c7c7fd 100644
--- a/src/lang/en.js
+++ b/src/lang/en.js
@@ -312,6 +312,10 @@ export default {
     rateLimiters: 'Rate limiters',
     database: 'Database',
     other: 'Other',
+    relays: 'Relays',
+    follow: 'Follow',
+    followRelay: 'Follow new relay',
+    instanceUrl: 'Instance URL',
     success: 'Settings changed successfully!',
     emojiPacks: 'Emoji packs',
     reloadEmoji: 'Reload emoji',
diff --git a/src/store/index.js b/src/store/index.js
index 668b4d4431ce912421e77ea91962be94576a3e96..c71fca877b61e34f3bca306d5c28c4fe3b947875 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -5,6 +5,7 @@ import errorLog from './modules/errorLog'
 import moderationLog from './modules/moderationLog'
 import invites from './modules/invites'
 import permission from './modules/permission'
+import relays from './modules/relays'
 import reports from './modules/reports'
 import settings from './modules/settings'
 import tagsView from './modules/tagsView'
@@ -23,6 +24,7 @@ const store = new Vuex.Store({
     moderationLog,
     invites,
     permission,
+    relays,
     reports,
     settings,
     tagsView,
diff --git a/src/store/modules/relays.js b/src/store/modules/relays.js
new file mode 100644
index 0000000000000000000000000000000000000000..6353569e2ebc5eac98e2d8df0b9a5319e149e726
--- /dev/null
+++ b/src/store/modules/relays.js
@@ -0,0 +1,44 @@
+import { fetchRelays, addRelay, deleteRelay } from '@/api/relays'
+
+const relays = {
+  state: {
+    fetchedRelays: [],
+    loading: true
+  },
+  mutations: {
+    SET_LOADING: (state, loading) => {
+      state.loading = loading
+    },
+    SET_RELAYS: (state, relays) => {
+      state.fetchedRelays = relays
+    },
+    ADD_RELAY: (state, relay) => {
+      state.fetchedRelays = [...state.fetchedRelays, relay]
+    },
+    DELETE_RELAY: (state, relay) => {
+      state.fetchedRelays = state.fetchedRelays.filter(fetchedRelay => fetchedRelay !== relay)
+    }
+  },
+  actions: {
+    async FetchRelays({ commit, getters }) {
+      commit('SET_LOADING', true)
+
+      const response = await fetchRelays(getters.authHost, getters.token)
+
+      commit('SET_RELAYS', response.data.relays)
+      commit('SET_LOADING', false)
+    },
+    async AddRelay({ commit, getters }, relay) {
+      commit('ADD_RELAY', relay)
+
+      await addRelay(relay, getters.authHost, getters.token)
+    },
+    async DeleteRelay({ commit, getters }, relay) {
+      commit('DELETE_RELAY', relay)
+
+      await deleteRelay(relay, getters.authHost, getters.token)
+    }
+  }
+}
+
+export default relays
diff --git a/src/views/settings/components/Relays.vue b/src/views/settings/components/Relays.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ef9264b4acc10db9535cba900f26b9de82bc97a4
--- /dev/null
+++ b/src/views/settings/components/Relays.vue
@@ -0,0 +1,79 @@
+<template>
+  <div v-if="!loading">
+    <el-row :gutter="5">
+      <el-col :span="8">
+        <el-input v-model="newRelay" :placeholder="$t('settings.followRelay')" @keyup.enter.native="followRelay"/>
+      </el-col>
+      <el-col :span="8">
+        <el-button type="primary" @click.native="followRelay">{{ $t('settings.follow') }}</el-button>
+      </el-col>
+    </el-row>
+    <el-table :data="relaysTable">
+      <el-table-column
+        :label="$t('settings.instanceUrl')"
+        prop="instance"/>
+      <el-table-column fixed="right" width="120">
+        <template slot-scope="scope">
+          <el-button
+            type="text"
+            size="small"
+            @click.native="deleteRelay(scope.row.instance)">
+            {{ $t('table.delete') }}
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Relays',
+  data() {
+    return {
+      newRelay: ''
+    }
+  },
+  computed: {
+    relays() {
+      return this.$store.state.relays.fetchedRelays
+    },
+    relaysTable() {
+      return this.relays.map(relay => {
+        return { instance: relay }
+      })
+    },
+    loading() {
+      return this.$store.state.relays.loading
+    }
+  },
+  mounted() {
+    this.$store.dispatch('FetchRelays')
+  },
+  methods: {
+    followRelay() {
+      try {
+        this.$store.dispatch('AddRelay', this.newRelay)
+      } catch (_e) {
+        return
+      } finally {
+        this.$store.dispatch('FetchRelays')
+      }
+    },
+    deleteRelay(relay) {
+      try {
+        this.$store.dispatch('DeleteRelay', relay)
+      } catch (_e) {
+        return
+      } finally {
+        this.$store.dispatch('FetchRelays')
+      }
+    }
+  }
+}
+</script>
+
+<style rel='stylesheet/scss' lang='scss'>
+@import '../styles/main';
+@include settings
+</style>
diff --git a/src/views/settings/components/index.js b/src/views/settings/components/index.js
index 4e5b8e8ec06ab495bcb106d5ab7526d72b0b914c..040d67120287bb9ef5721d169498a4a163a1ddf0 100644
--- a/src/views/settings/components/index.js
+++ b/src/views/settings/components/index.js
@@ -17,5 +17,6 @@ export { default as Metadata } from './Metadata'
 export { default as Mrf } from './MRF'
 export { default as Other } from './Other'
 export { default as RateLimiters } from './RateLimiters'
+export { default as Relays } from './Relays'
 export { default as Upload } from './Upload'
 export { default as WebPush } from './WebPush'
diff --git a/src/views/settings/index.vue b/src/views/settings/index.vue
index 5e0c482b3c4c1363ddef738f347853c85df18506..bc5afaad234940ceb4287e9d6268d8e25cd50f7c 100644
--- a/src/views/settings/index.vue
+++ b/src/views/settings/index.vue
@@ -59,6 +59,9 @@
       <el-tab-pane :label="$t('settings.rateLimiters')">
         <rate-limiters/>
       </el-tab-pane>
+      <el-tab-pane :label="$t('settings.relays')">
+        <relays/>
+      </el-tab-pane>
       <el-tab-pane :label="$t('settings.upload')">
         <upload/>
       </el-tab-pane>
@@ -73,11 +76,11 @@
 </template>
 
 <script>
-import { ActivityPub, Authentication, AutoLinker, Captcha, Database, Endpoint, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Upload, WebPush } from './components'
+import { ActivityPub, Authentication, AutoLinker, Captcha, Database, Endpoint, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush } from './components'
 import EmojiPacks from '../emojiPacks/index'
 
 export default {
-  components: { ActivityPub, Authentication, AutoLinker, Captcha, Database, Endpoint, EmojiPacks, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Upload, WebPush },
+  components: { ActivityPub, Authentication, AutoLinker, Captcha, Database, Endpoint, EmojiPacks, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush },
   computed: {
     isMobile() {
       return this.$store.state.app.device === 'mobile'