diff --git a/app/javascript/mastodon/api_types/accounts.ts b/app/javascript/mastodon/api_types/accounts.ts
index a9464b1a47..4a82a3548d 100644
--- a/app/javascript/mastodon/api_types/accounts.ts
+++ b/app/javascript/mastodon/api_types/accounts.ts
@@ -29,6 +29,7 @@ export interface ApiAccountOtherSettingsJSON {
     | 'mutuals_only'
     | 'block';
   subscription_policy: 'allow' | 'followers_only' | 'block';
+  bluesky: boolean;
 }
 
 export interface ApiServerFeaturesJSON {
diff --git a/app/javascript/mastodon/features/account/components/bluesky_pill.jsx b/app/javascript/mastodon/features/account/components/bluesky_pill.jsx
new file mode 100644
index 0000000000..dabbf61c68
--- /dev/null
+++ b/app/javascript/mastodon/features/account/components/bluesky_pill.jsx
@@ -0,0 +1,61 @@
+import PropTypes from 'prop-types';
+import { useState, useRef, useCallback } from 'react';
+
+import { FormattedMessage } from 'react-intl';
+
+
+import classNames from 'classnames';
+
+import Overlay from 'react-overlays/Overlay';
+
+import BadgeIcon from '@/material-icons/400-24px/badge.svg?react';
+import { Button } from 'mastodon/components/button';
+import { Icon } from 'mastodon/components/icon';
+
+export const BlueskyPill = ({ domain, username, isSelf }) => {
+  const [open, setOpen] = useState(false);
+  const triggerRef = useRef(null);
+
+  const handleClick = useCallback(() => {
+    setOpen(!open);
+  }, [open, setOpen]);
+
+  const handleBlueskyProfileOpenClick = useCallback(() => {
+    window.open(`https://bsky.app/profile/${replacedUsername}.${replacedDomain}.ap.brid.gy`, '_blank');
+  });
+
+  const replacedUsername = username.replaceAll('_', '-');
+  const replacedDomain = domain.replaceAll('_', '-');
+
+  return (
+    <>
+      <button className={classNames('account__domain-pill', { active: open })} ref={triggerRef} onClick={handleClick}>bluesky</button>
+
+      <Overlay show={open} rootClose onHide={handleClick} offset={[5, 5]} target={triggerRef}>
+        {({ props }) => (
+          <div {...props} className='account__domain-pill__popout dropdown-animation'>
+            <div className='account__domain-pill__popout__header'>
+              <div className='account__domain-pill__popout__header__icon'><Icon icon={BadgeIcon} /></div>
+              <h3><FormattedMessage id='bluesky_pill.account_available' defaultMessage='Bluesky account is available' /></h3>
+            </div>
+
+            <div className='account__domain-pill__popout__handle'>
+              <div className='account__domain-pill__popout__handle__label'>{isSelf ? <FormattedMessage id='bluesky_pill.your_handle' defaultMessage='Your bluesky account:' /> : <FormattedMessage id='bluesky_pill.their_handle' defaultMessage='Their bluesky account:' />}</div>
+              <div className='account__domain-pill__popout__handle__handle'>@{replacedUsername}.{replacedDomain}.ap.brid.gy</div>
+            </div>
+
+            <p>{isSelf ? <FormattedMessage id='bluesky_pill.who_you_are' defaultMessage='You can share your Mastodon account from Bluesky using the above user ID. However, please note that this is actually a bridge connection and there are many restrictions, such as only public posts will be shared.' /> : <FormattedMessage id='bluesky_pill.who_they_are' defaultMessage='You can follow this Mastodon account from Bluesky using the above user ID. However, please note that this is actually a bridge connection and there are many restrictions, such as only public posts will be shared.' />}</p>
+
+            <p><Button onClick={handleBlueskyProfileOpenClick}><FormattedMessage id='bluesky_pill.jump_bluesky_profile' defaultMessage='Jump Bluesky profile page' /></Button></p>
+          </div>
+        )}
+      </Overlay>
+    </>
+  );
+};
+
+BlueskyPill.propTypes = {
+  username: PropTypes.string.isRequired,
+  domain: PropTypes.string.isRequired,
+  isSelf: PropTypes.bool,
+};
diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx
index f2fb646df8..132ebb838e 100644
--- a/app/javascript/mastodon/features/account/components/header.jsx
+++ b/app/javascript/mastodon/features/account/components/header.jsx
@@ -34,6 +34,7 @@ import { WithRouterPropTypes } from 'mastodon/utils/react_router';
 import AccountNoteContainer from '../containers/account_note_container';
 import FollowRequestNoteContainer from '../containers/follow_request_note_container';
 
+import { BlueskyPill } from './bluesky_pill';
 import { DomainPill } from './domain_pill';
 
 const messages = defineMessages({
@@ -468,6 +469,7 @@ class Header extends ImmutablePureComponent {
               <small>
                 <span>@{username}<span className='invisible'>@{domain}</span></span>
                 <DomainPill username={username} domain={domain} isSelf={me === account.get('id')} />
+                {account.getIn(['other_settings', 'bluesky']) && <BlueskyPill username={username} domain={domain} isSelf={me === account.get('id')} />}
                 {lockedIcon}
               </small>
             </h1>
diff --git a/app/javascript/mastodon/models/account.ts b/app/javascript/mastodon/models/account.ts
index 6681b9df12..4a9bba4c26 100644
--- a/app/javascript/mastodon/models/account.ts
+++ b/app/javascript/mastodon/models/account.ts
@@ -60,6 +60,7 @@ const AccountOtherSettingsFactory = ImmutableRecord<AccountOtherSettingsShape>({
   allow_quote: true,
   emoji_reaction_policy: 'allow',
   subscription_policy: 'allow',
+  bluesky: false,
 });
 
 // ServerFeatures
diff --git a/app/models/concerns/account/interactions.rb b/app/models/concerns/account/interactions.rb
index 979b2278dc..2566b7ea39 100644
--- a/app/models/concerns/account/interactions.rb
+++ b/app/models/concerns/account/interactions.rb
@@ -220,6 +220,11 @@ module Account::Interactions
     following?(other_account) && followed_by?(other_account)
   end
 
+  def bluesky_connected?
+    bridge_account = Account.find_by(domain: 'bsky.brid.gy', username: 'bsky.brid.gy')
+    bridge_account && followed_by?(bridge_account)
+  end
+
   def blocking?(other_account)
     block_relationships.exists?(target_account: other_account)
   end
diff --git a/app/models/concerns/account/other_settings.rb b/app/models/concerns/account/other_settings.rb
index 7968f857ff..24b343ea48 100644
--- a/app/models/concerns/account/other_settings.rb
+++ b/app/models/concerns/account/other_settings.rb
@@ -79,6 +79,13 @@ module Account::OtherSettings
       show_emoji_reaction?(account)
     end
 
+    def bluesky_enabled?
+      return bluesky_connected? if local? && !suspended?
+      return settings['bluesky'] if settings.present? && settings.key?('bluesky')
+
+      false
+    end
+
     def public_settings
       # Please update `app/javascript/mastodon/api_types/accounts.ts` when making changes to the attributes
       {
@@ -90,6 +97,7 @@ module Account::OtherSettings
         'translatable_private' => translatable_private?,
         'allow_quote' => allow_quote?,
         'emoji_reaction_policy' => Setting.enable_emoji_reaction ? emoji_reaction_policy.to_s : 'block',
+        'bluesky' => bluesky_enabled?,
       }
     end