Guides/React

Core hooks

The core hooks in @solana/react

Every hook on this page must be rendered under a ClientProvider. They fall into four groups: accessing the client, reading once, reading live data, and triggering actions.

Accessing the client

useClient

Reads the Kit client published by the nearest ClientProvider. Throws SOLANA_ERROR__REACT__MISSING_PROVIDER if no provider is mounted above it.

Pass your client's type through the generic so every capability you installed is typed at the call site. The simplest way is to reuse the type of the client you already built - export it once alongside the client - so the hook's type always matches your real plugin composition:

// client.ts — where you build the client
import {  } from '@solana/kit';
import {  } from '@solana/kit-plugin-rpc';
import {  } from '@solana/kit-plugin-signer';
 
export const  = ().(()).(());
export type  = <typeof >;
 
// any component
import {  } from '@solana/react';
 
function () {
    const  = <>(); // rpc, signers, … all typed
    return < ={() => ..().()}>Fetch epoch</>;
}

AppClient uses Awaited<typeof client> so it resolves the promise when a plugin is async, for a fully-synchronous client Awaited<…> is a no-op and typeof client alone would do.

useClient is a pure type-cast with no runtime check.

useClientCapability

A runtime escape hatch for the uncommon case where the client isn't precisely typed — a loosely-typed Client read from context, say — and you want a clear failure at mount instead of a downstream undefined. It reads the client, asserts a capability is installed, and narrows the return type via the generic; if the capability is absent it throws SOLANA_ERROR__REACT__MISSING_CAPABILITY with the hookName and providerHint you supply.

When you type your client with useClient<AppClient>() the compiler already guarantees the capability is present, so you rarely need this.

import { ,  } from '@solana/kit';
import {  } from '@solana/react';
 
// The provider holds a loosely-typed `Client` (built elsewhere, or by a
// third party), so `useClient` can't prove `rpc` is installed. Assert it here
// and get a clear mount-time error — with your hint — if it isn't.
function () {
    const  = <<>>({
        : 'rpc',
        : 'EpochBadge',
        : 'Install a `solanaRpc()` plugin on the client.',
    });
    return < ={() => ..().()}>Refresh epoch</>;
}

Pass an array for capability when you need more than one (e.g. ['rpc', 'rpcSubscriptions']); the same providerHint is reported for whichever is missing.

Reading once

useRequest

Fires a one-shot request on mount and re-fires whenever the source changes identity or you call refresh(). The result tracks the call's lifecycle and keeps stale data/error populated while a refresh is in flight (stale-while-revalidate). Status is fetching, success, error, or disabled (when the source is null).

Pass either a Kit request source (most commonly a PendingRpcRequest) or an async (signal) => Promise<T> function to wrap any one-shot async call. Memoize the source (useMemo) or function (useCallback) on its inputs.

import {  } from 'react';
import { ,  } from '@solana/react';
 
function () {
    const  = <>();
    const  = (() => ..(), []);
    const { , , ,  } = (, {
        : () => .(5_000),
    });
    if () return < ={() => ()}>Retry</>;
    return <>{ ? `Blockhash: ${..}` : 'Loading…'}</>;
}

The getAbortSignal factory runs on every attempt (initial fire and every refresh()), so () => AbortSignal.timeout(5_000) gives each attempt its own five-second clock.

Reading live data

useSubscription

Subscribes to a stream source and surfaces the latest notification - no initial fetch. The subscription opens on mount, re-opens when the source changes identity, and tears down on unmount. Use reconnect() to re-open manually. Status is loading, loaded, error, or disabled.

import {  } from 'react';
import {  } from '@solana/kit';
import { ,  } from '@solana/react';
 
function ({  }: { :  }) {
    const  = <>();
    const  = (
        () => ..(),
        [, ],
    );
    const { , ,  } = ();
    if () return < ={() => ()}>Reconnect</>;
    return (
        <>
            { ? `${..} lamports at slot ${..}` : 'Connecting…'}
        </>
    );
}

useTrackedData

Renders a value that loads quickly and then stays live: a one-shot fetch seeds the value, and a subscription keeps it updated. The underlying store slot-dedupes between the two sources, so out-of-order arrivals never regress the surfaced value. data is a SolanaRpcResponse<T> envelope ({ context: { slot }, value }), so you can read data.value and data.context.slot directly. Status is loading, loaded, error, or disabled.

Pass a memoized spec with an initialValueSource + initialValueMapper (the initial fetch) and a streamSource + streamValueMapper (the subscription).

import {  } from 'react';
import {  } from '@solana/kit';
import { ,  } from '@solana/react';
 
function ({  }: { :  }) {
    const  = <>();
    const  = (
        () => ({
            : ..(),
            : (: bigint) => ,
            : ..(),
            : ({  }: { : bigint }) => ,
        }),
        [, ],
    );
    const { , ,  } = ();
    if () return < ={() => ()}>Retry</>;
    return <>{ ? `${.} lamports at slot ${..}` : 'Loading…'}</>;
}

Reach for useSubscription when there is no meaningful "initial value" to fetch, reach for useTrackedData when you want a value before the first notification arrives.

Triggering actions

useAction

Wraps an arbitrary async function and tracks each invocation through React state. Each dispatch(...) runs the function with a fresh AbortSignal, dispatching again while a call is in flight aborts the first. Status is idle, running, success, or error. Use dispatch from event handlers (fire-and-forget, never throws) and dispatchAsync when you need the resolved value or to propagate errors.

import {  } from '@solana/react';
 
function ({ ,  }: { : string; : string }) {
    const { , ,  } = (async (, : string) => {
        const  = await (, { : , : 'POST',  });
        if (!.) throw new (`HTTP ${.}`);
        return (await .()) as { : string };
    });
    return (
        < ={} ={() => ()}>
            { ? 'Posting…' :  ? 'Retry' : 'Post'}
        </>
    );
}

reset() returns the action to idle and aborts any in-flight call. The isIdle / isRunning / isSuccess / isError booleans are derived from status - use whichever reads better at the call site.

On this page