Back to Blog

Table of Contents

Highlights

Kit v7: Introducing React Hooks and Transaction Introspection

Written By

Anza Developers

July 15, 2026

Anza unveiled Solana Kit last year, a typescript SDK for building on Solana and the successor to web3.js. Yes, we do much more than just develop Agave, the dominant client on the network. And today, we’re excited to announce Solana Kit v7. Kit v7 adds two new features:

@solana/react a first-class React hook layer

@solana/transaction-introspection a package for turning RPC transaction responses into typed, parsable instructions

If you build Solana apps in React, @solana/react removes most of the boilerplate around fetching, subscribing, and performing async actions. If you're not using React, the same machinery ships as framework-agnostic reactive stores underneath. 

If you build explorers, indexers, or anything that reads confirmed transactions, @solana/transaction-introspection closes the gap between the shape of transaction that an RPC returns and what your program clients can parse.

React hooks: @solana/react

The React package is a small set of hooks for getting Solana data into your components and handling user actions. There's no required provider, so you can add them wherever you find them useful. All the hooks are safe to use with SSR (they only fetch data on the client). While re-fetching or after an error, they continue to show the last data they successfully loaded.

Reading data

useRequest fetches a value once when the component mounts and hands you a refresh function to re-fetch on demand. Build the source from a Kit RPC method and pass it in:

function LatestBlockhash() {
    const source = useMemo(() => rpc.getLatestBlockhash(), []);
    const { data, error, status, refresh } = useRequest(source);
    if (error) return <button onClick={() => refresh()}>Retry</button>;
    return <p>{data ? `Blockhash: ${data.value.blockhash}` : 'Loading…'}</p>

The source can be any async function, so useRequest isn't limited to RPC calls:


If your data source is a subscription, useSubscription automatically surfaces the latest data, with a reconnect function for recovering from a dropped connection:

function SlotHeight() {
    const source = useMemo(() => rpcSubscriptions.slotNotifications(), []);
    const { data, error, status, reconnect } = useSubscription(source);
    if (error) return <button onClick={() => reconnect()}>Reconnect</button>;
    return <p>{data ? `Slot ${data.slot}` : 'Connecting…'}</p>

useTrackedData combines the two: an initial fetch seeds the value, and a subscription keeps it live. Use it for anything that has both a current value and changes over time, like an account balance:

const spec = useMemo(
    () => ({
        initialValueSource: rpc.getBalance(address),
        initialValueMapper: lamports => lamports,
        streamSource: rpcSubscriptions.accountNotifications(address),
        streamValueMapper: ({ lamports }) => lamports,
    }),
    [address]

Actions

useAction wraps an async function and provides a dispatch function to call it, along with isRunning and error so you can reflect progress in the UI.

function SendButton({ transaction }: { transaction: Transaction }) {
    const { dispatch, isRunning, error } = useAction((signal, tx) => sendTransaction(tx, { signal }));
    return (
        <>
            <button disabled={isRunning} onClick={() => dispatch(transaction)}>
                {isRunning ? 'Sending…' : 'Send'}
            </button>
            {error ? <p role="alert">{error.message}</p>

TanStack Query and SWR

We have shipped hooks for both of these libraries, so you still get their caching, request deduplication, and devtools.

  • For TanStack Query, import useRequestQuery, useSubscriptionQuery, useTrackedDataQuery from @solana/react/query

  • For SWR, import useRequestSWR, useSubscriptionSWR, useTrackedDataSWR from @solana/react/swr

Each takes a cache key as the first argument and returns the library's native result type. Both libraries handle memoization for you:

const { data, error, isLoading, refetch } = useRequestQuery(['latestBlockhash']
Not using React?

The hooks are thin wrappers over a framework-agnostic layer that ships in the same release: reactive stores. This is where the lifecycle logic actually lives — not in the React hooks. They expose their value through a minimal subscribe / getState contract. getState() returns the same { data, error, status } shape you've seen above, and every Kit RPC request and subscription has a .reactiveStore() method that returns a store:


Coming soon

Upcoming releases will add action hooks like useSendTransaction and useConnectWallet that work with Kit plugins.

Transaction introspection: @solana/transaction-introspection

The getTransaction RPC call can be awkward to work with. Accounts are indices into a table, and instruction data is encoded bytes. Inner instructions are available under meta, but need parsing too. For v0 transactions, addressTableLookups is a separate field that you need to reconcile. Program clients generated with Codama expect Kit Instruction objects, and converting the RPC response to those is not trivial.

@solana/transaction-introspection does that bridging for you. It decodes an RPC response into instructions, resolves account indices against static keys and lookup tables, and normalizes inner instructions.

Start by decoding the response:


Then walkInstructions gives you every instruction in the transaction, including inner ones. Each entry is a fully resolved Kit Instruction, ready to be passed to program clients:


Thanks to @a_milz at Solana Foundation for contributing this package to Kit.

Coming soon

We'll generalize this to other RPC requests that return transactions with the same shape. We're adding getTransactionsForAddress to Kit, and transaction introspection will make its results even more useful.

Try it out

Everything above is documented in full, with runnable examples:

Kit is open source, and we build it in the open at anza-xyz/kit. We’re here to serve Solana developers and deliver a top notch developer experience. We look forward to hearing your feedback on Github and via our Discord channel.