Overview
Synto Mobile components are organized by feature domain, with each directory containing related UI and logic components. The architecture promotes reusability, separation of concerns, and type safety.
Component organization
Components are grouped into feature-based directories:
components/
├── account/ # Wallet balance and token management
├── auth/ # Authentication and authorization
├── chat/ # Chat interface components
├── cluster/ # Solana network selection
├── demo/ # Example features
├── settings/ # App configuration
├── solana/ # Wallet adapter integration
└── ui/ # Reusable UI primitives
Core components
App-level components
These components provide foundational app functionality:
Wraps the entire app with necessary context providers. Location: components/app-providers.tsxexport function AppProviders ({ children } : PropsWithChildren ) {
return (
< AppTheme >
< QueryClientProvider client = { queryClient } >
< ClusterProvider >
< SolanaProvider >
< AuthProvider > { children } </ AuthProvider >
</ SolanaProvider >
</ ClusterProvider >
</ QueryClientProvider >
</ AppTheme >
);
}
Responsibilities:
Theme configuration
Query client setup
Network selection
Solana connection
Authentication state
Manages light/dark theme switching using React Navigation themes. Location: components/app-theme.tsxexport function AppTheme ({ children } : PropsWithChildren ) {
const { theme } = useAppTheme ();
return < ThemeProvider value = { theme } > { children } </ ThemeProvider > ;
}
export function useAppTheme () {
const colorScheme = useColorScheme ();
const isDark = colorScheme === "dark" ;
const theme = isDark ? AppThemeDark : AppThemeLight ;
return { colorScheme , isDark , theme };
}
Standard page container with consistent layout. Location: components/app-page.tsxProvides padding, safe area handling, and scrollable content areas.
Themed text and view components. Location: components/app-text.tsx, components/app-view.tsxApply theme colors automatically based on the current color scheme.
Authentication components
AuthProvider
Manages authentication state and wallet connection lifecycle.
Location: components/auth/auth-provider.tsx
export interface AuthState {
isAuthenticated : boolean ;
isLoading : boolean ;
hasCompletedOnboarding : boolean ;
signIn : () => Promise < Account >;
signOut : () => Promise < void >;
markOnboardingComplete : () => Promise < void >;
checkAuthAndRedirect : () => void ;
}
export function useAuth () {
return use ( Context );
}
function MyScreen () {
const { isAuthenticated , signIn , signOut } = useAuth ();
if ( ! isAuthenticated ) {
return < Button onPress = { signIn } > Connect Wallet </ Button > ;
}
return < Button onPress = { signOut } > Disconnect </ Button > ;
}
export function AuthProvider ({ children } : PropsWithChildren ) {
const { disconnect } = useMobileWallet ();
const { accounts , isLoading } = useAuthorization ();
const signInMutation = useSignInMutation ();
const [ hasCompletedOnboarding , setHasCompletedOnboarding ] = useState ( false );
const isAuthenticated = ( accounts ?. length ?? 0 ) > 0 ;
// Auto-redirect based on auth state
const checkAuthAndRedirect = useCallback (() => {
if ( ! isAuthenticated && ! isLoading ) {
if ( ! hasCompletedOnboarding ) {
router . replace ( "/onboarding" );
} else {
router . replace ( "/sign-in" );
}
}
}, [ isAuthenticated , isLoading , hasCompletedOnboarding ]);
// ... rest of implementation
}
The AuthProvider automatically handles redirects based on authentication status and onboarding completion.
Solana components
These components handle blockchain interactions and wallet management.
SolanaProvider
Creates and manages the Solana RPC connection.
Location: components/solana/solana-provider.tsx
export function SolanaProvider ({ children , config } : SolanaProviderProps ) {
const { selectedCluster } = useCluster ();
const connection = useMemo (
() => new Connection ( selectedCluster . endpoint , config ),
[ selectedCluster , config ]
);
return (
< ConnectionContext.Provider value = { { connection } } >
{ children }
</ ConnectionContext.Provider >
);
}
export function useConnection () : Connection {
return useSolana (). connection ;
}
The connection is recreated whenever the selected cluster changes. Use useConnection() hook to access the current connection instance.
Mobile Wallet hooks
Core wallet interaction hook. Location: components/solana/use-mobile-wallet.tsxexport function useMobileWallet () {
const { authorizeSession , deauthorizeSessions } = useAuthorization ();
const signAndSendTransaction = useCallback (
async ( transaction : Transaction | VersionedTransaction ) => {
return await transact ( async ( wallet ) => {
await authorizeSession ( wallet );
const signatures = await wallet . signAndSendTransactions ({
transactions: [ transaction ],
});
return signatures [ 0 ];
});
},
[ authorizeSession ]
);
return {
connect ,
signIn ,
disconnect ,
signAndSendTransaction ,
signMessage ,
signTransactions ,
};
}
Features:
Automatic error handling and recovery
Session management
Transaction signing and sending
Message signing
UI-focused wallet hook with connection state. Location: components/solana/use-wallet-ui.tsxProvides the current connected account and UI helper methods.
Low-level authorization hook. Location: components/solana/use-authorization.tsxManages wallet authorization sessions and account state.
Wallet UI components
WalletUiButtonConnect Connect wallet button with loading states
WalletUiButtonDisconnect Disconnect wallet button
WalletUiDropdown Wallet menu with account info and actions
BaseButton Themed button component used across wallet UI
Account components
Components for displaying and managing wallet balances and tokens.
AccountFeature
Main account dashboard component.
Location: components/account/account-feature.tsx
export function AccountFeature () {
const { account } = useWalletUi ();
const [ refreshing , setRefreshing ] = useState ( false );
const invalidateBalance = useGetBalanceInvalidate ({
address: account ?. publicKey
});
const invalidateTokenAccounts = useGetTokenAccountsInvalidate ({
address: account ?. publicKey
});
const onRefresh = useCallback ( async () => {
setRefreshing ( true );
await Promise . all ([ invalidateBalance (), invalidateTokenAccounts ()]);
setRefreshing ( false );
}, [ invalidateBalance , invalidateTokenAccounts ]);
return (
< AppPage >
{ account ? (
< ScrollView refreshControl = {
< RefreshControl refreshing = { refreshing } onRefresh = { onRefresh } />
} >
< AccountUiBalance address = { account . publicKey } />
< AccountUiButtons />
< AccountUiTokenAccounts address = { account . publicKey } />
</ ScrollView >
) : (
< WalletUiButtonConnect />
) }
</ AppPage >
);
}
Account sub-components
Balance Display
Token Accounts
Action Buttons
AccountUiBalance - Shows SOL balance with formattingLocation: components/account/account-ui-balance.tsxUses useGetBalance hook to fetch and display balance.
AccountUiTokenAccounts - Lists all token holdingsLocation: components/account/account-ui-token-accounts.tsxDisplays SPL token balances using useGetTokenAccounts hook.
AccountUiButtons - Send, Receive, Airdrop actionsLocation: components/account/account-ui-buttons.tsxNavigation to send/receive flows and airdrop requests.
Account action hooks
These hooks provide account-related functionality:
use-get-balance.tsx
use-transfer-sol.tsx
use-request-airdrop.tsx
export function useGetBalance ({ address } : { address : PublicKey }) {
const connection = useConnection ();
const queryKey = useGetBalanceQueryKey ({
address ,
endpoint: connection . rpcEndpoint
});
return useQuery ({
queryKey ,
queryFn : () => connection . getBalance ( address ),
});
}
export function useGetBalanceInvalidate ({ address } : { address : PublicKey }) {
const connection = useConnection ();
const queryKey = useGetBalanceQueryKey ({ address , endpoint: connection . rpcEndpoint });
const client = useQueryClient ();
return () => client . invalidateQueries ({ queryKey });
}
Chat components
The chat interface consists of several interconnected components:
Component hierarchy
ChatScreen (app/(tabs)/chat/index.tsx)
├── ChatHeader
├── ChatList (when messages exist)
│ └── ChatMessage (for each message)
│ └── ToolManager (for tool invocations)
├── SuggestionCards (when no messages)
├── ChatInput
└── HistoryOverlay (when visible)
Location: components/chat/chat-header.tsx
Provides navigation and chat management controls.
interface ChatHeaderProps {
onHistoryPress : () => void ;
onNewChatPress : () => void ;
chatTitle ?: string ;
}
export function ChatHeader ({
onHistoryPress ,
onNewChatPress ,
chatTitle
} : ChatHeaderProps ) {
// Renders title, history button, new chat button
}
Location: components/chat/chat-input.tsx
interface ChatInputProps {
value : string ;
onChangeText : ( text : string ) => void ;
onSubmit : () => void ;
isLoading : boolean ;
}
export function ChatInput ({
value ,
onChangeText ,
onSubmit ,
isLoading
} : ChatInputProps ) {
return (
< Animated.View entering = { FadeInDown . delay ( 400 ). duration ( 600 ) } >
< View style = { styles . container } >
< TouchableOpacity >
< UiIconSymbol name = "plus" />
</ TouchableOpacity >
< TextInput
value = { value }
onChangeText = { onChangeText }
multiline
placeholder = "Ask anything"
/>
< TouchableOpacity onPress = { onSubmit } disabled = { isLoading } >
< UiIconSymbol name = "waveform" />
</ TouchableOpacity >
</ View >
</ Animated.View >
);
}
The chat input uses react-native-reanimated for smooth enter animations.
Location: components/chat/chat-message.tsx, components/chat/tool-manager.tsx
Renders individual messages with support for tool invocations.
interface ChatMessageProps {
message : Message ;
addToolResult ?: ( args : any ) => void ;
}
export function ChatMessage ({ message , addToolResult } : ChatMessageProps ) {
return (
< View >
< Text > { message . content } </ Text >
{ message . toolInvocations ?. map (( tool ) => (
< ToolManager
key = { tool . toolCallId }
toolInvocation = { tool }
addToolResult = { addToolResult }
/>
)) }
</ View >
);
}
SuggestionCards
Location: components/chat/suggestion-cards.tsx
Displays suggested prompts when starting a new chat.
interface SuggestionCardsProps {
onSuggestionPress : ( suggestion : string ) => void ;
}
export function SuggestionCards ({ onSuggestionPress } : SuggestionCardsProps ) {
const suggestions = [
"Check my SOL balance" ,
"Show my tokens" ,
"Get current TPS" ,
"Request airdrop" ,
];
return (
< View >
{ suggestions . map (( suggestion ) => (
< TouchableOpacity onPress = { () => onSuggestionPress ( suggestion ) } >
< Text > { suggestion } </ Text >
</ TouchableOpacity >
)) }
</ View >
);
}
HistoryOverlay
Location: components/chat/history-overlay.tsx
Modal overlay for viewing and managing chat history.
interface HistoryOverlayProps {
visible : boolean ;
onClose : () => void ;
onSelectConversation : ( id : string ) => void ;
onNewChat : () => void ;
onDeleteChat : ( id : string ) => void ;
conversations : ChatMetadata [];
currentChatId : string | null ;
}
Cluster components
Manage Solana network selection and information.
ClusterProvider
Location: components/cluster/cluster-provider.tsx
export interface ClusterProviderContext {
selectedCluster : Cluster ;
clusters : Cluster [];
setSelectedCluster : ( cluster : Cluster ) => void ;
getExplorerUrl ( path : string ) : string ;
}
export function useCluster () {
return useContext ( Context );
}
Cluster UI components
ClusterUiVersion Displays the current cluster’s RPC version
ClusterUiGenesisHash Shows the genesis hash of the selected cluster
UI components
Reusable UI primitives used throughout the app.
UiIconSymbol
Location: components/ui/ui-icon-symbol.tsx
Cross-platform icon component using SF Symbols on iOS and fallback icons on other platforms.
interface UiIconSymbolProps {
name : string ;
size ?: number ;
color ?: string ;
weight ?: 'ultraLight' | 'thin' | 'light' | 'regular' | 'medium' | 'semibold' | 'bold' ;
}
export function UiIconSymbol ({
name ,
size = 24 ,
color ,
weight = 'regular'
} : UiIconSymbolProps ) {
// iOS: Uses expo-symbols for SF Symbols
// Android/Web: Uses @expo/vector-icons fallback
}
Component patterns
Separation of concerns
Components follow a clear separation:
Feature components
High-level components that coordinate multiple sub-components (e.g., AccountFeature, ChatScreen)
UI components
Presentational components focused on rendering (e.g., AccountUiBalance, ChatMessage)
Hook components
Logic-only hooks that return data and actions (e.g., useGetBalance, useMultiChat)
Error boundaries
The app currently doesn’t implement error boundaries. Consider adding them for production: import { ErrorBoundary } from 'react-error-boundary' ;
function ErrorFallback ({ error }) {
return (
< View >
< Text > Something went wrong: </ Text >
< Text > { error . message } </ Text >
</ View >
);
}
< ErrorBoundary FallbackComponent = { ErrorFallback } >
< YourComponent />
</ ErrorBoundary >
TypeScript patterns
All components use TypeScript with strict type checking:
// Props interface
interface ComponentProps {
required : string ;
optional ?: number ;
callback : ( value : string ) => void ;
}
// Component with typed props
export function Component ({
required ,
optional = 10 ,
callback
} : ComponentProps ) {
// Component implementation
}
Next steps
Custom hooks Learn about reusable React hooks
App architecture Understand the overall architecture