@solana/react-hooks 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +316 -0
  2. package/package.json +85 -0
package/README.md ADDED
@@ -0,0 +1,316 @@
1
+ # @solana/react-hooks
2
+
3
+ React hooks for `@solana/client-core`. Drop in the provider and call hooks instead of juggling RPC
4
+ clients, wallets, and stores yourself.
5
+
6
+ > **Status:** Experimental – breaking changes may land often.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pnpm add @solana/react-hooks
12
+ ```
13
+
14
+ ## Minimal example
15
+
16
+ Mount the provider once and call hooks anywhere in the subtree.
17
+
18
+ ```tsx
19
+ import {
20
+ SolanaClientProvider,
21
+ useBalance,
22
+ useConnectWallet,
23
+ useWallet,
24
+ } from '@solana/react-hooks';
25
+
26
+ function WalletButton() {
27
+ const connectWallet = useConnectWallet();
28
+ return <button onClick={() => connectWallet('phantom')}>Connect Phantom</button>;
29
+ }
30
+
31
+ function WalletBalance() {
32
+ const wallet = useWallet();
33
+ const balance = useBalance(wallet.status === 'connected' ? wallet.session.account.address : undefined);
34
+
35
+ if (wallet.status !== 'connected') return <p>Connect a wallet</p>;
36
+ if (balance.fetching) return <p>Loading…</p>;
37
+
38
+ return <p>Lamports: {balance.lamports?.toString() ?? '0'}</p>;
39
+ }
40
+
41
+ export function App() {
42
+ return (
43
+ <SolanaClientProvider
44
+ config={{
45
+ endpoint: 'https://api.devnet.solana.com',
46
+ websocketEndpoint: 'wss://api.devnet.solana.com',
47
+ }}
48
+ >
49
+ <WalletButton />
50
+ <WalletBalance />
51
+ </SolanaClientProvider>
52
+ );
53
+ }
54
+ ```
55
+
56
+ ## Hooks at a glance
57
+
58
+ - `useWallet`, `useConnectWallet`, `useDisconnectWallet` – read or update the current wallet session.
59
+ - `useBalance` / `useAccount` – fetch lamports once or keep account data in sync.
60
+ - `useSolTransfer`, `useSplToken`, `useTransactionPool` – helper-driven flows for SOL, SPL, and
61
+ general transactions.
62
+ - `useClientStore` – access the underlying Zustand store if you need low-level state.
63
+
64
+ ### Wallet helpers
65
+
66
+ Read the current wallet session and expose connect/disconnect buttons.
67
+
68
+ ```tsx
69
+ const WalletActions = () => {
70
+ const wallet = useWallet();
71
+ const connect = useConnectWallet();
72
+ const disconnect = useDisconnectWallet();
73
+
74
+ if (wallet.status === 'connected') {
75
+ return (
76
+ <div>
77
+ <p>{wallet.session.account.address.toString()}</p>
78
+ <button onClick={() => disconnect()}>Disconnect</button>
79
+ </div>
80
+ );
81
+ }
82
+
83
+ return <button onClick={() => connect('phantom')}>Connect Phantom</button>;
84
+ };
85
+ ```
86
+
87
+ ### Balance watcher
88
+
89
+ Read lamports (cached plus live updates) for any address.
90
+
91
+ ```tsx
92
+ import { useBalance } from '@solana/react-hooks';
93
+
94
+ function BalanceCard({ address }) {
95
+ const { lamports, fetching, slot } = useBalance(address);
96
+ if (fetching) return <p>Loading…</p>;
97
+ return (
98
+ <div>
99
+ <p>Lamports: {lamports?.toString() ?? '0'}</p>
100
+ <small>Last slot: {slot?.toString() ?? 'unknown'}</small>
101
+ </div>
102
+ );
103
+ }
104
+ ```
105
+
106
+ ### Account cache
107
+
108
+ Fetch account data and optionally keep it in sync via subscriptions.
109
+
110
+ ```tsx
111
+ import { useAccount } from '@solana/react-hooks';
112
+
113
+ function AccountInspector({ address }) {
114
+ const account = useAccount(address, { watch: true });
115
+
116
+ if (!account) return <p>Loading…</p>;
117
+ if (account.error) return <p>Error loading account</p>;
118
+
119
+ return <pre>{JSON.stringify(account.data, null, 2)}</pre>;
120
+ }
121
+ ```
122
+
123
+ ### SOL transfers
124
+
125
+ Trigger SOL transfers with built-in status tracking.
126
+
127
+ ```tsx
128
+ import { useSolTransfer } from '@solana/react-hooks';
129
+
130
+ const SendSolButton = ({ destination, amount }) => {
131
+ const { send, isSending } = useSolTransfer();
132
+
133
+ return (
134
+ <button
135
+ disabled={isSending}
136
+ onClick={() =>
137
+ send({
138
+ destination,
139
+ lamports: amount,
140
+ })
141
+ }
142
+ >
143
+ {isSending ? 'Sending…' : 'Send SOL'}
144
+ </button>
145
+ );
146
+ };
147
+ ```
148
+
149
+ ### SPL tokens
150
+
151
+ Scope SPL helpers by mint and reuse the same API for balances and transfers.
152
+
153
+ ```tsx
154
+ const SplBalance = ({ mint }) => {
155
+ const { balance, send, isSending } = useSplToken(mint);
156
+
157
+ return (
158
+ <div>
159
+ <p>Amount: {balance?.uiAmount ?? '0'}</p>
160
+ <button
161
+ disabled={isSending}
162
+ onClick={() =>
163
+ send({
164
+ amount: 1n,
165
+ destinationOwner: 'Destination111111111111111111111111',
166
+ })
167
+ }
168
+ >
169
+ Send 1 token
170
+ </button>
171
+ </div>
172
+ );
173
+ };
174
+ ```
175
+
176
+ ### Transaction pool
177
+
178
+ Compose instructions, refresh blockhashes automatically, and send transactions from one hook.
179
+
180
+ ```tsx
181
+ import type { TransactionInstructionInput } from '@solana/client-core';
182
+
183
+ const useMemoizedInstruction = (): TransactionInstructionInput => ({
184
+ accounts: [],
185
+ data: new Uint8Array(),
186
+ programAddress: 'Example1111111111111111111111111111111111',
187
+ });
188
+
189
+ const TransactionFlow = () => {
190
+ const instruction = useMemoizedInstruction();
191
+ const {
192
+ addInstruction,
193
+ prepareAndSend,
194
+ sendStatus,
195
+ latestBlockhash,
196
+ } = useTransactionPool();
197
+
198
+ return (
199
+ <div>
200
+ <button onClick={() => addInstruction(instruction)}>Add instruction</button>
201
+ <button disabled={sendStatus === 'loading'} onClick={() => prepareAndSend()}>
202
+ {sendStatus === 'loading' ? 'Sending…' : 'Prepare & Send'}
203
+ </button>
204
+ <p>Recent blockhash: {latestBlockhash.blockhash ?? 'loading…'}</p>
205
+ </div>
206
+ );
207
+ };
208
+ ```
209
+
210
+ ### Client store access
211
+
212
+ Drop down to the underlying Zustand store when you need bespoke selectors.
213
+
214
+ ```tsx
215
+ import { useClientStore } from '@solana/react-hooks';
216
+
217
+ function ClusterStatus() {
218
+ const cluster = useClientStore((state) => state.cluster);
219
+ return <p>Cluster: {cluster.status.status}</p>;
220
+ }
221
+ ```
222
+
223
+ ## Query hooks
224
+
225
+ Wrap a subtree with `<SolanaQueryProvider>` and call hooks like `useLatestBlockhash`,
226
+ `useProgramAccounts`, or `useSimulateTransaction`. Every hook returns `{ data, status, refresh }` so
227
+ you can read the current value and trigger a refetch:
228
+
229
+ ### Latest blockhash
230
+
231
+ Poll or refetch the cluster's latest blockhash.
232
+
233
+ ```tsx
234
+ import { useLatestBlockhash } from '@solana/react-hooks';
235
+
236
+ function BlockhashTicker() {
237
+ const { blockhash, status, refresh } = useLatestBlockhash({ refreshInterval: 20_000 });
238
+
239
+ return (
240
+ <div>
241
+ <button onClick={() => refresh()}>Refresh</button>
242
+ <p>Status: {status}</p>
243
+ <p>Blockhash: {blockhash ?? 'loading…'}</p>
244
+ </div>
245
+ );
246
+ }
247
+ ```
248
+
249
+ ### Program accounts
250
+
251
+ ```tsx
252
+ import { SolanaQueryProvider, useProgramAccounts } from '@solana/react-hooks';
253
+
254
+ function ProgramAccountsList({ programAddress }) {
255
+ const { data, status, refresh } = useProgramAccounts(programAddress);
256
+
257
+ if (status === 'loading') return <p>Loading…</p>;
258
+ if (status === 'error') return <p>Retry later.</p>;
259
+
260
+ return (
261
+ <div>
262
+ <button onClick={() => refresh()}>Refresh</button>
263
+ <ul>
264
+ {data?.map(({ pubkey }) => (
265
+ <li key={pubkey.toString()}>{pubkey.toString()}</li>
266
+ ))}
267
+ </ul>
268
+ </div>
269
+ );
270
+ }
271
+
272
+ export function QueryDemo({ programAddress }) {
273
+ return (
274
+ <SolanaClientProvider config={{ endpoint: 'https://api.devnet.solana.com' }}>
275
+ <SolanaQueryProvider>
276
+ <ProgramAccountsList programAddress={programAddress} />
277
+ </SolanaQueryProvider>
278
+ </SolanaClientProvider>
279
+ );
280
+ }
281
+ ```
282
+
283
+ ### Transaction simulation
284
+
285
+ Simulate any transaction payload (wire string or object) and read RPC logs.
286
+
287
+ ```tsx
288
+ import { useSimulateTransaction } from '@solana/react-hooks';
289
+
290
+ function SimulationLogs({ transaction }) {
291
+ const { logs, status, refresh } = useSimulateTransaction(transaction);
292
+
293
+ if (status === 'loading') return <p>Simulating…</p>;
294
+ if (status === 'error') return <p>Simulation failed.</p>;
295
+
296
+ return (
297
+ <div>
298
+ <button onClick={() => refresh()}>Re-run</button>
299
+ <pre>{JSON.stringify(logs ?? [], null, 2)}</pre>
300
+ </div>
301
+ );
302
+ }
303
+ ```
304
+
305
+ ## Going further
306
+
307
+ - Need Wallet Standard buttons or sign/send helpers? Use `useSignIn`, `useSignMessage`,
308
+ `useSignTransaction`, and friends from `walletStandardHooks.ts`.
309
+ - Looking for examples? See `examples/react-hooks` for a ready-to-run playground that wires the
310
+ provider, hooks, and mock UIs together.
311
+
312
+ ## Scripts
313
+
314
+ - `pnpm build` – run both JS compilation and type definition emit
315
+ - `pnpm test:typecheck` – strict type-checking without emit
316
+ - `pnpm lint` / `pnpm format` – Biome-powered linting and formatting
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@solana/react-hooks",
3
+ "version": "0.0.0",
4
+ "description": "React hooks for the @solana/client-core Solana client",
5
+ "exports": {
6
+ "edge-light": {
7
+ "import": "./dist/index.node.mjs",
8
+ "require": "./dist/index.node.cjs"
9
+ },
10
+ "workerd": {
11
+ "import": "./dist/index.node.mjs",
12
+ "require": "./dist/index.node.cjs"
13
+ },
14
+ "browser": {
15
+ "import": "./dist/index.browser.mjs",
16
+ "require": "./dist/index.browser.cjs"
17
+ },
18
+ "node": {
19
+ "import": "./dist/index.node.mjs",
20
+ "require": "./dist/index.node.cjs"
21
+ },
22
+ "react-native": "./dist/index.native.mjs",
23
+ "types": "./dist/types/index.d.ts"
24
+ },
25
+ "browser": {
26
+ "./dist/index.node.cjs": "./dist/index.browser.cjs",
27
+ "./dist/index.node.mjs": "./dist/index.browser.mjs"
28
+ },
29
+ "main": "./dist/index.node.cjs",
30
+ "module": "./dist/index.node.mjs",
31
+ "react-native": "./dist/index.native.mjs",
32
+ "types": "./dist/types/index.d.ts",
33
+ "type": "commonjs",
34
+ "files": [
35
+ "./dist/"
36
+ ],
37
+ "sideEffects": false,
38
+ "keywords": [
39
+ "solana",
40
+ "web3",
41
+ "react",
42
+ "hooks"
43
+ ],
44
+ "scripts": {
45
+ "build": "pnpm compile:js && pnpm compile:typedefs",
46
+ "compile:js": "tsup --config ../build-scripts/tsup.config.package.ts",
47
+ "compile:typedefs": "tsc -p ./tsconfig.declarations.json",
48
+ "format": "biome check --write src",
49
+ "lint": "biome check src",
50
+ "test:typecheck": "tsc --noEmit",
51
+ "test": "vitest run --config ../../vitest.config.ts",
52
+ "typecheck": "pnpm test:typecheck"
53
+ },
54
+ "author": "Solana Labs Maintainers <maintainers@solanalabs.com>",
55
+ "license": "MIT",
56
+ "dependencies": {
57
+ "@solana/client-core": "workspace:*",
58
+ "@solana/addresses": "^5.0.0",
59
+ "@solana/codecs-core": "^5.0.0",
60
+ "@solana/errors": "^5.0.0",
61
+ "@solana/keys": "^5.0.0",
62
+ "@solana/promises": "^5.0.0",
63
+ "@solana/signers": "^5.0.0",
64
+ "@solana/transaction-messages": "^5.0.0",
65
+ "@solana/transactions": "^5.0.0",
66
+ "@solana/wallet-standard-features": "^1.3.0",
67
+ "@solana/kit": "^5.0.0",
68
+ "swr": "^2.3.6",
69
+ "zustand": "^5.0.0",
70
+ "@wallet-standard/base": "^1.1.0",
71
+ "@wallet-standard/errors": "^0.1.1",
72
+ "@wallet-standard/ui": "^1.0.1",
73
+ "@wallet-standard/ui-registry": "^1.0.1"
74
+ },
75
+ "devDependencies": {
76
+ "@types/react": "^19",
77
+ "react": "^19.0.0"
78
+ },
79
+ "peerDependencies": {
80
+ "react": ">=18"
81
+ },
82
+ "engines": {
83
+ "node": ">=20.18.0"
84
+ }
85
+ }