diff --git a/lib/utils/farcaster-utils.js b/lib/utils/farcaster-utils.js
--- a/lib/utils/farcaster-utils.js
+++ b/lib/utils/farcaster-utils.js
@@ -1,7 +1,12 @@
 // @flow
 
+import invariant from 'invariant';
+import * as React from 'react';
+
+import { setSyncedMetadataEntryActionType } from '../actions/synced-metadata-actions.js';
+import { IdentityClientContext } from '../shared/identity-client-context.js';
 import { syncedMetadataNames } from '../types/synced-metadata-types.js';
-import { useSelector } from '../utils/redux-utils.js';
+import { useSelector, useDispatch } from '../utils/redux-utils.js';
 
 function useCurrentUserFID(): ?string {
   return useSelector(
@@ -12,4 +17,29 @@
   );
 }
 
-export { useCurrentUserFID };
+function useLinkFID(): (fid: string) => Promise<void> {
+  const identityClientContext = React.useContext(IdentityClientContext);
+  invariant(identityClientContext, 'identityClientContext should be set');
+
+  const { identityClient } = identityClientContext;
+  const { linkFarcasterAccount } = identityClient;
+
+  const dispatch = useDispatch();
+
+  return React.useCallback(
+    async (fid: string) => {
+      await linkFarcasterAccount(fid);
+
+      dispatch({
+        type: setSyncedMetadataEntryActionType,
+        payload: {
+          name: syncedMetadataNames.CURRENT_USER_FID,
+          data: fid,
+        },
+      });
+    },
+    [dispatch, linkFarcasterAccount],
+  );
+}
+
+export { useCurrentUserFID, useLinkFID };
diff --git a/native/components/connect-farcaster-bottom-sheet.react.js b/native/components/connect-farcaster-bottom-sheet.react.js
--- a/native/components/connect-farcaster-bottom-sheet.react.js
+++ b/native/components/connect-farcaster-bottom-sheet.react.js
@@ -5,11 +5,8 @@
 import { View, StyleSheet } from 'react-native';
 import { useSafeAreaInsets } from 'react-native-safe-area-context';
 
-import { setSyncedMetadataEntryActionType } from 'lib/actions/synced-metadata-actions.js';
 import { useIsAppForegrounded } from 'lib/shared/lifecycle-utils.js';
-import { syncedMetadataNames } from 'lib/types/synced-metadata-types.js';
 import { useCurrentUserFID } from 'lib/utils/farcaster-utils.js';
-import { useDispatch } from 'lib/utils/redux-utils.js';
 
 import FarcasterPrompt from './farcaster-prompt.react.js';
 import FarcasterWebView, {
@@ -20,6 +17,7 @@
 import BottomSheet from '../bottom-sheet/bottom-sheet.react.js';
 import type { RootNavigationProp } from '../navigation/root-navigator.react.js';
 import type { NavigationRoute } from '../navigation/route-names.js';
+import { useTryLinkFID } from '../utils/farcaster-utils.js';
 
 const bottomSheetPaddingTop = 32;
 const farcasterPromptHeight = 350;
@@ -34,25 +32,30 @@
 function ConnectFarcasterBottomSheet(props: Props): React.Node {
   const { navigation } = props;
 
-  const dispatch = useDispatch();
+  const { goBack } = navigation;
+
+  const [webViewState, setWebViewState] =
+    React.useState<FarcasterWebViewState>('closed');
+
+  const [isLoadingLinkFID, setIsLoadingLinkFID] = React.useState(false);
 
   const fid = useCurrentUserFID();
 
+  const tryLinkFID = useTryLinkFID();
+
   const onSuccess = React.useCallback(
-    (newFID: string) => {
-      dispatch({
-        type: setSyncedMetadataEntryActionType,
-        payload: {
-          name: syncedMetadataNames.CURRENT_USER_FID,
-          data: newFID,
-        },
-      });
+    async (newFID: string) => {
+      setWebViewState('closed');
+
+      try {
+        await tryLinkFID(newFID);
+      } finally {
+        setIsLoadingLinkFID(false);
+      }
     },
-    [dispatch],
+    [tryLinkFID],
   );
 
-  const { goBack } = navigation;
-
   const bottomSheetRef = React.useRef(null);
 
   const bottomSheetContext = React.useContext(BottomSheetContext);
@@ -71,9 +74,6 @@
     );
   }, [insets.bottom, setContentHeight]);
 
-  const [webViewState, setWebViewState] =
-    React.useState<FarcasterWebViewState>('closed');
-
   const isAppForegrounded = useIsAppForegrounded();
 
   React.useEffect(() => {
@@ -83,11 +83,11 @@
   }, [fid, isAppForegrounded]);
 
   const onPressConnect = React.useCallback(() => {
+    setIsLoadingLinkFID(true);
     setWebViewState('opening');
   }, []);
 
-  const connectButtonVariant =
-    webViewState === 'opening' ? 'loading' : 'enabled';
+  const connectButtonVariant = isLoadingLinkFID ? 'loading' : 'enabled';
 
   const connectFarcasterBottomSheet = React.useMemo(
     () => (
diff --git a/native/profile/farcaster-account-settings.react.js b/native/profile/farcaster-account-settings.react.js
--- a/native/profile/farcaster-account-settings.react.js
+++ b/native/profile/farcaster-account-settings.react.js
@@ -3,10 +3,7 @@
 import * as React from 'react';
 import { View } from 'react-native';
 
-import {
-  setSyncedMetadataEntryActionType,
-  clearSyncedMetadataEntryActionType,
-} from 'lib/actions/synced-metadata-actions.js';
+import { clearSyncedMetadataEntryActionType } from 'lib/actions/synced-metadata-actions.js';
 import { syncedMetadataNames } from 'lib/types/synced-metadata-types.js';
 import { useCurrentUserFID } from 'lib/utils/farcaster-utils.js';
 import { useDispatch } from 'lib/utils/redux-utils.js';
@@ -18,6 +15,7 @@
 import type { FarcasterWebViewState } from '../components/farcaster-web-view.react.js';
 import type { NavigationRoute } from '../navigation/route-names.js';
 import { useStyles } from '../themes/colors.js';
+import { useTryLinkFID } from '../utils/farcaster-utils.js';
 
 type Props = {
   +navigation: ProfileNavigationProp<'FarcasterAccountSettings'>,
@@ -44,26 +42,29 @@
   const [webViewState, setWebViewState] =
     React.useState<FarcasterWebViewState>('closed');
 
+  const [isLoadingLinkFID, setIsLoadingLinkFID] = React.useState(false);
+
+  const tryLinkFID = useTryLinkFID();
+
   const onSuccess = React.useCallback(
-    (newFID: string) => {
+    async (newFID: string) => {
       setWebViewState('closed');
-      dispatch({
-        type: setSyncedMetadataEntryActionType,
-        payload: {
-          name: syncedMetadataNames.CURRENT_USER_FID,
-          data: newFID,
-        },
-      });
+
+      try {
+        await tryLinkFID(newFID);
+      } finally {
+        setIsLoadingLinkFID(false);
+      }
     },
-    [dispatch],
+    [tryLinkFID],
   );
 
   const onPressConnectFarcaster = React.useCallback(() => {
+    setIsLoadingLinkFID(true);
     setWebViewState('opening');
   }, []);
 
-  const connectButtonVariant =
-    webViewState === 'opening' ? 'loading' : 'enabled';
+  const connectButtonVariant = isLoadingLinkFID ? 'loading' : 'enabled';
 
   const button = React.useMemo(() => {
     if (fid) {
diff --git a/native/utils/farcaster-utils.js b/native/utils/farcaster-utils.js
new file mode 100644
--- /dev/null
+++ b/native/utils/farcaster-utils.js
@@ -0,0 +1,41 @@
+// @flow
+
+import * as React from 'react';
+import { Alert } from 'react-native';
+
+import { getMessageForException } from 'lib/utils/errors.js';
+import { useLinkFID } from 'lib/utils/farcaster-utils.js';
+
+import {
+  getFarcasterAccountAlreadyLinkedAlertDetails,
+  UnknownErrorAlertDetails,
+} from './alert-messages.js';
+
+function useTryLinkFID(): (newFID: string) => Promise<void> {
+  const linkFID = useLinkFID();
+
+  return React.useCallback(
+    async (newFID: string) => {
+      try {
+        await linkFID(newFID);
+      } catch (e) {
+        if (
+          getMessageForException(e) ===
+          'farcaster ID already associated with different user'
+        ) {
+          const { title, message } =
+            getFarcasterAccountAlreadyLinkedAlertDetails();
+          Alert.alert(title, message);
+        } else {
+          Alert.alert(
+            UnknownErrorAlertDetails.title,
+            UnknownErrorAlertDetails.message,
+          );
+        }
+      }
+    },
+    [linkFID],
+  );
+}
+
+export { useTryLinkFID };