Skip to main content

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.tsx
export 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.tsx
export 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>;
}
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.tsx
export 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

AccountUiBalance - Shows SOL balance with formattingLocation: components/account/account-ui-balance.tsxUses useGetBalance hook to fetch and display balance.

Account action hooks

These hooks provide account-related functionality:
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)

ChatHeader

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
}

ChatInput

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.

ChatMessage & ToolManager

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:
1

Feature components

High-level components that coordinate multiple sub-components (e.g., AccountFeature, ChatScreen)
2

UI components

Presentational components focused on rendering (e.g., AccountUiBalance, ChatMessage)
3

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

Build docs developers (and LLMs) love