diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index 069c0b40..e663ba15 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -1,5 +1,8 @@
 import { reduce, filter, findIndex, clone, get } from 'lodash'
 import Status from '../status/status.vue'
+import ThreadTree from '../thread_tree/thread_tree.vue'
+
+const debug = console.log
 
 const sortById = (a, b) => {
   const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id
@@ -53,6 +56,15 @@ const conversation = {
     }
   },
   computed: {
+    displayStyle () {
+      return this.$store.state.config.conversationDisplay
+    },
+    isTreeView () {
+      return this.displayStyle === 'tree'
+    },
+    isLinearView () {
+      return this.displayStyle === 'linear'
+    },
     hideStatus () {
       if (this.$refs.statusComponent && this.$refs.statusComponent[0]) {
         return this.virtualHidden && this.$refs.statusComponent[0].suspendable
@@ -90,6 +102,49 @@ const conversation = {
 
       return sortAndFilterConversation(conversation, this.status)
     },
+    threadTree () {
+      const reverseLookupTable = this.conversation.reduce((table, status, index) => {
+        table[status.id] = index
+        return table
+      }, {})
+
+      const threads = this.conversation.reduce((a, cur) => {
+        const id = cur.id
+        a.forest[id] = this.getReplies(id)
+          .map(s => s.id)
+          .sort((a, b) => reverseLookupTable[a] - reverseLookupTable[b])
+
+        a.topLevel = a.topLevel.filter(k => a.forest[id].contains(k))
+        return a
+      }, {
+        forest: {},
+        topLevel: this.conversation.map(s => s.id)
+      })
+
+      const walk = (forest, topLevel, depth = 0, processed = {}) => topLevel.map(id => {
+        if (processed[id]) {
+          return []
+        }
+
+        processed[id] = true
+        return [{
+          status: this.conversation[reverseLookupTable[id]],
+          id,
+          depth
+        }, walk(forest, forest[child], depth + 1, processed)].reduce((a, b) => a.concat(b), [])
+      }).reduce((a, b) => a.concat(b), [])
+
+      const linearized = walk(threads.forest, threads.topLevel)
+
+      return linearized
+    },
+    topLevel () {
+      const topLevel = this.conversation.reduce((tl, cur) =>
+        tl.filter(k => this.getReplies(cur.id).map(v => v.id).indexOf(k.id) === -1), this.conversation)
+      debug("toplevel =", topLevel)
+      debug("toplevel =", topLevel)
+      return topLevel
+    },
     replies () {
       let i = 1
       // eslint-disable-next-line camelcase
@@ -109,7 +164,7 @@ const conversation = {
       }, {})
     },
     isExpanded () {
-      return this.expanded || this.isPage
+      return !!(this.expanded || this.isPage)
     },
     hiddenStyle () {
       const height = (this.status && this.status.virtualHeight) || '120px'
@@ -117,7 +172,8 @@ const conversation = {
     }
   },
   components: {
-    Status
+    Status,
+    ThreadTree
   },
   watch: {
     statusId (newVal, oldVal) {
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index 3fb26d92..cea5f88f 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -18,24 +18,47 @@
         {{ $t('timeline.collapse') }}
       </button>
     </div>
-    <status
-      v-for="status in conversation"
-      :key="status.id"
-      ref="statusComponent"
-      :inline-expanded="collapsable && isExpanded"
-      :statusoid="status"
-      :expandable="!isExpanded"
-      :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
-      :focused="focused(status.id)"
-      :in-conversation="isExpanded"
-      :highlight="getHighlight()"
-      :replies="getReplies(status.id)"
-      :in-profile="inProfile"
-      :profile-user-id="profileUserId"
-      class="conversation-status status-fadein panel-body"
-      @goto="setHighlight"
-      @toggleExpanded="toggleExpanded"
-    />
+    <div v-if="isTreeView">
+      <thread-tree
+        v-for="status in topLevel"
+        :key="status.id"
+        ref="statusComponent"
+
+        :status="status"
+        :in-profile="inProfile"
+        :conversation="conversation"
+        :collapsable="collapsable"
+        :is-expanded="isExpanded"
+        :pinned-status-ids-object="pinnedStatusIdsObject"
+        :profile-user-id="profileUserId"
+
+        :focused="focused"
+        :get-replies="getReplies"
+        :get-highlight="getHighlight"
+        :set-highlight="setHighlight"
+        :toggle-expanded="toggleExpanded"
+      />
+    </div>
+    <div v-if="isLinearView">
+      <status
+        v-for="status in conversation"
+        :key="status.id"
+        ref="statusComponent"
+        :inline-expanded="collapsable && isExpanded"
+        :statusoid="status"
+        :expandable="!isExpanded"
+        :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
+        :focused="focused(status.id)"
+        :in-conversation="isExpanded"
+        :highlight="getHighlight()"
+        :replies="getReplies(status.id)"
+        :in-profile="inProfile"
+        :profile-user-id="profileUserId"
+        class="conversation-status status-fadein panel-body"
+        @goto="setHighlight"
+        @toggleExpanded="toggleExpanded"
+      />
+    </div>
   </div>
   <div
     v-else
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 952c328d..abe8f6f7 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -20,6 +20,11 @@ const GeneralTab = {
         value: mode,
         label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
       })),
+      conversationDisplayOptions: ['tree', 'linear'].map(mode => ({
+        key: mode,
+        value: mode,
+        label: this.$t(`settings.conversation_display_${mode}`)
+      })),
       mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
         key: mode,
         value: mode,
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index eba3b268..8951c021 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -152,6 +152,15 @@
             {{ $t('settings.show_yous') }}
           </BooleanSetting>
         </li>
+        <li>
+          <ChoiceSetting
+            id="conversationDisplay"
+            path="conversationDisplay"
+            :options="conversationDisplayOptions"
+          >
+            {{ $t('settings.conversation_display') }}
+          </ChoiceSetting>
+        </li>
         <li>
           <ChoiceSetting
             id="mentionLinkDisplay"
diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js
new file mode 100644
index 00000000..4f95122a
--- /dev/null
+++ b/src/components/thread_tree/thread_tree.js
@@ -0,0 +1,52 @@
+import Status from '../status/status.vue'
+
+const debug = console.log
+
+const ThreadTree = {
+  components: {
+    Status
+  },
+  name: 'ThreadTree',
+  props: {
+    depth: Number,
+    status: Object,
+    inProfile: Boolean,
+    conversation: Array,
+    collapsable: Boolean,
+    isExpanded: Boolean,
+    pinnedStatusIdsObject: Object,
+    profileUserId: String,
+
+    focused: Function,
+    getHighlight: Function,
+    getReplies: Function,
+    setHighlight: Function,
+    toggleExpanded: Function
+  },
+  computed: {
+    reverseLookupTable () {
+      return this.conversation.reduce((table, status, index) => {
+        table[status.id] = index
+        return table
+      }, {})
+    },
+    currentReplies () {
+      debug('status:', this.status)
+      debug('getReplies:', this.getReplies(this.status.id))
+      return this.getReplies(this.status.id).map(({ id }) => this.statusById(id))
+    },
+  },
+  methods: {
+    statusById (id) {
+      return this.conversation[this.reverseLookupTable[id]]
+    },
+    collapseThread () {
+    },
+    showThread () {
+    },
+    showAllSubthreads () {
+    }
+  }
+}
+
+export default ThreadTree
diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue
new file mode 100644
index 00000000..8256eee6
--- /dev/null
+++ b/src/components/thread_tree/thread_tree.vue
@@ -0,0 +1,55 @@
+<template>
+  <div class="thread-tree panel-body">
+    <status
+      :key="status.id"
+      ref="statusComponent"
+      :inline-expanded="collapsable && isExpanded"
+      :statusoid="status"
+      :expandable="!isExpanded"
+      :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
+      :focused="focused(status.id)"
+      :in-conversation="isExpanded"
+      :highlight="getHighlight()"
+      :replies="getReplies(status.id)"
+      :in-profile="inProfile"
+      :profile-user-id="profileUserId"
+      class="conversation-status status-fadein panel-body"
+      @goto="setHighlight"
+      @toggleExpanded="toggleExpanded"
+    />
+    <div
+      v-if="currentReplies.length"
+      class="thread-tree-replies"
+    >
+      <thread-tree
+        v-for="replyStatus in currentReplies"
+        :key="replyStatus.id"
+        ref="childComponent"
+        :status="replyStatus"
+
+        :in-profile="inProfile"
+        :conversation="conversation"
+        :collapsable="collapsable"
+        :is-expanded="isExpanded"
+        :pinned-status-ids-object="pinnedStatusIdsObject"
+        :profile-user-id="profileUserId"
+
+        :focused="focused"
+        :get-replies="getReplies"
+        :get-highlight="getHighlight"
+        :set-highlight="setHighlight"
+        :toggle-expanded="toggleExpanded"
+
+        class="conversation-status status-fadein panel-body"
+      />
+    </div>
+  </div>
+</template>
+
+<script src="./thread_tree.js"></script>
+
+<style lang="scss">
+.thread-tree-replies {
+  margin-left: 1em;
+}
+</style>
diff --git a/src/modules/config.js b/src/modules/config.js
index 20979174..ec75dbfb 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -12,6 +12,7 @@ const browserLocale = (window.navigator.language || 'en').split('-')[0]
 export const multiChoiceProperties = [
   'postContentType',
   'subjectLineBehavior',
+  'conversationDisplay', // tree | linear
   'mentionLinkDisplay' // short | full_for_remote | full
 ]
 
@@ -81,7 +82,8 @@ export const defaultState = {
   hidePostStats: undefined, // instance default
   hideUserStats: undefined, // instance default
   virtualScrolling: undefined, // instance default
-  sensitiveByDefault: undefined // instance default
+  sensitiveByDefault: undefined, // instance default
+  conversationDisplay: undefined // instance default
 }
 
 // caching the instance default properties
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 1abd784f..a160a557 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -53,6 +53,7 @@ const defaultState = {
   theme: 'pleroma-dark',
   virtualScrolling: true,
   sensitiveByDefault: false,
+  conversationDisplay: 'tree',
 
   // Nasty stuff
   customEmoji: [],