diff --git a/src/boot/routes.js b/src/boot/routes.js
index 715b394e..1ab8209d 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -22,6 +22,7 @@ import About from 'components/about/about.vue'
 import RemoteUserResolver from 'components/remote_user_resolver/remote_user_resolver.vue'
 import Lists from 'components/lists/lists.vue'
 import ListTimeline from 'components/list_timeline/list_timeline.vue'
+import ListEdit from 'components/list_edit/list_edit.vue'
 
 export default (store) => {
   const validateAuthenticatedRoute = (to, from, next) => {
@@ -73,7 +74,8 @@ export default (store) => {
     { name: 'about', path: '/about', component: About },
     { name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile },
     { name: 'lists', path: '/lists', component: Lists },
-    { name: 'list-timeline', path: '/lists/:id', component: ListTimeline }
+    { name: 'list-timeline', path: '/lists/:id', component: ListTimeline },
+    { name: 'list-edit', path: '/lists/:id/edit', component: ListEdit }
   ]
 
   if (store.state.instance.pleromaChatMessagesAvailable) {
diff --git a/src/components/list_card/list_card.js b/src/components/list_card/list_card.js
index 42e56aff..6546796c 100644
--- a/src/components/list_card/list_card.js
+++ b/src/components/list_card/list_card.js
@@ -1,3 +1,12 @@
+import { library } from '@fortawesome/fontawesome-svg-core'
+import {
+  faEllipsisH
+} from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+  faEllipsisH
+)
+
 const ListCard = {
   props: [
     'list'
diff --git a/src/components/list_card/list_card.vue b/src/components/list_card/list_card.vue
index 5b4fefc8..752a7eb5 100644
--- a/src/components/list_card/list_card.vue
+++ b/src/components/list_card/list_card.vue
@@ -1,10 +1,21 @@
 <template>
-  <router-link
-    :to="listLink(list.id)"
-    class="list-card"
-  >
-    {{ list.title }}
-  </router-link>
+  <div class="list-card">
+    <router-link
+      :to="listLink(list.id)"
+      class="list-name"
+    >
+      {{ list.title }}
+    </router-link>
+    <router-link
+      :to="listLink(list.id) + '/edit'"
+      class="button-list-edit"
+    >
+      <FAIcon
+        class="fa-scale-110 fa-old-padding"
+        icon="ellipsis-h"
+      />
+    </router-link>
+  </div>
 </template>
 
 <script src="./list_card.js"></script>
@@ -13,10 +24,13 @@
 @import '../../_variables.scss';
 
 .list-card {
+  display: flex;
+}
+
+.list-name,
+.button-list-edit {
   margin: 0;
   padding: 1em;
-  display: flex;
-  flex: 1 0;
   color: $fallback--link;
   color: var(--link, $fallback--link);
 
@@ -30,4 +44,8 @@
     --lightText: var(--selectedMenuLightText, $fallback--lightText);
   }
 }
+
+.list-name {
+  flex-grow: 1;
+}
 </style>
diff --git a/src/components/list_edit/list_edit.js b/src/components/list_edit/list_edit.js
new file mode 100644
index 00000000..0e03dbcb
--- /dev/null
+++ b/src/components/list_edit/list_edit.js
@@ -0,0 +1,106 @@
+import { mapState, mapGetters } from 'vuex'
+import BasicUserCard from '../basic_user_card/basic_user_card.vue'
+import UserAvatar from '../user_avatar/user_avatar.vue'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import {
+  faSearch,
+  faChevronLeft
+} from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+  faSearch,
+  faChevronLeft
+)
+
+const ListNew = {
+  components: {
+    BasicUserCard,
+    UserAvatar
+  },
+  data () {
+    return {
+      title: '',
+      suggestions: [],
+      userIds: [],
+      selectedUserIds: [],
+      loading: false,
+      query: ''
+    }
+  },
+  created () {
+    this.$store.state.api.backendInteractor.getList({ id: this.id })
+      .then((data) => { this.title = data.title })
+    this.$store.state.api.backendInteractor.getListAccounts({ id: this.id })
+      .then((data) => { this.selectedUserIds = data })
+  },
+  computed: {
+    id () {
+      return this.$route.params.id
+    },
+    users () {
+      return this.userIds.map(userId => this.findUser(userId))
+    },
+    availableUsers () {
+      if (this.query.length !== 0) {
+        return this.users
+      } else {
+        return this.suggestions
+      }
+    },
+    ...mapState({
+      currentUser: state => state.users.currentUser,
+      backendInteractor: state => state.api.backendInteractor
+    }),
+    ...mapGetters(['findUser'])
+  },
+  methods: {
+    onInput () {
+      this.search(this.query)
+    },
+    selectUser (user, event) {
+      if (this.selectedUserIds.includes(user.id)) {
+        this.removeUser(user.id)
+        event.target.classList.remove('selected')
+      } else {
+        this.addUser(user)
+        event.target.classList.add('selected')
+      }
+    },
+    addUser (user) {
+      this.selectedUserIds.push(user.id)
+    },
+    removeUser (userId) {
+      this.selectedUserIds = this.selectedUserIds.filter(id => id !== userId)
+    },
+    search (query) {
+      if (!query) {
+        this.loading = false
+        return
+      }
+
+      this.loading = true
+      this.userIds = []
+      this.$store.dispatch('search', { q: query, resolve: true, type: 'accounts', following: true })
+        .then(data => {
+          this.loading = false
+          this.userIds = data.accounts.map(a => a.id)
+        })
+    },
+    updateList () {
+      // the API has two different endpoints for "updating the list name" and
+      // "updating the accounts on the list".
+      this.$store.state.api.backendInteractor.updateList({ id: this.id, title: this.title })
+      this.$store.state.api.backendInteractor.addAccountsToList({
+        id: this.id, accountIds: this.selectedUserIds
+      }).then(() => {
+        this.$router.push({ name: 'list-timeline', params: { id: this.id } })
+      })
+    },
+    deleteList () {
+      this.$store.state.api.backendInteractor.deleteList({ id: this.id })
+        .then(this.$router.push({ name: 'lists' }))
+    }
+  }
+}
+
+export default ListNew
diff --git a/src/components/list_edit/list_edit.vue b/src/components/list_edit/list_edit.vue
new file mode 100644
index 00000000..cadd25da
--- /dev/null
+++ b/src/components/list_edit/list_edit.vue
@@ -0,0 +1,106 @@
+<template>
+  <div class="panel-default panel list-edit">
+    <div
+      ref="header"
+      class="panel-heading"
+    >
+      <button
+        @click="$router.back"
+        class="button-unstyled go-back-button"
+      >
+        <FAIcon
+          size="lg"
+          icon="chevron-left"
+        />
+      </button>
+    </div>
+    <div class="input-wrap">
+      <input
+        ref="title"
+        v-model="title"
+        :placeholder="$t('lists.title')"
+      />
+    </div>
+    <div class="input-wrap">
+      <div class="input-search">
+        <FAIcon
+          class="search-icon fa-scale-110 fa-old-padding"
+          icon="search"
+        />
+      </div>
+      <input
+        ref="search"
+        v-model="query"
+        :placeholder="$t('lists.search')"
+        @input="onInput"
+      >
+    </div>
+    <div class="member-list">
+      <div
+        v-for="user in availableUsers"
+        :key="user.id"
+        class="member"
+      >
+        <div @click.capture.prevent="selectUser(user, $event)">
+          <BasicUserCard :user="user" />
+        </div>
+      </div>
+    </div>
+    <button
+      :disabled="title && title.length === 0"
+      class="btn button-default"
+      @click="updateList"
+    >
+      {{ $t('lists.save') }}
+    </button>
+    <button
+      class="btn button-default"
+      @click="deleteList"
+    >
+      {{ $t('lists.delete') }}
+    </button>
+  </div>
+</template>
+
+<script src="./list_edit.js"></script>
+
+<style lang="scss">
+@import '../../_variables.scss';
+
+.list-edit {
+  .input-wrap {
+    display: flex;
+    margin: 0.7em 0.5em 0.7em 0.5em;
+
+    input {
+      width: 100%;
+    }
+  }
+
+  .search-icon {
+    margin-right: 0.3em;
+  }
+
+  .member-list {
+    padding-bottom: 0.7rem;
+  }
+
+  .basic-user-card:hover,
+  .basic-user-card.selected {
+    cursor: pointer;
+    background-color: var(--selectedPost, $fallback--lightBg);
+  }
+
+  .go-back-button {
+    text-align: center;
+    line-height: 1;
+    height: 100%;
+    align-self: start;
+    width: var(--__panel-heading-height-inner);
+  }
+
+  .btn {
+    margin: 0.5em;
+  }
+}
+</style>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index e113400f..3430620b 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -952,7 +952,9 @@
     "new": "New List",
     "title": "List title",
     "search": "Search users",
-    "create": "Create"
+    "create": "Create",
+    "save": "Save changes",
+    "delete": "Delete list"
   },
   "file_type": {
     "audio": "Audio",
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index ff6c00d5..7f2fc5ac 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -50,6 +50,7 @@ const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context`
 const MASTODON_USER_URL = '/api/v1/accounts'
 const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships'
 const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses`
+const MASTODON_LIST_URL = id => `/api/v1/lists/${id}`
 const MASTODON_LIST_TIMELINE_URL = id => `/api/v1/timelines/list/${id}`
 const MASTODON_LIST_ACCOUNTS_URL = id => `/api/v1/lists/${id}/accounts`
 const MASTODON_TAG_TIMELINE_URL = tag => `/api/v1/timelines/tag/${tag}`
@@ -403,6 +404,31 @@ const createList = ({ title, credentials }) => {
   }).then((data) => data.json())
 }
 
+const getList = ({ id, credentials }) => {
+  const url = MASTODON_LIST_URL(id)
+  return fetch(url, { headers: authHeaders(credentials) })
+    .then((data) => data.json())
+}
+
+const updateList = ({ id, title, credentials }) => {
+  const url = MASTODON_LIST_URL(id)
+  const headers = authHeaders(credentials)
+  headers['Content-Type'] = 'application/json'
+
+  return fetch(url, {
+    method: 'PUT',
+    headers: headers,
+    body: JSON.stringify({ title })
+  })
+}
+
+const getListAccounts = ({ id, credentials }) => {
+  const url = MASTODON_LIST_ACCOUNTS_URL(id)
+  return fetch(url, { headers: authHeaders(credentials) })
+    .then((data) => data.json())
+    .then((data) => data.map(({ id }) => id))
+}
+
 const addAccountsToList = ({ id, accountIds, credentials }) => {
   const url = MASTODON_LIST_ACCOUNTS_URL(id)
   const headers = authHeaders(credentials)
@@ -415,6 +441,14 @@ const addAccountsToList = ({ id, accountIds, credentials }) => {
   })
 }
 
+const deleteList = ({ id, credentials }) => {
+  const url = MASTODON_LIST_URL(id)
+  return fetch(url, {
+    method: 'DELETE',
+    headers: authHeaders(credentials)
+  })
+}
+
 const fetchConversation = ({ id, credentials }) => {
   let urlContext = MASTODON_STATUS_CONTEXT_URL(id)
   return fetch(urlContext, { headers: authHeaders(credentials) })
@@ -1389,7 +1423,11 @@ const apiService = {
   fetchFollowRequests,
   fetchLists,
   createList,
+  getList,
+  updateList,
+  getListAccounts,
   addAccountsToList,
+  deleteList,
   approveUser,
   denyUser,
   suggestions,