Overview
This guide will walk you through integrating Hermis into a React application. You’ll learn how to set up wallet connections, display wallet information, and perform basic Solana operations.
Installation
Install the React package and its peer dependencies:
npm install @hermis/solana-headless-react @solana/wallet-adapter-phantom @solana/wallet-adapter-solflare
Step 1: Set Up the Provider
Wrap your app with the HermisProvider to enable wallet functionality throughout your application:
import React from 'react' ;
import { HermisProvider } from '@hermis/solana-headless-react' ;
import { WalletAdapterNetwork } from '@solana/wallet-adapter-base' ;
import Home from './Home' ;
function App () {
// Auto-detect wallet-standard compatible wallets
const wallets = []; // Add custom wallet adapters here if needed
return (
< HermisProvider
endpoint = "https://api.devnet.solana.com"
network = { WalletAdapterNetwork . Devnet }
autoConnect = { true }
wallets = { wallets }
onError = { ( error ) => console . error ( 'Wallet error:' , error ) }
>
< Home />
</ HermisProvider >
);
}
export default App ;
Provider Options
The Solana RPC endpoint URL (e.g., https://api.devnet.solana.com)
network
WalletAdapterNetwork
required
The Solana network: Mainnet, Devnet, or Testnet
Automatically reconnect to the last used wallet on page load
Array of wallet adapters to support
Callback function for handling wallet errors
Step 2: Create a Wallet Component
Use the useWallet hook to access wallet functionality:
import React from 'react' ;
import { useWallet , useSolanaBalance } from '@hermis/solana-headless-react' ;
function Home () {
const {
wallet ,
publicKey ,
connecting ,
connected ,
connect ,
disconnect ,
select
} = useWallet ();
const { balance , loading } = useSolanaBalance ( publicKey );
const handleConnect = async () => {
if ( wallet ) {
await connect ();
} else {
// Select a wallet first if none is selected
select ( 'Phantom' );
await connect ();
}
};
return (
< div className = "container" >
< h1 > Solana Wallet Demo </ h1 >
{ ! connected ? (
< div >
< button
onClick = { handleConnect }
disabled = { connecting }
className = "connect-button"
>
{ connecting ? 'Connecting...' : 'Connect Wallet' }
</ button >
</ div >
) : (
< div className = "wallet-info" >
< p >< strong > Wallet: </ strong > { wallet ?. adapter . name } </ p >
< p >< strong > Address: </ strong > { publicKey ?. toBase58 () } </ p >
< p >< strong > Balance: </ strong > { loading ? 'Loading...' : ` ${ balance } SOL` } </ p >
< button onClick = { disconnect } className = "disconnect-button" >
Disconnect
</ button >
</ div >
) }
</ div >
);
}
export default Home ;
Step 3: Send a Transaction
Add transaction functionality using the sendTransaction method:
import React , { useState } from 'react' ;
import { useWallet , useConnection } from '@hermis/solana-headless-react' ;
import { Transaction , SystemProgram , LAMPORTS_PER_SOL , PublicKey } from '@solana/web3.js' ;
function SendTransaction () {
const { publicKey , sendTransaction } = useWallet ();
const { connection } = useConnection ();
const [ recipient , setRecipient ] = useState ( '' );
const [ amount , setAmount ] = useState ( '0.1' );
const [ sending , setSending ] = useState ( false );
const [ signature , setSignature ] = useState ( '' );
const handleSend = async () => {
if ( ! publicKey || ! recipient ) return ;
setSending ( true );
setSignature ( '' );
try {
const transaction = new Transaction ();
const recipientPubKey = new PublicKey ( recipient );
transaction . add (
SystemProgram . transfer ({
fromPubkey: publicKey ,
toPubkey: recipientPubKey ,
lamports: parseFloat ( amount ) * LAMPORTS_PER_SOL
})
);
// Get recent blockhash
const { blockhash } = await connection . getLatestBlockhash ();
transaction . recentBlockhash = blockhash ;
transaction . feePayer = publicKey ;
// Send transaction
const sig = await sendTransaction ( transaction , connection );
setSignature ( sig );
console . log ( 'Transaction sent:' , sig );
} catch ( error ) {
console . error ( 'Error sending transaction:' , error );
alert ( 'Failed to send transaction' );
} finally {
setSending ( false );
}
};
return (
< div className = "send-transaction" >
< h2 > Send SOL </ h2 >
< div className = "form" >
< input
type = "text"
placeholder = "Recipient address"
value = { recipient }
onChange = { ( e ) => setRecipient ( e . target . value ) }
className = "input"
/>
< input
type = "number"
placeholder = "Amount (SOL)"
value = { amount }
onChange = { ( e ) => setAmount ( e . target . value ) }
step = "0.01"
min = "0"
className = "input"
/>
< button
onClick = { handleSend }
disabled = { ! publicKey || ! recipient || sending }
className = "send-button"
>
{ sending ? 'Sending...' : 'Send SOL' }
</ button >
</ div >
{ signature && (
< div className = "success" >
< p > Transaction successful! </ p >
< a
href = { `https://explorer.solana.com/tx/ ${ signature } ?cluster=devnet` }
target = "_blank"
rel = "noopener noreferrer"
>
View on Explorer
</ a >
</ div >
) }
</ div >
);
}
export default SendTransaction ;
Available Hooks
useWallet
Primary hook for wallet interactions:
const {
// Wallet state
autoConnect , // Whether auto-connect is enabled
wallets , // Array of all available wallets
wallet , // Currently selected wallet
publicKey , // Public key of connected wallet (web3.js)
connecting , // Connection status
connected , // Whether wallet is connected
disconnecting , // Disconnection status
// Kit-specific properties
address , // Kit Address type (null if not connected)
addressString , // Plain address string (null if not connected)
chain , // Solana chain identifier (e.g., 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1')
messageSigner , // Message signer for Kit architecture (null if not available)
transactionSigner , // Transaction sending signer for Kit (null if not available)
getChainId , // Function to get chain ID: (network: 'devnet' | 'mainnet' | 'testnet') => `solana:${string}`
// Methods
select , // Select a wallet by name
connect , // Connect to selected wallet
disconnect , // Disconnect wallet
// Transaction methods (support both web3.js and Kit)
sendTransaction , // Send a transaction (dual architecture support)
signTransaction , // Sign a transaction (dual architecture support)
signAllTransactions , // Sign multiple transactions (dual architecture support)
signAndSendTransaction , // Sign and send transaction (dual architecture support)
// Message signing
signMessage , // Sign a message
signIn , // Sign in with wallet
// Utilities
hasFeature , // Check if wallet supports a feature: (feature: 'signMessage' | 'signTransaction' | 'signAllTransactions' | 'signIn') => boolean
} = useWallet ();
useConnection
Access the Solana connection:
const {
connection , // DualConnection - supports both web3.js Connection and Kit Rpc
network // WalletAdapterNetwork (optional)
} = useConnection ();
// Works with both architectures
const accountInfo = await connection . getAccountInfo ( publicKey );
// For Kit RPC, use the .send() pattern
const { value } = await connection . getLatestBlockhash (). send ();
useSolanaBalance
Fetch and track wallet balance:
const {
balance , // Balance in SOL
balanceLamports , // Balance in lamports
loading , // Loading state
error , // Error if occurred
refetch // Manually refetch balance
} = useSolanaBalance ( publicKey , 10000 ); // 10 second refresh
useSolanaTokenAccounts
Fetch SPL token accounts:
const {
tokenAccounts , // Array of token accounts
loading , // Loading state
error , // Error if occurred
refetch // Manually refetch
} = useSolanaTokenAccounts ( publicKey );
useSolanaNFTs
Fetch NFTs owned by a wallet:
const {
nfts , // Array of NFTs
loading , // Loading state
error , // Error if occurred
refetch // Manually refetch
} = useSolanaNFTs ( publicKey );
useSolanaTransaction
Track transaction status with confirmations:
const {
status , // TransactionStatus object
loading , // Loading state
refetch // Manually refetch status
} = useSolanaTransaction ( signature , autoFetch );
// TransactionStatus includes:
// - signature: string
// - status: 'pending' | 'confirmed' | 'finalized' | 'failed'
// - confirmations: number
// - confirmationStatus: SignatureStatus['confirmationStatus']
// - error?: string
// - transactionDetails?: VersionedTransactionResponse
useWalletAdapters
Get grouped and sorted wallet adapters:
const {
installed , // Installed wallet adapters
loadable , // Loadable wallet adapters
notDetected , // Not detected wallet adapters
all // All wallet adapters (sorted)
} = useWalletAdapters ();
useAnchorWallet
Get an Anchor-compatible wallet with dual architecture support:
const anchorWallet = useAnchorWallet ();
// Returns AnchorWallet | undefined
// Primary use: Pass to Anchor programs (uses web3.js internally)
const program = new Program ( idl , programId , { connection , wallet: anchorWallet });
await program . methods . myMethod (). accounts ({ ... }). rpc ();
// Advanced use: Manual signing with web3.js
const signedTx = await anchorWallet . signTransaction ( web3Transaction );
// Advanced use: Manual signing with Kit (also supported)
const signedKitTx = await anchorWallet . signTransaction ( kitTransactionMessage );
useWalletModal
Manage wallet selection modal state:
const {
visible , // Whether modal is visible
showModal , // Show the modal
hideModal , // Hide the modal
selectedWallet , // Currently selected wallet name
setSelectedWallet // Set selected wallet
} = useWalletModal ();
Next.js Integration
For Next.js 13+ with App Router, create a client-side provider:
'use client' ;
import { HermisProvider } from '@hermis/solana-headless-react' ;
import { WalletAdapterNetwork } from '@solana/wallet-adapter-base' ;
export function Providers ({ children } : { children : React . ReactNode }) {
// Auto-detect wallet-standard compatible wallets
const wallets = []; // Add custom wallet adapters here if needed
return (
< HermisProvider
endpoint = "https://api.devnet.solana.com"
network = { WalletAdapterNetwork . Devnet }
autoConnect = { true }
wallets = { wallets }
>
{ children }
</ HermisProvider >
);
}
Then use in your layout:
import { Providers } from './providers' ;
export default function RootLayout ({ children } : { children : React . ReactNode }) {
return (
< html lang = "en" >
< body >
< Providers > { children } </ Providers >
</ body >
</ html >
);
}
Best Practices
Always check connecting, connected, and disconnecting states to provide appropriate UI feedback to users.
Implement proper error handling for all wallet operations, especially during transactions. Use the onError callback in the provider.
Mobile Wallet Adapter Performance
Ensure your RPC endpoint matches the intended network. Don’t mix mainnet and devnet operations.
What’s Next?