diff --git a/src/routes/_api/timelines.js b/src/routes/_api/timelines.js
index 1a9c5d67..7fc1c727 100644
--- a/src/routes/_api/timelines.js
+++ b/src/routes/_api/timelines.js
@@ -27,11 +27,11 @@ export function getTimeline (instanceName, accessToken, timeline, maxId, since,
   let url = `${basename(instanceName)}/api/v1/${timelineUrlName}`
 
   if (timeline.startsWith('tag/')) {
-    url += '/' + timeline.split('/').slice(-1)[0]
+    url += '/' + timeline.split('/')[1]
   } else if (timeline.startsWith('account/')) {
-    url += '/' + timeline.split('/').slice(-1)[0] + '/statuses'
+    url += '/' + timeline.split('/')[1] + '/statuses'
   } else if (timeline.startsWith('list/')) {
-    url += '/' + timeline.split('/').slice(-1)[0]
+    url += '/' + timeline.split('/')[1]
   }
 
   let params = {}
@@ -51,6 +51,14 @@ export function getTimeline (instanceName, accessToken, timeline, maxId, since,
     params.local = true
   }
 
+  if (timeline.startsWith('account/')) {
+    if (timeline.endsWith('media')) {
+      params.only_media = true
+    } else {
+      params.exclude_replies = !timeline.endsWith('/with_replies')
+    }
+  }
+
   url += '?' + paramsString(params)
 
   return get(url, auth(accessToken), { timeout: DEFAULT_TIMEOUT })
diff --git a/src/routes/_components/profile/AccountProfileFilters.html b/src/routes/_components/profile/AccountProfileFilters.html
new file mode 100644
index 00000000..49c291ec
--- /dev/null
+++ b/src/routes/_components/profile/AccountProfileFilters.html
@@ -0,0 +1,107 @@
+<nav aria-label="Filters" class="account-profile-filters">
+  <ul>
+    {#each filterTabs as filterTab (filterTab.href)}
+      <li class="{filter === filterTab.filter ? 'current-filter' : 'not-current-filter'}">
+        <a aria-label="{filterTab.label} { filter === filterTab.filter ? '(Current)' : ''}"
+           href={filterTab.href}
+           rel="prefetch">
+          {filterTab.label}
+        </a>
+      </li>
+    {/each}
+  </ul>
+</nav>
+<style>
+  li {
+    flex: 1;
+    text-align: center;
+  }
+
+  /* reset */
+  ul, li {
+    margin: 0;
+    padding: 0;
+  }
+
+  ul {
+    list-style: none;
+    display: flex;
+    margin: 5px 0;
+    box-sizing: border-box;
+  }
+
+  li {
+    border: 1px solid var(--main-border);
+    box-sizing: border-box;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border-top-left-radius: 10px;
+    border-top-right-radius: 10px;
+    background: var(--tab-bg);
+  }
+
+  li:not(:first-child) {
+    border-left: none;
+  }
+
+  li:hover {
+    background: var(--button-bg-hover);
+  }
+
+  li.not-current-filter {
+    background: var(--tab-bg-non-selected);
+  }
+
+  li.current-filter {
+    border-bottom: none;
+  }
+
+  li.current-filter:hover {
+    background: var(--tab-bg-hover);
+  }
+
+  li.not-current-filter:hover {
+    background: var(--tab-bg-hover-non-selected);
+  }
+
+  li:active {
+    background: var(--tab-bg-active);
+  }
+
+  a {
+    padding: 10px;
+    color: var(--body-text-color);
+    font-size: 1.1em;
+    flex: 1;
+  }
+
+  a:hover {
+    text-decoration: none;
+  }
+</style>
+<script>
+  export default {
+    computed: {
+      filterTabs: ({ account }) => (
+        [
+          {
+            filter: '',
+            label: 'Toots',
+            href: `/accounts/${account.id}`
+          },
+          {
+            filter: 'with_replies',
+            label: 'Toots and replies',
+            href: `/accounts/${account.id}/with_replies`
+          },
+          {
+            filter: 'media',
+            label: 'Media',
+            href: `/accounts/${account.id}/media`
+          }
+        ]
+      )
+    }
+  }
+</script>
diff --git a/src/routes/_components/profile/AccountProfilePage.html b/src/routes/_components/profile/AccountProfilePage.html
new file mode 100644
index 00000000..6f4ce2c4
--- /dev/null
+++ b/src/routes/_components/profile/AccountProfilePage.html
@@ -0,0 +1,66 @@
+{#if $isUserLoggedIn}
+<TimelinePage {timeline} >
+  <DynamicPageBanner title="" ariaTitle="Profile page for {accountName}"/>
+  {#if $currentAccountProfile && $currentVerifyCredentials}
+  <AccountProfile account={$currentAccountProfile}
+                  relationship={$currentAccountRelationship}
+                  verifyCredentials={$currentVerifyCredentials}
+  />
+  <AccountProfileFilters account={$currentAccountProfile} {filter} />
+  {/if}
+  {#if !filter}
+    <PinnedStatuses {accountId} />
+  {/if}
+</TimelinePage>
+{:else}
+<HiddenFromSSR>
+  <FreeTextLayout>
+    <h1>Profile</h1>
+
+    <p>A user timeline will appear here when logged in.</p>
+  </FreeTextLayout>
+</HiddenFromSSR>
+{/if}
+<script>
+  import TimelinePage from '../TimelinePage.html'
+  import FreeTextLayout from '../FreeTextLayout.html'
+  import { store } from '../../_store/store.js'
+  import HiddenFromSSR from '../HiddenFromSSR'
+  import DynamicPageBanner from '../DynamicPageBanner.html'
+  import { updateProfileAndRelationship, clearProfileAndRelationship } from '../../_actions/accounts'
+  import AccountProfile from './AccountProfile.html'
+  import PinnedStatuses from '../timeline/PinnedStatuses.html'
+  import AccountProfileFilters from './AccountProfileFilters.html'
+
+  export default {
+    oncreate () {
+      let { accountId } = this.get()
+      clearProfileAndRelationship()
+      updateProfileAndRelationship(accountId)
+    },
+    store: () => store,
+    computed: {
+      profileName: ({ $currentAccountProfile }) => {
+        return ($currentAccountProfile && ('@' + $currentAccountProfile.acct)) || ''
+      },
+      shortProfileName: ({ $currentAccountProfile }) => {
+        return ($currentAccountProfile && ('@' + $currentAccountProfile.username)) || ''
+      },
+      accountName: ({ $currentAccountProfile }) => {
+        return ($currentAccountProfile && ($currentAccountProfile.display_name || $currentAccountProfile.username)) || ''
+      },
+      timeline: ({ accountId, filter }) => (
+        `account/${accountId}` + (filter ? `/${filter}` : '')
+      )
+    },
+    components: {
+      TimelinePage,
+      FreeTextLayout,
+      HiddenFromSSR,
+      DynamicPageBanner,
+      AccountProfile,
+      PinnedStatuses,
+      AccountProfileFilters
+    }
+  }
+</script>
diff --git a/src/routes/_pages/accounts/[accountId]/index.html b/src/routes/_pages/accounts/[accountId]/index.html
index 747ecf5c..44da3597 100644
--- a/src/routes/_pages/accounts/[accountId]/index.html
+++ b/src/routes/_pages/accounts/[accountId]/index.html
@@ -1,59 +1,10 @@
-{#if $isUserLoggedIn}
-  <TimelinePage timeline="account/{params.accountId}">
-    <DynamicPageBanner title="" ariaTitle="Profile page for {accountName}"/>
-    {#if $currentAccountProfile && $currentVerifyCredentials}
-    <AccountProfile account={$currentAccountProfile}
-                    relationship={$currentAccountRelationship}
-                    verifyCredentials={$currentVerifyCredentials}
-    />
-    {/if}
-    <PinnedStatuses accountId={params.accountId} />
-  </TimelinePage>
-{:else}
-  <HiddenFromSSR>
-    <FreeTextLayout>
-      <h1>Profile</h1>
-
-      <p>A user timeline will appear here when logged in.</p>
-    </FreeTextLayout>
-  </HiddenFromSSR>
-{/if}
+<AccountProfilePage accountId={params.accountId} filter="" />
 <script>
-  import TimelinePage from '../../../_components/TimelinePage.html'
-  import FreeTextLayout from '../../../_components/FreeTextLayout.html'
-  import { store } from '../../../_store/store.js'
-  import HiddenFromSSR from '../../../_components/HiddenFromSSR'
-  import DynamicPageBanner from '../../../_components/DynamicPageBanner.html'
-  import { updateProfileAndRelationship, clearProfileAndRelationship } from '../../../_actions/accounts'
-  import AccountProfile from '../../../_components/profile/AccountProfile.html'
-  import PinnedStatuses from '../../../_components/timeline/PinnedStatuses.html'
+  import AccountProfilePage from '../../../_components/profile/AccountProfilePage.html'
 
   export default {
-    oncreate () {
-      let { params } = this.get()
-      let { accountId } = params
-      clearProfileAndRelationship()
-      updateProfileAndRelationship(accountId)
-    },
-    store: () => store,
-    computed: {
-      profileName: ({ $currentAccountProfile }) => {
-        return ($currentAccountProfile && ('@' + $currentAccountProfile.acct)) || ''
-      },
-      shortProfileName: ({ $currentAccountProfile }) => {
-        return ($currentAccountProfile && ('@' + $currentAccountProfile.username)) || ''
-      },
-      accountName: ({ $currentAccountProfile }) => {
-        return ($currentAccountProfile && ($currentAccountProfile.display_name || $currentAccountProfile.username)) || ''
-      }
-    },
     components: {
-      TimelinePage,
-      FreeTextLayout,
-      HiddenFromSSR,
-      DynamicPageBanner,
-      AccountProfile,
-      PinnedStatuses
+      AccountProfilePage
     }
   }
 </script>
diff --git a/src/routes/_pages/accounts/[accountId]/media.html b/src/routes/_pages/accounts/[accountId]/media.html
new file mode 100644
index 00000000..3671f6f7
--- /dev/null
+++ b/src/routes/_pages/accounts/[accountId]/media.html
@@ -0,0 +1,10 @@
+<AccountProfilePage accountId={params.accountId} filter="media" />
+<script>
+  import AccountProfilePage from '../../../_components/profile/AccountProfilePage.html'
+
+  export default {
+    components: {
+      AccountProfilePage
+    }
+  }
+</script>
diff --git a/src/routes/_pages/accounts/[accountId]/with_replies.html b/src/routes/_pages/accounts/[accountId]/with_replies.html
new file mode 100644
index 00000000..523954b1
--- /dev/null
+++ b/src/routes/_pages/accounts/[accountId]/with_replies.html
@@ -0,0 +1,10 @@
+<AccountProfilePage accountId={params.accountId} filter="with_replies" />
+<script>
+  import AccountProfilePage from '../../../_components/profile/AccountProfilePage.html'
+
+  export default {
+    components: {
+      AccountProfilePage
+    }
+  }
+</script>
diff --git a/src/routes/_store/computations/timelineComputations.js b/src/routes/_store/computations/timelineComputations.js
index 64a11f18..35afbe32 100644
--- a/src/routes/_store/computations/timelineComputations.js
+++ b/src/routes/_store/computations/timelineComputations.js
@@ -23,9 +23,17 @@ export function timelineComputations (store) {
   store.compute('currentTimelineType', ['currentTimeline'], currentTimeline => (
     currentTimeline && currentTimeline.split('/')[0])
   )
-  store.compute('currentTimelineValue', ['currentTimeline'], currentTimeline => (
-    currentTimeline && currentTimeline.split('/').slice(-1)[0])
-  )
+  store.compute('currentTimelineValue', ['currentTimeline'], currentTimeline => {
+    if (!currentTimeline) {
+      return void 0
+    }
+    let split = currentTimeline.split('/')
+    let len = split.length
+    if (split[len - 1] === 'with_replies' || split[len - 1] === 'media') {
+      return split[len - 2]
+    }
+    return split[len - 1]
+  })
   store.compute('firstTimelineItemId', ['timelineItemSummaries'], (timelineItemSummaries) => (
     getFirstIdFromItemSummaries(timelineItemSummaries)
   ))
diff --git a/src/routes/accounts/[accountId]/media.html b/src/routes/accounts/[accountId]/media.html
new file mode 100644
index 00000000..c5f9e83c
--- /dev/null
+++ b/src/routes/accounts/[accountId]/media.html
@@ -0,0 +1,20 @@
+<Title name="Profile with media" />
+
+  <LazyPage {pageComponent} {params} />
+
+<script>
+  import Title from '../../_components/Title.html'
+  import LazyPage from '../../_components/LazyPage.html'
+  import pageComponent from '../../_pages/accounts/[accountId]/media.html'
+
+  export default {
+    components: {
+
+      Title,
+      LazyPage
+    },
+    data: () => ({
+      pageComponent
+    })
+  }
+</script>
diff --git a/src/routes/accounts/[accountId]/with_replies.html b/src/routes/accounts/[accountId]/with_replies.html
new file mode 100644
index 00000000..da1a8980
--- /dev/null
+++ b/src/routes/accounts/[accountId]/with_replies.html
@@ -0,0 +1,20 @@
+<Title name="Profile with replies" />
+
+  <LazyPage {pageComponent} {params} />
+
+<script>
+  import Title from '../../_components/Title.html'
+  import LazyPage from '../../_components/LazyPage.html'
+  import pageComponent from '../../_pages/accounts/[accountId]/with_replies.html'
+
+  export default {
+    components: {
+
+      Title,
+      LazyPage
+    },
+    data: () => ({
+      pageComponent
+    })
+  }
+</script>
diff --git a/src/scss/themes/_base.scss b/src/scss/themes/_base.scss
index 178fc2ae..b5ee2822 100644
--- a/src/scss/themes/_base.scss
+++ b/src/scss/themes/_base.scss
@@ -100,4 +100,10 @@
   --file-drop-mask: #{rgba(255, 255, 255, 0.8)};
 
   --banner-fill: #{$main-theme-color};
+
+  --tab-bg: #{$main-bg-color};
+  --tab-bg-non-selected: #{darken($main-bg-color, 3%)};
+  --tab-bg-active: #{darken($main-bg-color, 25%)};
+  --tab-bg-hover: #{darken($main-bg-color, 4%)};
+  --tab-bg-hover-non-selected: #{darken($main-bg-color, 7%)};
 }
diff --git a/src/scss/themes/_dark.scss b/src/scss/themes/_dark.scss
index 8612e058..ab6a07f5 100644
--- a/src/scss/themes/_dark.scss
+++ b/src/scss/themes/_dark.scss
@@ -38,4 +38,10 @@
   --settings-list-item-bg-hover: #{lighten($main-bg-color, 3%)};
 
   --banner-fill: #{lighten($main-theme-color, 10%)};
+
+  --tab-bg: #{$main-bg-color};
+  --tab-bg-non-selected: #{darken($main-bg-color, 2%)};
+  --tab-bg-active: #{lighten($main-bg-color, 15%)};
+  --tab-bg-hover: #{lighten($main-bg-color, 1%)};
+  --tab-bg-hover-non-selected: #{darken($main-bg-color, 1%)};
 }
diff --git a/tests/spec/031-account-filters.js b/tests/spec/031-account-filters.js
new file mode 100644
index 00000000..707667dc
--- /dev/null
+++ b/tests/spec/031-account-filters.js
@@ -0,0 +1,31 @@
+import {
+  accountProfileFilterMedia, accountProfileFilterStatuses,
+  accountProfileFilterStatusesAndReplies,
+  avatarInComposeBox,
+  getNthPinnedStatus, getNthStatus,
+  getUrl
+} from '../utils'
+import { loginAsFoobar } from '../roles'
+
+fixture`031-account-filters.js`
+  .page`http://localhost:4002`
+
+test('Basic account filters test', async t => {
+  await loginAsFoobar(t)
+  await t
+    .click(avatarInComposeBox)
+    .expect(getUrl()).contains('/accounts/2')
+    .expect(getNthPinnedStatus(1).innerText).contains('this is unlisted')
+    .expect(getNthStatus(1).innerText).contains('this is unlisted')
+    .click(accountProfileFilterStatusesAndReplies)
+    .expect(getUrl()).contains('/accounts/2/with_replies')
+    .expect(getNthPinnedStatus(1).exists).notOk()
+    .expect(getNthStatus(1).innerText).contains('this is unlisted')
+    .click(accountProfileFilterMedia)
+    .expect(getNthPinnedStatus(1).exists).notOk()
+    .expect(getNthStatus(1).innerText).contains('kitten CW')
+    .click(accountProfileFilterStatuses)
+    .expect(getUrl()).contains('/accounts/2')
+    .expect(getNthPinnedStatus(1).innerText).contains('this is unlisted')
+    .expect(getNthStatus(1).innerText).contains('this is unlisted')
+})
diff --git a/tests/utils.js b/tests/utils.js
index 3a01e01f..31f7cbbf 100644
--- a/tests/utils.js
+++ b/tests/utils.js
@@ -60,6 +60,10 @@ export const composeModalPostPrivacyButton = $('.modal-dialog .compose-box-toolb
 
 export const postPrivacyDialogButtonUnlisted = $('[aria-label="Post privacy dialog"] li:nth-child(2) button')
 
+export const accountProfileFilterStatuses = $('.account-profile-filters li:nth-child(1)')
+export const accountProfileFilterStatusesAndReplies = $('.account-profile-filters li:nth-child(2)')
+export const accountProfileFilterMedia = $('.account-profile-filters li:nth-child(3)')
+
 export function getComposeModalNthMediaAltInput (n) {
   return $(`.modal-dialog .compose-media:nth-child(${n}) .compose-media-alt input`)
 }