React hooks for Stellar and Soroban. The
wagmiyou've been waiting for.
npm install stellar-hooksstellar-hooks wires the Stellar JS SDK v13 and the Freighter wallet API into a set of ergonomic React hooks so you can build Stellar dApps without copy-pasting the same boilerplate across repos.
- Freighter Integration: Seamlessly connect and interact with the Freighter wallet.
- Horizon Data Fetching: Easy access to account balances, offers, and more.
- Soroban Support: Call smart contracts with built-in simulation and auth handling.
- Transaction Helpers: Simplified submission and polling for both classic and Soroban.
- Modular Adapters: First-class support for React Query and SWR.
- Type-Safe: Written in TypeScript with full type definitions.
// main.tsx
import { StellarProvider } from "stellar-hooks";
ReactDOM.createRoot(document.getElementById("root")!).render(
<StellarProvider network="testnet">
<App />
</StellarProvider>
);// App.tsx
import { useFreighter, useStellarBalance } from "stellar-hooks";
export function App() {
const { isConnected, publicKey, connect } = useFreighter();
const { xlmBalance } = useStellarBalance(publicKey);
if (!isConnected) {
return <button onClick={connect}>Connect Freighter</button>;
}
return (
<p>
{publicKey} · {xlmBalance?.balance ?? "..."} XLM
</p>
);
}Read the active network configuration and switch networks at runtime from anywhere inside <StellarProvider>.
Connect to and interact with the Freighter browser extension wallet, including arbitrary data signing via signBlob.
Fetch and subscribe to a Stellar account's data, including balances, sequence number, and thresholds.
Invoke a Soroban smart-contract method. Handles simulation, auth, submission, and status polling in one hook.
Submit a pre-signed transaction XDR and poll until it is confirmed. Works with both Soroban (RPC) and classic Stellar (Horizon) transactions.
const {
isInstalled, // boolean — is Freighter installed?
isConnected, // boolean — has the user granted access?
publicKey, // string | null
network, // string | null e.g. "TESTNET"
networkPassphrase, // string | null
isLoading,
error,
connect, // () => Promise<void>
disconnect, // () => void
signTransaction, // (xdr: string, opts?) => Promise<string>
signAuthEntry, // (entryPreimageXdr: string) => Promise<string>
signBlob, // (blob: string, opts?) => Promise<string>
} = useFreighter();Read the active network configuration and switch networks at runtime. All values reflect the currently active network — including any network switch made via switchNetwork.
const {
network, // StellarNetwork — "testnet" | "mainnet" | "futurenet" | "custom"
networkPassphrase, // string — e.g. "Test SDF Network ; September 2015"
horizonUrl, // string — active Horizon REST API endpoint
sorobanRpcUrl, // string — active Soroban RPC endpoint
config, // NetworkConfig — full { network, horizonUrl, sorobanRpcUrl, networkPassphrase }
switchNetwork, // (network: StellarNetwork, customConfig?: CustomNetworkConfig) => void
} = useNetwork();Switch networks at runtime (e.g. a settings UI):
import { useNetwork } from "stellar-hooks";
import type { StellarNetwork } from "stellar-hooks";
function NetworkSwitcher() {
const { network, switchNetwork } = useNetwork();
return (
<select
value={network}
onChange={(e) => switchNetwork(e.target.value as StellarNetwork)}
>
<option value="testnet">Testnet</option>
<option value="mainnet">Mainnet</option>
<option value="futurenet">Futurenet</option>
</select>
);
}When switching to a custom network, pass the full CustomNetworkConfig as the second argument:
switchNetwork("custom", {
network: "custom",
horizonUrl: "https://my-horizon.example.com",
sorobanRpcUrl: "https://my-rpc.example.com",
networkPassphrase: "My Network ; 2024",
});The selected network is persisted to localStorage and survives page reloads.
Fetch (and optionally poll) a full Stellar account from Horizon.
const {
data, // StellarAccountData | null
isLoading,
error,
lastFetchedAt, // Date | null
refetch,
} = useStellarAccount("G...", {
enabled: true, // default: true
refetchInterval: 5000, // poll every 5 s; 0 = disabled (default)
});
// data.balances → StellarBalance[]
// data.sequence → string
// data.subentryCount → number
// data.numSponsored → number
// data.numSponsoring → number
// data.raw → raw Horizon.AccountResponseConvenience wrapper around useStellarAccount that surfaces the XLM balance and optionally a specific asset balance.
const {
balances, // StellarBalance[]
xlmBalance, // StellarBalance | null (the native XLM entry)
assetBalance, // StellarBalance | null (the specific asset requested, if any)
isLoading,
error,
refetch,
} = useStellarBalance("G...", { code: "USDC", issuer: "G..." });Simulate → sign (via Freighter) → submit → poll a Soroban contract call. Full lifecycle in one hook.
const { call, status, result, hash, error, reset } = useSorobanContract({
contractId: "CABC...XYZ", // Soroban C... contract address
method: "increment",
args: [nativeToScVal(1, { type: "u32" })],
fee: "100", // stroops (default: BASE_FEE)
timeoutSeconds: 30, // default: 30
});
// Statuses: "idle" | "building" | "signing" | "submitting" | "polling" | "success" | "error"
<button onClick={() => call()} disabled={status !== "idle"}>
{status}
</button>You may also pass a pre-configured rpc.Server instance via the sorobanRpcServer option to reuse an existing connection or custom transport:
const { call, status } = useSorobanContract({
contractId: "CABC...XYZ",
method: "hello",
args: [nativeToScVal("world")],
sorobanRpcServer: myCustomServer,
});result contains the raw xdr.ScVal return value. Parse it with scValToNative from the SDK.
Submit a pre-signed XDR and poll for confirmation. Useful when you sign outside React (e.g. hardware wallet, server-side).
const { submit, status, hash, isSuccess, isError, error, reset } = useTransaction({
mode: "soroban", // "soroban" (default) | "classic"
timeoutSeconds: 60,
});
await submit(signedXdr);Read a raw Soroban ledger entry by its xdr.LedgerKey without constructing a contract call.
import { xdr, Address, Contract } from "@stellar/stellar-sdk";
const key = xdr.LedgerKey.contractData(
new xdr.LedgerKeyContractData({
contract: new Address(CONTRACT_ID).toScAddress(),
key: xdr.ScVal.scvSymbol("Counter"),
durability: xdr.ContractDataDurability.persistent(),
})
);
const { data, isLoading, error, refetch } = useLedgerEntry(key, {
refetchInterval: 3000,
});Build, sign, and submit a classic Stellar payment (native XLM or any Stellar asset) via Freighter in one hook.
const {
submit, // () => Promise<void> — build, sign, and submit the payment
status, // "idle" | "submitting" | "polling" | "success" | "error"
hash, // string | null — transaction hash on success
isLoading, // boolean
isSuccess, // boolean
isError, // boolean
error, // Error | null
reset, // () => void
} = usePayment({
destination: "GBXXX...",
asset: { type: "native" }, // XLM
// asset: { type: "credit", code: "USDC", issuer: "G..." }, // any asset
amount: "10",
memo: "Thanks!", // optional, max 28 bytes
fee: 100, // optional, stroops (default: 100)
timeoutSeconds: 60, // optional (default: 60)
onSuccess: (hash) => console.log("Sent!", hash),
onError: (err) => console.error(err),
});
return <button onClick={submit} disabled={isLoading}>Send XLM</button>;Wrap your app (or the portion that needs Stellar) with <StellarProvider> to configure the network. Every hook that reads blockchain data consumes endpoint configuration from this provider.
| Prop | Type | Default | Description |
|---|---|---|---|
network |
StellarNetwork |
"testnet" |
The network to connect to. One of "testnet", "mainnet", "futurenet", or "custom". |
customConfig |
CustomNetworkConfig |
— | Required when network is "custom". Supplies Horizon URL, Soroban RPC URL, and the network passphrase for your deployment. |
children |
React.ReactNode |
— | The component tree that will have access to Stellar context. |
| Network | Horizon URL | Soroban RPC URL | Network Passphrase |
|---|---|---|---|
testnet |
https://horizon-testnet.stellar.org |
https://soroban-testnet.stellar.org |
Test SDF Network ; September 2015 |
mainnet |
https://horizon.stellar.org |
https://mainnet.sorobanrpc.com |
Public Global Stellar Network ; September 2015 |
futurenet |
https://horizon-futurenet.stellar.org |
https://rpc-futurenet.stellar.org |
Test SDF Future Network ; October 2022 |
These presets are also exported as NETWORK_CONFIGS if you need them outside React:
import { NETWORK_CONFIGS } from "stellar-hooks";
const { horizonUrl } = NETWORK_CONFIGS.mainnet;// Testnet (default)
<StellarProvider network="testnet">
<App />
</StellarProvider>
// Mainnet
<StellarProvider network="mainnet">
<App />
</StellarProvider>
// Futurenet
<StellarProvider network="futurenet">
<App />
</StellarProvider>
// Custom / self-hosted network
<StellarProvider
network="custom"
customConfig={{
network: "custom",
horizonUrl: "https://my-horizon.example.com",
sorobanRpcUrl: "https://my-rpc.example.com",
networkPassphrase: "My Network ; 2024",
}}
>
<App />
</StellarProvider>Use this interface when connecting to a private or self-hosted Stellar network.
| Field | Type | Description |
|---|---|---|
network |
"custom" |
Must be "custom". |
horizonUrl |
string |
Horizon REST API base URL for this network. |
sorobanRpcUrl |
string |
Soroban RPC endpoint for contract simulation and submission. |
networkPassphrase |
string |
Network passphrase used when signing transactions. |
<StellarProvider> automatically persists the active network in localStorage under the keys stellar-hooks:network and stellar-hooks:custom-config. On subsequent page loads the persisted choice is restored, overriding the network prop. To switch networks at runtime and persist the change, use useNetwork().switchNetwork.
All types are exported and fully documented via JSDoc.
import type {
StellarNetwork,
NetworkConfig,
CustomNetworkConfig,
StellarAccountData,
StellarBalance,
FreighterState,
UseFreighterReturn,
TransactionStatus,
ContractCallOptions,
} from "stellar-hooks";| Peer dependency | Version |
|---|---|
| react | ≥ 18 |
| react-dom | ≥ 18 |
The library ships with @stellar/stellar-sdk v13 and @stellar/freighter-api v2 as direct dependencies — you don't need to install them separately unless you need a different version.
git clone https://github.com/YOUR_USERNAME/stellar-hooksnpm installnpm run dev— builds in watch mode- Edit hooks in
src/hooks/, types insrc/types/ - Open a PR
- Run
npm run changesetto create a changeset note for your change. - If your PR includes code changes, run
npm run buildbefore opening the PR.
Please review our Contributing Guide and Code of Conduct for more details before opening a pull request.
Full documentation with live examples is available at https://spiffamani.github.io/stellar-hooks/
To preview the documentation site locally:
npm install
npm run docs:devThe docs site will be available at http://localhost:5173 (or the port VitePress assigns).
This repository uses Changesets for automated changelog generation, version bumps, and npm publishing.
- Use
npm run changesetto add a release note to your PR. - After a changeset is merged into
main, the GitHub Actions release workflow will publish the package automatically. - To enable automated publishing, add
NPM_TOKENto repository secrets.
-
usePayment()— send XLM / SAT payments with one hook -
useClaimableBalance()— list and claim claimable balances -
useContractEvents()— subscribe to Soroban contract events via streaming -
usePathPayment()— strict send / receive path payment hook -
useStellarToml()— fetch and parse a domain'sstellar.toml -
useStellarToml()— fetch and parse a domain'sstellar.toml - React Query / SWR adapter (optional peer dependency)
testnet (default), mainnet, futurenet, and any custom network via the custom mode with a customConfig prop on <StellarProvider>.
No — it ships as a direct dependency. You only need to install it separately if you require a version different from the bundled one.
Most hooks that interact with user accounts (useFreighter, useSorobanContract, useStellarBalance, etc.) rely on a Freighter-connected wallet. useStellarAccount and useLedgerEntry are read-only and work without a wallet.
useFreighter depends on the Freighter browser extension API, so it works in web environments only. The other hooks should work anywhere you can run @stellar/stellar-sdk.
useStellarBalance is a lightweight wrapper around useStellarAccount that surfaces the native XLM balance at the top level for convenience.
Both useStellarAccount and useLedgerEntry accept a refetchInterval option (in ms). Set it to 5000 to poll every 5 seconds, or 0 (default) to disable polling.
No — the hooks consume configuration from the provider context. Wrap your app with <StellarProvider> at the root.
MIT