React hooks library for the Solana blockchain
Welcome to @gillsdk/react, a React hooks library for easily interacting with the Solana
blockchain.
Notice:
@gillsdk/reactis in active development. All APIs are subject to change until reaching the first major version (v1.0.0).
This React hooks library is built on top of two core libraries:
gill- modern JavaScript/TypeScript library for interacting with the Solana blockchain.@tanstack/react-query- popular and powerful asynchronous state management for React.
Install @gillsdk/react with your package manager of choice:
npm install gill @gillsdk/react @tanstack/react-querypnpm add gill @gillsdk/react @tanstack/react-queryyarn add gill @gillsdk/react @tanstack/react-queryNote:
gilland@tanstack/react-queryare peer dependencies of@gillsdk/reactso you need to explicitly install them. This allows you have more/easier control over managing dependencies yourself.
Setup and configure your SolanaProvider to use the gill hooks:
- Wrap your React app in a context provider
- Create a client-only provider for NextJs and React server components
- Wrap your app in the client-only provider for NextJs
- Using React hooks in React server component applications
Manage and use your Solana client's connections:
useSolanaClient- get the current Solana client (includingrpcandrpcSubscriptions)useUpdateSolanaClient- update the current Solana client (includingrpcandrpcSubscriptions)
Fetch data from the Solana blockchain with the gill hooks:
useAccount- get the account info for an addressuseBalance- get account balance (in lamports)useLatestBlockhash- get the latest blockhashuseSignatureStatuses- get signature statusesuseSignaturesForAddress- get signatures for addressuseProgramAccounts- get program accounts (GPA)useTokenMint- get a decoded token's Mint accountuseTokenAccount- get the token account for a given mint and owner (or ATA)
Send data to the Solana blockchain with the gill hooks:
useSendTransaction- send a serialized solana transaction (Base64 encoded) to the Solana blockchain
const { send, signature, isPending, isError } = useSendTransaction({
transaction: base64EncodedTransaction,
config: {
skipPreflight: true,
preflightCommitment: 'finalized'
}
});
// Send the transaction
send();Wrap your app with the SolanaProvider React context provider and pass your Solana client to it:
import { createSolanaClient } from "gill";
import { SolanaProvider } from "@gillsdk/react";
const client = createSolanaClient({
urlOrMoniker: "devnet",
});
function App() {
return <SolanaProvider client={client}>{/* ... */}</SolanaProvider>;
}For application that use React server components, like NextJS, you will need to create a "client only" wrapper for the
SolanaProvider exported from @gillsdk/react:
"use client"; // <--- this "use client" directive is required!
import { createSolanaClient } from "gill";
import { SolanaProvider } from "@gillsdk/react";
const client = createSolanaClient({
urlOrMoniker: "devnet",
});
export function SolanaProviderClient({ children }: { children: React.ReactNode }) {
return <SolanaProvider client={client}>{children}</SolanaProvider>;
}After creating your client-only provider, you can wrap your app with this
SolanaProviderClient (normally inside the
root layout.tsx):
import { SolanaProviderClient } from "@/providers/solana-provider";
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
return (
<html>
<body>
<SolanaProviderClient>{children}</SolanaProviderClient>
</body>
</html>
);
}After you have setup your client-only provider, you must set the use client directive in any component that uses the
@gillsdk/react library. Signifying this component is required to be "client only".
See React's use client directive docs.
Note: NextJs uses React server components by default. Read their docs here on the
use clientdirective.
"use client"; // <--- directive required anywhere you use `@gillsdk/react`
import { useBalance, ... } from "@gillsdk/react";
// ... other imports
export function PageClient() {
const { balance } = useBalance({
address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
});
return (
{/* ... */}
);
}Get the current Solana client configured in the SolanaProvider,
including the rpc and rpcSubscriptions connections:
"use client";
import { useSolanaClient } from "@gillsdk/react";
export function PageClient() {
const { rpc, rpcSubscriptions } = useSolanaClient();
// you can now use `rpc` to access any of the Solana JSON RPC methods
return { ... }
}Get an account's balance (in lamports) using the Solana RPC method of
getBalance:
"use client";
import { lamportsToSol } from "gill";
import { useBalance } from "@gillsdk/react";
export function PageClient() {
const { balance, isLoading, isError, error } = useBalance({
address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
});
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<p>Balance: {lamportsToSol(balance) + " SOL"}</p>
</div>
);
}Get the latest blockhash using the Solana RPC method of
getLatestBlockhash
"use client";
import { useLatestBlockhash } from "@gillsdk/react";
export function PageClient() {
const { latestBlockhash, isLoading, isError, error } = useLatestBlockhash();
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<pre>latestBlockhash: {JSON.stringify(latestBlockhash, null, "\t")}</pre>
</div>
);
}Get the account info for an address using the Solana RPC method of
getAccountInfo:
See also: useTokenMint and
useTokenAccount
"use client";
import { useAccount } from "@gillsdk/react";
export function PageClient() {
const { account, isLoading, isError, error } = useAccount({
address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
});
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<pre>account: {JSON.stringify(account, null, "\t")}</pre>
</div>
);
}You can also provide a Decoder for known account data structure in order to decode the data byte array into a typed
object:
![NOTE] Some popular account types may have their own dedicated hook, like Token Mints (
useTokenMint) and anduseTokenAccount. If a dedicated hook exists for an account type, it is highly recommended to use those hooks as opposed to manually providing adecodertouseAccount().
"use client";
import { useAccount } from "@gillsdk/react";
import { getMintDecoder } from "gill/programs";
export function PageClient() {
const { account, isLoading, isError, error } = useAccount({
// USDC mint account (on Solana mainnet)
address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
decoder: getMintDecoder(),
});
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<pre>account: {JSON.stringify(account, null, "\t")}</pre>
</div>
);
}Get the statuses of signatures using the Solana RPC method of
getSignatureStatuses:
"use client";
import { useSignatureStatuses } from "@gillsdk/react";
export function PageClient() {
const { statuses, isLoading, isError, error } = useSignatureStatuses({
signatures: ["5ewJmppABUbsWcDQEvThJj4GH4pRVK8NDjUtMVJXjvEndkhdy23mHjHpDmHVNNGoKsjPAsCwD4vzTQY4V2GEmvKu"],
});
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<pre>statuses: {JSON.stringify(statuses, null, "\t")}</pre>
</div>
);
}Get all the accounts owned by a program using the Solana RPC method of
getProgramAccounts:
"use client";
import { useProgramAccounts } from "@gillsdk/react";
export function PageClient() {
const { accounts, isLoading, isError, error } = useProgramAccounts({
program: "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T",
config: {
encoding: "base64",
filters: [
{ dataSize: 17n },
{
memcmp: {
offset: 4n,
bytes: "3Mc6vR",
encoding: "base64",
},
},
],
},
});
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<pre>accounts: {JSON.stringify(accounts, null, "\t")}</pre>
</div>
);
}Get a decoded Mint account for a given token's Mint address.
Note: the Mint's information can be accessed via the returned
account.datafield.
"use client";
import { useTokenMint } from "@gillsdk/react";
export function PageClient() {
const { account, isLoading, isError, error } = useTokenMint({
// USDC mint account (on Solana mainnet)
mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
});
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<pre>account: {JSON.stringify(account, null, "\t")}</pre>
</div>
);
}Get the token account for a given mint and owner:
"use client";
import { useTokenAccount } from "@gillsdk/react";
export function PageClient() {
const { account, isLoading, isError, error } = useTokenAccount({
// token on devnet
mint: "HwxZNMkZbZMeiu9Xnmc6Rg8jYgNsJB47jwabHGUebW4F",
owner: "nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5",
});
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<pre>account: {JSON.stringify(account, null, "\t")}</pre>
</div>
);
}If you already know the specific Associated Token Account's address (ATA), then you can get that specific token account
by providing the ata address:
Note: This is most commonly used for multi-sig protocols like Squads.
"use client";
import { useTokenAccount } from "@gillsdk/react";
export function PageClient() {
const { account, isLoading, isError, error } = useTokenAccount({
ata: "CCMCWh4FudPEmY6Q1AVi5o8mQMXkHYkJUmZfzRGdcJ9P",
});
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<pre>account: {JSON.stringify(account, null, "\t")}</pre>
</div>
);
}Get the signatures for confirmed transactions using the RPC method of getSignaturesForAddress
"use client";
import { useSignaturesForAddress } from "@gillsdk/react";
export function PageClient() {
const { signatures, isLoading, isError, error } = useSignaturesForAddress({
address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
config: {
limit: 10,
},
});
// if (isLoading) { return ... }
// if (isError) { return ... }
return (
<div className="">
<pre>signatures: {JSON.stringify(signatures, null, "\t")}</pre>
</div>
);
}