diff --git a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx
new file mode 100644
index 0000000000..9970e27d06
--- /dev/null
+++ b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx
@@ -0,0 +1,1057 @@
+import { useCallback, useMemo } from 'react';
+
+import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
+
+import classNames from 'classnames';
+import { Helmet } from 'react-helmet';
+import { NavLink } from 'react-router-dom';
+
+import { useLinks } from '@/hooks/useLinks';
+import CheckIcon from '@/material-icons/400-24px/check.svg?react';
+import LockIcon from '@/material-icons/400-24px/lock.svg?react';
+import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
+import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react';
+import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
+import ShareIcon from '@/material-icons/400-24px/share.svg?react';
+import {
+  followAccount,
+  unblockAccount,
+  unmuteAccount,
+  pinAccount,
+  unpinAccount,
+} from 'mastodon/actions/accounts';
+import { initBlockModal } from 'mastodon/actions/blocks';
+import { mentionCompose, directCompose } from 'mastodon/actions/compose';
+import {
+  initDomainBlockModal,
+  unblockDomain,
+} from 'mastodon/actions/domain_blocks';
+import { openModal } from 'mastodon/actions/modal';
+import { initMuteModal } from 'mastodon/actions/mutes';
+import { initReport } from 'mastodon/actions/reports';
+import { Avatar } from 'mastodon/components/avatar';
+import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge';
+import { Button } from 'mastodon/components/button';
+import { CopyIconButton } from 'mastodon/components/copy_icon_button';
+import {
+  FollowersCounter,
+  FollowingCounter,
+  StatusesCounter,
+} from 'mastodon/components/counters';
+import { Icon } from 'mastodon/components/icon';
+import { IconButton } from 'mastodon/components/icon_button';
+import { LoadingIndicator } from 'mastodon/components/loading_indicator';
+import { ShortNumber } from 'mastodon/components/short_number';
+import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
+import { DomainPill } from 'mastodon/features/account/components/domain_pill';
+import AccountNoteContainer from 'mastodon/features/account/containers/account_note_container';
+import FollowRequestNoteContainer from 'mastodon/features/account/containers/follow_request_note_container';
+import { useIdentity } from 'mastodon/identity_context';
+import { autoPlayGif, me, domain as localDomain } from 'mastodon/initial_state';
+import type { Account } from 'mastodon/models/account';
+import type { DropdownMenu } from 'mastodon/models/dropdown_menu';
+import type { Relationship } from 'mastodon/models/relationship';
+import {
+  PERMISSION_MANAGE_USERS,
+  PERMISSION_MANAGE_FEDERATION,
+} from 'mastodon/permissions';
+import { getAccountHidden } from 'mastodon/selectors/accounts';
+import { useAppSelector, useAppDispatch } from 'mastodon/store';
+
+import MemorialNote from './memorial_note';
+import MovedNote from './moved_note';
+
+const messages = defineMessages({
+  unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
+  follow: { id: 'account.follow', defaultMessage: 'Follow' },
+  followBack: { id: 'account.follow_back', defaultMessage: 'Follow back' },
+  mutual: { id: 'account.mutual', defaultMessage: 'Mutual' },
+  cancel_follow_request: {
+    id: 'account.cancel_follow_request',
+    defaultMessage: 'Withdraw follow request',
+  },
+  requested: {
+    id: 'account.requested',
+    defaultMessage: 'Awaiting approval. Click to cancel follow request',
+  },
+  unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
+  edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
+  linkVerifiedOn: {
+    id: 'account.link_verified_on',
+    defaultMessage: 'Ownership of this link was checked on {date}',
+  },
+  account_locked: {
+    id: 'account.locked_info',
+    defaultMessage:
+      'This account privacy status is set to locked. The owner manually reviews who can follow them.',
+  },
+  mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
+  direct: { id: 'account.direct', defaultMessage: 'Privately mention @{name}' },
+  unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
+  block: { id: 'account.block', defaultMessage: 'Block @{name}' },
+  mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
+  report: { id: 'account.report', defaultMessage: 'Report @{name}' },
+  share: { id: 'account.share', defaultMessage: "Share @{name}'s profile" },
+  copy: { id: 'account.copy', defaultMessage: 'Copy link to profile' },
+  media: { id: 'account.media', defaultMessage: 'Media' },
+  blockDomain: {
+    id: 'account.block_domain',
+    defaultMessage: 'Block domain {domain}',
+  },
+  unblockDomain: {
+    id: 'account.unblock_domain',
+    defaultMessage: 'Unblock domain {domain}',
+  },
+  hideReblogs: {
+    id: 'account.hide_reblogs',
+    defaultMessage: 'Hide boosts from @{name}',
+  },
+  showReblogs: {
+    id: 'account.show_reblogs',
+    defaultMessage: 'Show boosts from @{name}',
+  },
+  enableNotifications: {
+    id: 'account.enable_notifications',
+    defaultMessage: 'Notify me when @{name} posts',
+  },
+  disableNotifications: {
+    id: 'account.disable_notifications',
+    defaultMessage: 'Stop notifying me when @{name} posts',
+  },
+  pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' },
+  preferences: {
+    id: 'navigation_bar.preferences',
+    defaultMessage: 'Preferences',
+  },
+  follow_requests: {
+    id: 'navigation_bar.follow_requests',
+    defaultMessage: 'Follow requests',
+  },
+  favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
+  lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
+  followed_tags: {
+    id: 'navigation_bar.followed_tags',
+    defaultMessage: 'Followed hashtags',
+  },
+  blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
+  domain_blocks: {
+    id: 'navigation_bar.domain_blocks',
+    defaultMessage: 'Blocked domains',
+  },
+  mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
+  endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' },
+  unendorse: {
+    id: 'account.unendorse',
+    defaultMessage: "Don't feature on profile",
+  },
+  add_or_remove_from_list: {
+    id: 'account.add_or_remove_from_list',
+    defaultMessage: 'Add or Remove from lists',
+  },
+  admin_account: {
+    id: 'status.admin_account',
+    defaultMessage: 'Open moderation interface for @{name}',
+  },
+  admin_domain: {
+    id: 'status.admin_domain',
+    defaultMessage: 'Open moderation interface for {domain}',
+  },
+  languages: {
+    id: 'account.languages',
+    defaultMessage: 'Change subscribed languages',
+  },
+  openOriginalPage: {
+    id: 'account.open_original_page',
+    defaultMessage: 'Open original page',
+  },
+});
+
+const titleFromAccount = (account: Account) => {
+  const displayName = account.display_name;
+  const acct =
+    account.acct === account.username
+      ? `${account.username}@${localDomain}`
+      : account.acct;
+  const prefix =
+    displayName.trim().length === 0 ? account.username : displayName;
+
+  return `${prefix} (@${acct})`;
+};
+
+const messageForFollowButton = (relationship?: Relationship) => {
+  if (!relationship) return messages.follow;
+
+  if (relationship.get('following') && relationship.get('followed_by')) {
+    return messages.mutual;
+  } else if (relationship.get('following') || relationship.get('requested')) {
+    return messages.unfollow;
+  } else if (relationship.get('followed_by')) {
+    return messages.followBack;
+  } else {
+    return messages.follow;
+  }
+};
+
+const dateFormatOptions: Intl.DateTimeFormatOptions = {
+  month: 'short',
+  day: 'numeric',
+  year: 'numeric',
+  hour: '2-digit',
+  minute: '2-digit',
+};
+
+export const AccountHeader: React.FC<{
+  accountId: string;
+  hideTabs?: boolean;
+}> = ({ accountId, hideTabs }) => {
+  const dispatch = useAppDispatch();
+  const intl = useIntl();
+  const { signedIn, permissions } = useIdentity();
+  const account = useAppSelector((state) => state.accounts.get(accountId));
+  const relationship = useAppSelector((state) =>
+    state.relationships.get(accountId),
+  );
+  const hidden = useAppSelector((state) => getAccountHidden(state, accountId));
+  const handleLinkClick = useLinks();
+
+  const handleFollow = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    if (relationship?.following || relationship?.requested) {
+      dispatch(
+        openModal({ modalType: 'CONFIRM_UNFOLLOW', modalProps: { account } }),
+      );
+    } else {
+      dispatch(followAccount(account.id));
+    }
+  }, [dispatch, account, relationship]);
+
+  const handleBlock = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    if (relationship?.blocking) {
+      dispatch(unblockAccount(account.id));
+    } else {
+      dispatch(initBlockModal(account));
+    }
+  }, [dispatch, account, relationship]);
+
+  const handleMention = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    dispatch(mentionCompose(account));
+  }, [dispatch, account]);
+
+  const handleDirect = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    dispatch(directCompose(account));
+  }, [dispatch, account]);
+
+  const handleReport = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    dispatch(initReport(account));
+  }, [dispatch, account]);
+
+  const handleReblogToggle = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    if (relationship?.showing_reblogs) {
+      dispatch(followAccount(account.id, { reblogs: false }));
+    } else {
+      dispatch(followAccount(account.id, { reblogs: true }));
+    }
+  }, [dispatch, account, relationship]);
+
+  const handleNotifyToggle = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    if (relationship?.notifying) {
+      dispatch(followAccount(account.id, { notify: false }));
+    } else {
+      dispatch(followAccount(account.id, { notify: true }));
+    }
+  }, [dispatch, account, relationship]);
+
+  const handleMute = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    if (relationship?.muting) {
+      dispatch(unmuteAccount(account.id));
+    } else {
+      dispatch(initMuteModal(account));
+    }
+  }, [dispatch, account, relationship]);
+
+  const handleBlockDomain = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    dispatch(initDomainBlockModal(account));
+  }, [dispatch, account]);
+
+  const handleUnblockDomain = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    const domain = account.acct.split('@')[1];
+
+    if (!domain) {
+      return;
+    }
+
+    dispatch(unblockDomain(domain));
+  }, [dispatch, account]);
+
+  const handleEndorseToggle = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    if (relationship?.endorsed) {
+      dispatch(unpinAccount(account.id));
+    } else {
+      dispatch(pinAccount(account.id));
+    }
+  }, [dispatch, account, relationship]);
+
+  const handleAddToList = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    dispatch(
+      openModal({
+        modalType: 'LIST_ADDER',
+        modalProps: {
+          accountId: account.id,
+        },
+      }),
+    );
+  }, [dispatch, account]);
+
+  const handleChangeLanguages = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    dispatch(
+      openModal({
+        modalType: 'SUBSCRIBED_LANGUAGES',
+        modalProps: {
+          accountId: account.id,
+        },
+      }),
+    );
+  }, [dispatch, account]);
+
+  const handleInteractionModal = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    dispatch(
+      openModal({
+        modalType: 'INTERACTION',
+        modalProps: {
+          type: 'follow',
+          accountId: account.id,
+          url: account.uri,
+        },
+      }),
+    );
+  }, [dispatch, account]);
+
+  const handleOpenAvatar = useCallback(
+    (e: React.MouseEvent) => {
+      if (e.button !== 0 || e.ctrlKey || e.metaKey) {
+        return;
+      }
+
+      e.preventDefault();
+
+      if (!account) {
+        return;
+      }
+
+      dispatch(
+        openModal({
+          modalType: 'IMAGE',
+          modalProps: {
+            src: account.avatar,
+            alt: '',
+          },
+        }),
+      );
+    },
+    [dispatch, account],
+  );
+
+  const handleShare = useCallback(() => {
+    if (!account) {
+      return;
+    }
+
+    void navigator.share({
+      url: account.url,
+    });
+  }, [account]);
+
+  const handleEditProfile = useCallback(() => {
+    window.open('/settings/profile', '_blank');
+  }, []);
+
+  const handleMouseEnter = useCallback(
+    ({ currentTarget }: React.MouseEvent) => {
+      if (autoPlayGif) {
+        return;
+      }
+
+      currentTarget
+        .querySelectorAll
('.custom-emoji')
+        .forEach((emoji) => {
+          emoji.src = emoji.getAttribute('data-original') ?? '';
+        });
+    },
+    [],
+  );
+
+  const handleMouseLeave = useCallback(
+    ({ currentTarget }: React.MouseEvent) => {
+      if (autoPlayGif) {
+        return;
+      }
+
+      currentTarget
+        .querySelectorAll('.custom-emoji')
+        .forEach((emoji) => {
+          emoji.src = emoji.getAttribute('data-static') ?? '';
+        });
+    },
+    [],
+  );
+
+  const suspended = account?.suspended;
+  const isRemote = account?.acct !== account?.username;
+  const remoteDomain = isRemote ? account?.acct.split('@')[1] : null;
+
+  const menu = useMemo(() => {
+    const arr: DropdownMenu = [];
+
+    if (!account) {
+      return arr;
+    }
+
+    if (signedIn && account.id !== me && !account.suspended) {
+      arr.push({
+        text: intl.formatMessage(messages.mention, {
+          name: account.username,
+        }),
+        action: handleMention,
+      });
+      arr.push({
+        text: intl.formatMessage(messages.direct, {
+          name: account.username,
+        }),
+        action: handleDirect,
+      });
+      arr.push(null);
+    }
+
+    if (isRemote) {
+      arr.push({
+        text: intl.formatMessage(messages.openOriginalPage),
+        href: account.url,
+      });
+      arr.push(null);
+    }
+
+    if (account.id === me) {
+      arr.push({
+        text: intl.formatMessage(messages.edit_profile),
+        href: '/settings/profile',
+      });
+      arr.push({
+        text: intl.formatMessage(messages.preferences),
+        href: '/settings/preferences',
+      });
+      arr.push({ text: intl.formatMessage(messages.pins), to: '/pinned' });
+      arr.push(null);
+      arr.push({
+        text: intl.formatMessage(messages.follow_requests),
+        to: '/follow_requests',
+      });
+      arr.push({
+        text: intl.formatMessage(messages.favourites),
+        to: '/favourites',
+      });
+      arr.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
+      arr.push({
+        text: intl.formatMessage(messages.followed_tags),
+        to: '/followed_tags',
+      });
+      arr.push(null);
+      arr.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
+      arr.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
+      arr.push({
+        text: intl.formatMessage(messages.domain_blocks),
+        to: '/domain_blocks',
+      });
+    } else if (signedIn) {
+      if (relationship?.following) {
+        if (!relationship.muting) {
+          if (relationship.showing_reblogs) {
+            arr.push({
+              text: intl.formatMessage(messages.hideReblogs, {
+                name: account.username,
+              }),
+              action: handleReblogToggle,
+            });
+          } else {
+            arr.push({
+              text: intl.formatMessage(messages.showReblogs, {
+                name: account.username,
+              }),
+              action: handleReblogToggle,
+            });
+          }
+
+          arr.push({
+            text: intl.formatMessage(messages.languages),
+            action: handleChangeLanguages,
+          });
+          arr.push(null);
+        }
+
+        arr.push({
+          text: intl.formatMessage(
+            account.getIn(['relationship', 'endorsed'])
+              ? messages.unendorse
+              : messages.endorse,
+          ),
+          action: handleEndorseToggle,
+        });
+        arr.push({
+          text: intl.formatMessage(messages.add_or_remove_from_list),
+          action: handleAddToList,
+        });
+        arr.push(null);
+      }
+
+      if (relationship?.muting) {
+        arr.push({
+          text: intl.formatMessage(messages.unmute, {
+            name: account.username,
+          }),
+          action: handleMute,
+        });
+      } else {
+        arr.push({
+          text: intl.formatMessage(messages.mute, {
+            name: account.username,
+          }),
+          action: handleMute,
+          dangerous: true,
+        });
+      }
+
+      if (relationship?.blocking) {
+        arr.push({
+          text: intl.formatMessage(messages.unblock, {
+            name: account.username,
+          }),
+          action: handleBlock,
+        });
+      } else {
+        arr.push({
+          text: intl.formatMessage(messages.block, {
+            name: account.username,
+          }),
+          action: handleBlock,
+          dangerous: true,
+        });
+      }
+
+      if (!account.suspended) {
+        arr.push({
+          text: intl.formatMessage(messages.report, {
+            name: account.username,
+          }),
+          action: handleReport,
+          dangerous: true,
+        });
+      }
+    }
+
+    if (signedIn && isRemote) {
+      arr.push(null);
+
+      if (relationship?.domain_blocking) {
+        arr.push({
+          text: intl.formatMessage(messages.unblockDomain, {
+            domain: remoteDomain,
+          }),
+          action: handleUnblockDomain,
+        });
+      } else {
+        arr.push({
+          text: intl.formatMessage(messages.blockDomain, {
+            domain: remoteDomain,
+          }),
+          action: handleBlockDomain,
+          dangerous: true,
+        });
+      }
+    }
+
+    if (
+      (account.id !== me &&
+        (permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) ||
+      (isRemote &&
+        (permissions & PERMISSION_MANAGE_FEDERATION) ===
+          PERMISSION_MANAGE_FEDERATION)
+    ) {
+      arr.push(null);
+      if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
+        arr.push({
+          text: intl.formatMessage(messages.admin_account, {
+            name: account.username,
+          }),
+          href: `/admin/accounts/${account.id}`,
+        });
+      }
+      if (
+        isRemote &&
+        (permissions & PERMISSION_MANAGE_FEDERATION) ===
+          PERMISSION_MANAGE_FEDERATION
+      ) {
+        arr.push({
+          text: intl.formatMessage(messages.admin_domain, {
+            domain: remoteDomain,
+          }),
+          href: `/admin/instances/${remoteDomain}`,
+        });
+      }
+    }
+
+    return arr;
+  }, [
+    account,
+    relationship,
+    permissions,
+    isRemote,
+    remoteDomain,
+    intl,
+    signedIn,
+    handleAddToList,
+    handleBlock,
+    handleBlockDomain,
+    handleChangeLanguages,
+    handleDirect,
+    handleEndorseToggle,
+    handleMention,
+    handleMute,
+    handleReblogToggle,
+    handleReport,
+    handleUnblockDomain,
+  ]);
+
+  if (!account) {
+    return null;
+  }
+
+  let actionBtn, bellBtn, lockedIcon, shareBtn;
+
+  const info = [];
+
+  if (me !== account.id && relationship?.blocking) {
+    info.push(
+      
+        
+      ,
+    );
+  }
+
+  if (me !== account.id && relationship?.muting) {
+    info.push(
+      
+        
+      ,
+    );
+  } else if (me !== account.id && relationship?.domain_blocking) {
+    info.push(
+      
+        
+      ,
+    );
+  }
+
+  if (relationship?.requested || relationship?.following) {
+    bellBtn = (
+      
+    );
+  }
+
+  if ('share' in navigator) {
+    shareBtn = (
+      
+    );
+  } else {
+    shareBtn = (
+      
+    );
+  }
+
+  if (me !== account.id) {
+    if (signedIn && !relationship) {
+      // Wait until the relationship is loaded
+      actionBtn = (
+        
+      );
+    } else if (!relationship?.blocking) {
+      actionBtn = (
+        
+      );
+    } else {
+      actionBtn = (
+        
+      );
+    }
+  } else {
+    actionBtn = (
+      
+    );
+  }
+
+  if (account.moved && !relationship?.following) {
+    actionBtn = '';
+  }
+
+  if (account.locked) {
+    lockedIcon = (
+      
+    );
+  }
+
+  const content = { __html: account.note_emojified };
+  const displayNameHtml = { __html: account.display_name_html };
+  const fields = account.fields;
+  const isLocal = !account.acct.includes('@');
+  const username = account.acct.split('@')[0];
+  const domain = isLocal ? localDomain : account.acct.split('@')[1];
+  const isIndexable = !account.noindex;
+
+  const badges = [];
+
+  if (account.bot) {
+    badges.push();
+  } else if (account.group) {
+    badges.push();
+  }
+
+  account.get('roles', []).forEach((role) => {
+    badges.push(
+      {role.get('name')}}
+        domain={domain}
+        roleId={role.get('id')}
+      />,
+    );
+  });
+
+  return (
+    
+      {!hidden && account.memorial && 
}
+      {!hidden && account.moved && (
+        
+      )}
+
+      
+        {!(suspended || hidden || account.moved) &&
+          relationship?.requested_by && (
+            
+          )}
+
+        
+          
{info}
+
+          {!(suspended || hidden) && (
+            

+          )}
+        
+
+        
+          
+            
+              
+            
+
+            
+              {!hidden && bellBtn}
+              {!hidden && shareBtn}
+              
+              {!hidden && actionBtn}
+            
+          
+
+          
+            
+              
+              
+                
+                  @{username}
+                  @{domain}
+                
+                
+                {lockedIcon}
+              
+            
+          
+
+          {badges.length > 0 && (
+            
{badges}
+          )}
+
+          {!(suspended || hidden) && (
+            
+              
+                {account.id !== me && signedIn && (
+                  
+                )}
+
+                {account.note.length > 0 && account.note !== '
' && (
+                  
+                )}
+
+                
+                  
+                    - 
+                      
+                    +
- 
+                      {intl.formatDate(account.created_at, {
+                        year: 'numeric',
+                        month: 'short',
+                        day: '2-digit',
+                      })}
+                    +
+
+                  {fields.map((pair, i) => (
+                    
+                      
+
+                      - 
+                        {pair.verified_at && (
+                          
+                            
+                          
+                        )}{' '}
+                        
+                      +
+                  ))}
+                
+              
+
+              
+                
+                  
+                
+
+                
+                  
+                
+
+                
+                  
+                
+              
+            
+          )}
+        
+      
+
+      {!(hideTabs || hidden) && (
+        
+          
+            
+          
+          
+            
+          
+          
+            
+          
+        
+      )}
+
+      
+        {titleFromAccount(account)}
+        
+        
+      
+    
-        {(!hidden && account.get('memorial')) && 
}
-        {(!hidden && account.get('moved')) && 
}
-
-        
-
-        {!(hideTabs || hidden) && (
-          
-            
-            
-            
-          
-        )}
-