Bladeren bron

docs: improve React section documentation (#211)

* docs: improve React section documentation

* docs: update package name, fix formatting, address review comments

* docs: fix @gillsdk/react formatting

* Fix: typo

* fix: few more fixes

* Update docs/content/docs/react/hooks/data-fetching.mdx

Co-authored-by: Nick Frostbutter <75431177+nickfrosty@users.noreply.github.com>

* docs: add RPC method to getProgramAccounts description

* docs: fix use client directive explanation

* docs: update token imports to gill/programs

* docs: remove inefficient NFT example

* docs: improve grammar in hook descriptions

* docs: simplify AccountExplorer example

* docs: add missing imports to WalletTransaction example

* docs: add GPA limitations warning

* docs: fix repository link in transactions

* docs: remove reference to unmerged React example app

* docs: fix error handling example to use balance instead of data

* chore: fix things again after suggestions

---------

Co-authored-by: nickfrosty <75431177+nickfrosty@users.noreply.github.com>
Gui Bibeau 2 maanden geleden
bovenliggende
commit
ec362e2ae9

+ 3 - 3
docs/content/docs/guides/tokens/create-token.mdx

@@ -75,7 +75,7 @@ console.log("signer:", signer.address);
 To use the legacy Token Program:
 
 ```ts twoslash
-import { TOKEN_PROGRAM_ADDRESS } from "gill/programs/token";
+import { TOKEN_PROGRAM_ADDRESS } from "gill/programs";
 
 const tokenProgram = TOKEN_PROGRAM_ADDRESS;
 ```
@@ -83,7 +83,7 @@ const tokenProgram = TOKEN_PROGRAM_ADDRESS;
 To use the Token Extensions Program (aka Token22):
 
 ```ts twoslash
-import { TOKEN_2022_PROGRAM_ADDRESS } from "gill/programs/token";
+import { TOKEN_2022_PROGRAM_ADDRESS } from "gill/programs";
 
 const tokenProgram = TOKEN_2022_PROGRAM_ADDRESS;
 ```
@@ -122,7 +122,7 @@ const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
 
 ```ts
 import { createTransaction } from "gill";
-import { getMintSize } from "gill/programs/token";
+import { getMintSize } from "gill/programs";
 
 const space = getMintSize();
 

+ 2 - 0
docs/content/docs/meta.json

@@ -9,6 +9,8 @@
     "examples",
     "---Getting Started---",
     "...getting-started",
+    "---React---",
+    "...react",
     "---Comparisons---",
     "...compare",
     "---Guides---",

+ 322 - 0
docs/content/docs/react/examples.mdx

@@ -0,0 +1,322 @@
+---
+title: Examples
+description: Real-world examples of using @gillsdk/react in your applications
+---
+
+Learn how to use `@gillsdk/react` through practical examples that demonstrate common patterns and
+use cases.
+
+## Basic Examples
+
+### Display Wallet Balance
+
+A simple component to display a wallet's SOL balance:
+
+```tsx
+"use client";
+
+import { lamportsToSol } from "gill";
+import { useBalance } from "@gillsdk/react";
+
+export function WalletBalance({ address }: { address: string }) {
+  const { balance, isLoading, isError, error } = useBalance({
+    address,
+    refetchInterval: 10000, // Refetch every 10 seconds
+  });
+
+  if (isLoading) return <div>Loading balance...</div>;
+  if (isError) return <div>Error: {error?.message}</div>;
+
+  return (
+    <div className="p-4 border rounded">
+      <h3 className="font-bold">Wallet Balance</h3>
+      <p className="text-2xl">{lamportsToSol(balance)} SOL</p>
+    </div>
+  );
+}
+```
+
+### Token Balance Display
+
+Display a specific SPL token balance:
+
+```tsx
+"use client";
+
+import { useTokenAccount, useTokenMint } from "@gillsdk/react";
+import { tokenAmountToUiAmount } from "@gillsdk/programs";
+
+export function TokenBalance({ mint, owner }: { mint: string; owner: string }) {
+  const { account: tokenAccount, isLoading: loadingAccount } = useTokenAccount({
+    mint,
+    owner,
+  });
+
+  const { account: mintAccount, isLoading: loadingMint } = useTokenMint({
+    mint,
+  });
+
+  if (loadingAccount || loadingMint) return <div>Loading...</div>;
+
+  const decimals = mintAccount?.data?.decimals || 0;
+  const amount = tokenAccount?.data?.amount || 0n;
+  const displayAmount = tokenAmountToUiAmount(amount, decimals);
+
+  return (
+    <div className="p-4 border rounded">
+      <h3 className="font-bold">Token Balance</h3>
+      <p className="text-2xl">{displayAmount.toLocaleString()}</p>
+    </div>
+  );
+}
+```
+
+## Advanced Examples
+
+### Transaction History
+
+Display recent transactions for an address:
+
+```tsx
+"use client";
+
+import { useSignaturesForAddress } from "@gillsdk/react";
+
+export function TransactionHistory({ address }: { address: string }) {
+  const { signatures, isLoading, refetch } = useSignaturesForAddress({
+    address,
+    config: {
+      limit: 10,
+    },
+  });
+
+  if (isLoading) return <div>Loading transactions...</div>;
+
+  return (
+    <div className="space-y-2">
+      <div className="flex justify-between items-center">
+        <h3 className="font-bold">Recent Transactions</h3>
+        <button onClick={() => refetch()} className="px-3 py-1 bg-blue-500 text-white rounded">
+          Refresh
+        </button>
+      </div>
+
+      {signatures?.map((sig) => (
+        <div key={sig.signature} className="p-3 border rounded">
+          <p className="font-mono text-sm truncate">{sig.signature}</p>
+          <div className="flex justify-between text-sm text-gray-600">
+            <span>Slot: {sig.slot}</span>
+            <span>{sig.blockTime && new Date(sig.blockTime * 1000).toLocaleString()}</span>
+          </div>
+          {sig.err && <p className="text-red-500 text-sm">Error: {JSON.stringify(sig.err)}</p>}
+        </div>
+      ))}
+    </div>
+  );
+}
+```
+
+## Integration Examples
+
+### With Next.js App Router
+
+Create a providers file:
+
+```tsx filename="app/providers.tsx"
+"use client";
+
+import { createSolanaClient } from "gill";
+import { SolanaProvider } from "@gillsdk/react";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+
+const queryClient = new QueryClient({
+  defaultOptions: {
+    queries: {
+      staleTime: 60 * 1000, // 1 minute
+      cacheTime: 10 * 60 * 1000, // 10 minutes
+    },
+  },
+});
+
+const solanaClient = createSolanaClient({
+  urlOrMoniker: process.env.NEXT_PUBLIC_RPC_URL || "mainnet",
+});
+
+export function Providers({ children }: { children: React.ReactNode }) {
+  return (
+    <QueryClientProvider client={queryClient}>
+      <SolanaProvider client={solanaClient}>{children}</SolanaProvider>
+    </QueryClientProvider>
+  );
+}
+```
+
+Use in layout:
+
+```tsx filename="app/layout.tsx"
+import { Providers } from "./providers";
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+  return (
+    <html lang="en">
+      <body>
+        <Providers>{children}</Providers>
+      </body>
+    </html>
+  );
+}
+```
+
+### With Wallet Adapter
+
+Combine `@gillsdk/react` with Solana wallet adapter:
+
+```tsx
+"use client";
+
+import { useWallet } from "@solana/wallet-adapter-react";
+import { useBalance } from "@gillsdk/react";
+import { lamportsToSol } from "gill";
+
+export function ConnectedWalletInfo() {
+  const { publicKey, connected } = useWallet();
+
+  const { balance, isLoading } = useBalance({
+    address: publicKey?.toString() || "",
+    enabled: !!publicKey, // Only fetch when wallet is connected
+  });
+
+  if (!connected) {
+    return <div>Please connect your wallet</div>;
+  }
+
+  return (
+    <div className="p-4 border rounded">
+      <p className="text-sm text-gray-600">Connected Wallet</p>
+      <p className="font-mono truncate">{publicKey?.toString()}</p>
+      {isLoading ? (
+        <p>Loading balance...</p>
+      ) : (
+        <p className="text-xl font-bold">{lamportsToSol(balance)} SOL</p>
+      )}
+    </div>
+  );
+}
+```
+
+## Performance Patterns
+
+### Dependent Queries
+
+Fetch data conditionally based on other data:
+
+```tsx
+"use client";
+
+import { useAccount, useTokenAccount } from "@gillsdk/react";
+
+export function ConditionalTokenBalance({ address }: { address: string }) {
+  // First, check if the address exists
+  const { account, isLoading: loadingAccount } = useAccount({
+    address,
+  });
+
+  // Only fetch token balance if account exists
+  const { account: tokenAccount, isLoading: loadingToken } = useTokenAccount({
+    mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
+    owner: address,
+    enabled: !!account, // Only fetch if account exists
+  });
+
+  if (loadingAccount) return <div>Checking account...</div>;
+  if (!account) return <div>Account not found</div>;
+  if (loadingToken) return <div>Loading token balance...</div>;
+
+  return (
+    <div>
+      <p>USDC Balance: {tokenAccount?.data?.amount.toString()}</p>
+    </div>
+  );
+}
+```
+
+### Optimistic Updates
+
+Update UI immediately while transaction confirms:
+
+```tsx
+"use client";
+
+import { useBalance } from "@gillsdk/react";
+import { useQueryClient } from "@tanstack/react-query";
+
+export function OptimisticBalance({ address }: { address: string }) {
+  const queryClient = useQueryClient();
+  const { balance } = useBalance({ address });
+
+  const handleTransfer = async (amount: bigint) => {
+    // Optimistically update the balance
+    queryClient.setQueryData(["gill", "balance", address], (old: bigint) => old - amount);
+
+    try {
+      // Send transaction...
+      // await sendTransaction(...)
+
+      // Refetch to get real balance
+      queryClient.invalidateQueries({
+        queryKey: ["gill", "balance", address],
+      });
+    } catch (error) {
+      // Revert optimistic update on error
+      queryClient.invalidateQueries({
+        queryKey: ["gill", "balance", address],
+      });
+    }
+  };
+
+  return (
+    <div>
+      <p>Balance: {balance?.toString()}</p>
+      {/* Transfer UI */}
+    </div>
+  );
+}
+```
+
+## Error Handling
+
+### Global Error Boundary
+
+```tsx
+"use client";
+
+import { ErrorBoundary } from "react-error-boundary";
+import { useQueryErrorResetBoundary } from "@tanstack/react-query";
+
+function ErrorFallback({ error, resetErrorBoundary }) {
+  return (
+    <div className="p-4 bg-red-50 border border-red-200 rounded">
+      <h2 className="text-red-800 font-bold">Something went wrong</h2>
+      <pre className="text-sm">{error.message}</pre>
+      <button onClick={resetErrorBoundary} className="mt-2 px-4 py-2 bg-red-600 text-white rounded">
+        Try again
+      </button>
+    </div>
+  );
+}
+
+export function AppWithErrorBoundary({ children }) {
+  const { reset } = useQueryErrorResetBoundary();
+
+  return (
+    <ErrorBoundary FallbackComponent={ErrorFallback} onReset={reset}>
+      {children}
+    </ErrorBoundary>
+  );
+}
+```
+
+## Resources
+
+- [gill sdk Documentation](/docs) - Core `gill` library documentation
+- [TanStack Query Docs](https://tanstack.com/query) - Learn more about React Query

+ 275 - 0
docs/content/docs/react/getting-started.mdx

@@ -0,0 +1,275 @@
+---
+title: Getting Started with @gillsdk/react
+description: Learn how to set up and configure @gillsdk/react in your React application
+---
+
+This guide will walk you through setting up `@gillsdk/react` in your React application, including
+configuration for Next.js App Router, Remix, and other frameworks that support React Server
+Components.
+
+## Prerequisites
+
+Before you begin, make sure you have:
+
+1. A React application (Create React App, Next.js, Vite, etc.)
+2. Node.js LTS (v22 or later) installed
+3. Basic knowledge of React hooks
+
+## Setup Steps
+
+<Steps>
+
+<Step>
+
+### Install Dependencies
+
+First, install `@gillsdk/react` along with its peer dependencies:
+
+```package-install
+gill @gillsdk/react @tanstack/react-query
+```
+
+</Step>
+
+<Step>
+
+### Create a Solana Client
+
+Create a Solana client using gill's `createSolanaClient` function:
+
+```typescript
+import { createSolanaClient } from "gill";
+
+const client = createSolanaClient({
+  urlOrMoniker: "devnet", // or "mainnet", "testnet", or a custom RPC URL
+});
+```
+
+<Callout type="warn">
+  The public Solana RPC endpoints are subject to rate limits and should not be used in production.
+  For production applications, use a dedicated RPC provider like Helius, QuickNode, or Alchemy.
+</Callout>
+
+</Step>
+
+<Step>
+
+### Wrap Your App with SolanaProvider
+
+The `SolanaProvider` is a React context provider that makes the Solana client available to all
+`@gillsdk/react` hooks in your component tree.
+
+#### For Standard React Apps
+
+```tsx
+import { createSolanaClient } from "gill";
+import { SolanaProvider } from "@gillsdk/react";
+
+const client = createSolanaClient({
+  urlOrMoniker: "devnet",
+});
+
+function App() {
+  return <SolanaProvider client={client}>{/* Your app components */}</SolanaProvider>;
+}
+
+export default App;
+```
+
+#### For Next.js App Router and React Server Components
+
+For applications that use React Server Components (like Next.js App Router, Remix, or other
+RSC-enabled frameworks), you need to create a client-side wrapper component.
+
+<Callout type="info" title='What is the "use client" directive?'>
+  
+  The `"use client"` directive tells the bundler that this
+  component and its children should run on the client side (in the browser) rather than on the
+  server. This is necessary because:
+  
+  - `@gillsdk/react` uses React hooks (`useState`, `useEffect`, etc.)
+  which only work on the client
+  - The provider creates and hooks consume React context, which is a
+  client-side feature
+  - Without this directive, you'll get errors about using hooks in Server Components.
+
+</Callout>
+
+Here's how to create a client-side provider wrapper:
+
+```tsx filename="app/providers/solana-provider.tsx"
+"use client"; // Required - marks this as a Client Component
+
+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>;
+}
+```
+
+Then wrap your app in the root layout with this `SolanaProviderClient` component:
+
+```tsx filename="app/layout.tsx"
+import { SolanaProviderClient } from "@/providers/solana-provider";
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+  return (
+    <html lang="en">
+      <body>
+        <SolanaProviderClient>{children}</SolanaProviderClient>
+      </body>
+    </html>
+  );
+}
+```
+
+</Step>
+
+<Step>
+
+### Use @gillsdk/react Hooks
+
+Now you can use any `@gillsdk/react` hook in your components. When using frameworks with React
+Server Components (like Next.js App Router), remember to add the `"use client"` directive to any
+component that uses `@gillsdk/react` hooks:
+
+```tsx filename="app/components/balance.tsx"
+"use client"; // Required when using React Server Components
+
+import { lamportsToSol } from "gill";
+import { useBalance } from "@gillsdk/react";
+
+export function WalletBalance() {
+  const { balance, isLoading, isError, error } = useBalance({
+    address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
+  });
+
+  if (isLoading) return <div>Loading balance...</div>;
+  if (isError) return <div>Error: {error?.message}</div>;
+
+  return (
+    <div>
+      <p>Balance: {lamportsToSol(balance)} SOL</p>
+    </div>
+  );
+}
+```
+
+</Step>
+
+</Steps>
+
+## Configuration Options
+
+### Custom RPC Endpoints
+
+For production applications, configure a dedicated RPC endpoint:
+
+```typescript
+const client = createSolanaClient({
+  urlOrMoniker: "https://your-rpc-provider.com/your-api-key",
+});
+```
+
+### Popular RPC Providers
+
+- [Helius](https://helius.dev)
+- [QuickNode](https://quicknode.com)
+- [Alchemy](https://alchemy.com)
+- [Triton](https://triton.one)
+- [Syndica](https://syndica.io)
+
+### Multiple Clients
+
+You can manage multiple Solana clients using the `useUpdateSolanaClient` hook:
+
+```tsx
+"use client";
+
+import { createSolanaClient } from "gill";
+import { useUpdateSolanaClient } from "@gillsdk/react";
+
+function NetworkSwitcher() {
+  const updateClient = useUpdateSolanaClient();
+
+  const switchToMainnet = () => {
+    const mainnetClient = createSolanaClient({
+      urlOrMoniker: "mainnet",
+    });
+    updateClient(mainnetClient);
+  };
+
+  return <button onClick={switchToMainnet}>Switch to Mainnet</button>;
+}
+```
+
+## TypeScript Configuration
+
+`@gillsdk/react` is fully typed out of the box. No additional TypeScript configuration is required.
+However, ensure your `tsconfig.json` has:
+
+```json
+{
+  "compilerOptions": {
+    "strict": true,
+    "esModuleInterop": true,
+    "skipLibCheck": true,
+    "jsx": "react-jsx" // or "preserve" for Next.js
+  }
+}
+```
+
+## Troubleshooting
+
+### "use client" Errors
+
+If you see errors like "You're importing a component that needs useEffect. It only works in a Client
+Component" or "useState only works in Client Components", this means you're trying to use
+`@gillsdk/react` in a Server Component. (which NextJS App router does by default unless the
+`"use client"` directive is explicitly used).
+
+**Why this happens:**
+
+- React Server Components run on the server during build/request time
+- `@gillsdk/react` uses browser-specific features like React hooks and Web APIs
+- The Solana client needs to make RPC calls from the browser
+
+**To fix:**
+
+1. Add `"use client"` at the top of any file using `@gillsdk/react` hooks
+2. Ensure your `SolanaProvider` wrapper has `"use client"` at the top
+3. Make sure the provider is properly wrapped around your component tree
+
+**Common scenarios:**
+
+- Forgot to add `"use client"` to a component using
+  [`useBalance`](/docs/react/hooks/data-fetching#usebalance),
+  [`useAccount`](/docs/react/hooks/data-fetching#useaccount), etc.
+- Trying to use `@gillsdk/react` hooks directly in a `page.tsx` without the directive
+- Importing a Client Component into a Server Component without proper boundaries
+
+### RPC Rate Limiting
+
+If you're hitting rate limits:
+
+1. Switch from public endpoints to a dedicated RPC provider
+2. Implement request batching and caching strategies
+3. Consider using the `staleTime` and `cacheTime` options in hooks
+
+### Bundle Size
+
+To minimize bundle size:
+
+1. Import only the hooks you need
+2. Ensure tree-shaking is enabled in your bundler
+3. Use dynamic imports for heavy components
+
+---
+
+Ready to start using `@gillsdk/react`? Check out the [available hooks](/docs/react/hooks) to begin
+fetching blockchain data in your React application.

+ 264 - 0
docs/content/docs/react/hooks/client.mdx

@@ -0,0 +1,264 @@
+---
+title: Client Management Hooks
+description: Hooks for managing and accessing the Solana client connection
+---
+
+These hooks allow you to access and manage the Solana client configured in your `SolanaProvider`.
+
+## useSolanaClient
+
+Get the current Solana client configured in the `SolanaProvider`, including the `rpc` and
+`rpcSubscriptions` connections.
+
+### Usage
+
+```tsx
+"use client";
+
+import { useSolanaClient } from "@gillsdk/react";
+
+export function MyComponent() {
+  const { rpc, rpcSubscriptions } = useSolanaClient();
+
+  // You can now use rpc to access any Solana JSON RPC methods
+  const getSlot = async () => {
+    const slot = await rpc.getSlot().send();
+    console.log("Current slot:", slot);
+  };
+
+  return <button onClick={getSlot}>Get Current Slot</button>;
+}
+```
+
+### Return Value
+
+```typescript
+interface SolanaClient {
+  rpc: SolanaRpc;
+  rpcSubscriptions: SolanaRpcSubscriptions;
+  sendAndConfirmTransaction: SendAndConfirmTransaction;
+}
+```
+
+### Advanced Example
+
+Using the client to make custom RPC calls:
+
+```tsx
+"use client";
+
+import { useSolanaClient } from "@gillsdk/react";
+import type { Address } from "gill";
+
+export function AccountExplorer({ accountAddress }: { accountAddress: Address }) {
+  const { rpc } = useSolanaClient();
+  const [accountInfo, setAccountInfo] = useState(null);
+
+  const fetchAccountInfo = async () => {
+    try {
+      const info = await rpc
+        .getAccountInfo(accountAddress, {
+          encoding: "base64",
+        })
+        .send();
+
+      setAccountInfo(info);
+    } catch (error) {
+      console.error("Failed to fetch account info:", error);
+    }
+  };
+
+  return (
+    <div>
+      <button onClick={fetchAccountInfo}>Fetch Account Info</button>
+      {accountInfo && <pre>{JSON.stringify(accountInfo, null, 2)}</pre>}
+    </div>
+  );
+}
+```
+
+## useUpdateSolanaClient
+
+Update the current Solana client in the `SolanaProvider`. This is useful for switching between
+different networks or RPC endpoints.
+
+### Usage
+
+```tsx
+"use client";
+
+import { createSolanaClient } from "gill";
+import type { SolanaClusterMoniker } from "gill";
+import { useUpdateSolanaClient } from "@gillsdk/react";
+
+export function NetworkSwitcher() {
+  const updateClient = useUpdateSolanaClient();
+
+  const switchNetwork = (network: SolanaClusterMoniker) => {
+    const newClient = createSolanaClient({
+      urlOrMoniker: network,
+    });
+    updateClient(newClient);
+  };
+
+  return (
+    <div>
+      <button onClick={() => switchNetwork("mainnet")}>Mainnet</button>
+      <button onClick={() => switchNetwork("devnet")}>Devnet</button>
+      <button onClick={() => switchNetwork("testnet")}>Testnet</button>
+    </div>
+  );
+}
+```
+
+### Parameters
+
+```typescript
+updateClient(client: SolanaClient): void
+```
+
+- `client` - The new Solana client to set
+
+### Advanced Example
+
+Switching to a custom RPC endpoint with error handling:
+
+```tsx
+"use client";
+
+import { createSolanaClient } from "gill";
+import type { SolanaClientUrlOrMoniker } from "gill";
+import { useUpdateSolanaClient, useSolanaClient } from "@gillsdk/react";
+
+export function CustomRPCManager() {
+  const currentClient = useSolanaClient();
+  const updateClient = useUpdateSolanaClient();
+  const [rpcUrl, setRpcUrl] = useState("");
+  const [isConnecting, setIsConnecting] = useState(false);
+
+  const connectToCustomRPC = async () => {
+    if (!rpcUrl) return;
+
+    setIsConnecting(true);
+    try {
+      const newClient = createSolanaClient({
+        urlOrMoniker: rpcUrl,
+      });
+
+      // Test the connection
+      await newClient.rpc.getSlot().send();
+
+      // If successful, update the client
+      updateClient(newClient);
+      alert("Successfully connected to custom RPC!");
+    } catch (error) {
+      alert(`Failed to connect: ${error.message}`);
+    } finally {
+      setIsConnecting(false);
+    }
+  };
+
+  return (
+    <div>
+      <input
+        type="text"
+        value={rpcUrl}
+        onChange={(e) => setRpcUrl(e.target.value)}
+        placeholder="Enter custom RPC URL"
+      />
+      <button onClick={connectToCustomRPC} disabled={isConnecting}>
+        {isConnecting ? "Connecting..." : "Connect"}
+      </button>
+    </div>
+  );
+}
+```
+
+## Best Practices
+
+### Client Initialization
+
+Initialize your client with appropriate configuration for your use case:
+
+```typescript
+// Development
+const devClient = createSolanaClient({
+  urlOrMoniker: "devnet",
+});
+
+// Production with custom RPC
+const prodClient = createSolanaClient({
+  urlOrMoniker: process.env.NEXT_PUBLIC_RPC_URL || "mainnet",
+});
+
+// With custom configuration
+const customClient = createSolanaClient({
+  urlOrMoniker: "https://your-rpc.com",
+  config: {
+    commitment: "confirmed",
+    wsEndpoint: "wss://your-rpc.com/ws",
+  },
+});
+```
+
+### Network Switching
+
+When implementing network switching, consider:
+
+1. **Clear caches** when switching networks to avoid stale data
+2. **Update wallet connections** if using wallet adapters
+3. **Notify users** of network changes
+4. **Persist preferences** in local storage
+
+```tsx
+function NetworkManager() {
+  const updateClient = useUpdateSolanaClient();
+  const queryClient = useQueryClient();
+
+  const switchNetwork = async (network: SolanaClientUrlOrMoniker) => {
+    // Create new client
+    const newClient = createSolanaClient({
+      urlOrMoniker: network,
+    });
+
+    // Update client
+    updateClient(newClient);
+
+    // Clear all cached queries
+    await queryClient.invalidateQueries();
+
+    // Save preference
+    localStorage.setItem("preferred-network", network);
+  };
+
+  // ... rest of component
+}
+```
+
+## Troubleshooting
+
+### Client Not Available
+
+If `useSolanaClient` returns undefined, ensure:
+
+1. Your component is wrapped in `SolanaProvider`
+2. The provider is properly initialized with a client
+3. You're not calling the hook during SSR (use `"use client"` directive)
+
+### RPC Connection Issues
+
+When updating clients, handle connection failures gracefully:
+
+```tsx
+const updateClientSafely = async (url: SolanaClientUrlOrMoniker) => {
+  try {
+    const newClient = createSolanaClient({ urlOrMoniker: url });
+    // Test connection
+    await newClient.rpc.getHealth().send();
+    updateClient(newClient);
+  } catch (error) {
+    console.error("Failed to connect to RPC:", error);
+    // Fallback to previous client or default
+  }
+};
+```

+ 513 - 0
docs/content/docs/react/hooks/data-fetching.mdx

@@ -0,0 +1,513 @@
+---
+title: Data Fetching Hooks
+description: Hooks for fetching data from the Solana blockchain
+---
+
+These hooks provide easy access to common Solana blockchain data with automatic caching, refetching,
+and error handling.
+
+## useAccount
+
+Get the account info for an address using the Solana RPC method
+[`getAccountInfo`](https://solana.com/docs/rpc/http/getaccountinfo).
+
+### Usage
+
+```tsx
+"use client";
+
+import { useAccount } from "@gillsdk/react";
+
+export function AccountInfo() {
+  const { account, isLoading, isError, error } = useAccount({
+    address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
+  });
+
+  if (isLoading) return <div>Loading...</div>;
+  if (isError) return <div>Error: {error?.message}</div>;
+
+  return (
+    <div>
+      <pre>{JSON.stringify(account, null, 2)}</pre>
+    </div>
+  );
+}
+```
+
+### With Decoder
+
+Fetch and decode an account's data into a typed object using an appropriate `decoder` function:
+
+```tsx
+"use client";
+
+import { useAccount } from "@gillsdk/react";
+import { getMintDecoder } from "gill/programs";
+
+export function MintInfo() {
+  const { account, isLoading } = useAccount({
+    // USDC mint account on mainnet
+    address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
+    decoder: getMintDecoder(),
+  });
+
+  if (isLoading) return <div>Loading...</div>;
+
+  return (
+    <div>
+      <p>Supply: {account?.data?.supply.toString()}</p>
+      <p>Decimals: {account?.data?.decimals}</p>
+    </div>
+  );
+}
+```
+
+### Parameters
+
+```typescript
+interface UseAccountOptions {
+  address: Address | string;
+  decoder?: Decoder<T>;
+  commitment?: Commitment;
+  minContextSlot?: number;
+  enabled?: boolean;
+  // ... other TanStack Query options
+}
+```
+
+## useBalance
+
+Get an account's balance in lamports using
+[`getBalance`](https://solana.com/docs/rpc/http/getbalance).
+
+### Usage
+
+```tsx
+"use client";
+
+import { lamportsToSol } from "gill";
+import { useBalance } from "@gillsdk/react";
+
+export function WalletBalance() {
+  const { balance, isLoading, isError, error } = useBalance({
+    address: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
+  });
+
+  if (isLoading) return <div>Loading...</div>;
+  if (isError) return <div>Error: {error?.message}</div>;
+
+  return (
+    <div>
+      <p>Balance: {lamportsToSol(balance)} SOL</p>
+    </div>
+  );
+}
+```
+
+### With Automatic Refetch
+
+```tsx
+const { balance } = useBalance({
+  address: walletAddress,
+  refetchInterval: 5000, // Refetch every 5 seconds
+  refetchOnWindowFocus: true,
+});
+```
+
+### Parameters
+
+```typescript
+interface UseBalanceOptions {
+  address: Address | string;
+  commitment?: Commitment;
+  minContextSlot?: number;
+  enabled?: boolean;
+  // ... other TanStack Query options
+}
+```
+
+## useLatestBlockhash
+
+Get the latest blockhash using
+[`getLatestBlockhash`](https://solana.com/docs/rpc/http/getlatestblockhash).
+
+### Usage
+
+```tsx
+"use client";
+
+import { useLatestBlockhash } from "@gillsdk/react";
+
+export function BlockhashDisplay() {
+  const { latestBlockhash, isLoading } = useLatestBlockhash();
+
+  if (isLoading) return <div>Loading...</div>;
+
+  return (
+    <div>
+      <p>Blockhash: {latestBlockhash?.blockhash}</p>
+      <p>Last Valid Height: {latestBlockhash?.lastValidBlockHeight}</p>
+    </div>
+  );
+}
+```
+
+### For Transactions
+
+```tsx
+const { latestBlockhash } = useLatestBlockhash();
+
+// Use in transaction
+const transaction = createTransaction({
+  blockhash: latestBlockhash.blockhash,
+  // ... other transaction params
+});
+```
+
+### Parameters
+
+```typescript
+interface UseLatestBlockhashOptions {
+  commitment?: Commitment;
+  minContextSlot?: number;
+  enabled?: boolean;
+  // ... other TanStack Query options
+}
+```
+
+## useSignatureStatuses
+
+Get the statuses of signatures using
+[`getSignatureStatuses`](https://solana.com/docs/rpc/http/getSignatureStatuses).
+
+### Usage
+
+```tsx
+"use client";
+
+import { useSignatureStatuses } from "@gillsdk/react";
+
+export function TransactionStatus({ signature }) {
+  const { statuses, isLoading } = useSignatureStatuses({
+    signatures: [signature],
+  });
+
+  if (isLoading) return <div>Checking status...</div>;
+
+  const status = statuses?.[0];
+  return (
+    <div>
+      <p>Confirmed: {status?.confirmationStatus}</p>
+      <p>Confirmations: {status?.confirmations}</p>
+      {status?.err && <p>Error: {JSON.stringify(status.err)}</p>}
+    </div>
+  );
+}
+```
+
+### Multiple Signatures
+
+```tsx
+const { statuses } = useSignatureStatuses({
+  signatures: [sig1, sig2, sig3],
+  searchTransactionHistory: true,
+});
+
+statuses?.forEach((status, index) => {
+  console.log(`Signature ${index}:`, status);
+});
+```
+
+### Parameters
+
+```typescript
+interface UseSignatureStatusesOptions {
+  signatures: string[];
+  searchTransactionHistory?: boolean;
+  enabled?: boolean;
+  // ... other TanStack Query options
+}
+```
+
+## useSignaturesForAddress
+
+Get signatures for confirmed transactions using
+[`getSignaturesForAddress`](https://solana.com/docs/rpc/http/getsignaturesforaddress).
+
+### Usage
+
+```tsx
+"use client";
+
+import { useSignaturesForAddress } from "@gillsdk/react";
+
+export function TransactionHistory({ address }) {
+  const { signatures, isLoading } = useSignaturesForAddress({
+    address,
+    config: {
+      limit: 10,
+    },
+  });
+
+  if (isLoading) return <div>Loading history...</div>;
+
+  return (
+    <ul>
+      {signatures?.map((sig) => (
+        <li key={sig.signature}>
+          <p>Signature: {sig.signature}</p>
+          <p>Slot: {sig.slot}</p>
+          <p>Time: {new Date(sig.blockTime * 1000).toLocaleString()}</p>
+        </li>
+      ))}
+    </ul>
+  );
+}
+```
+
+### With Pagination
+
+```tsx
+const { signatures, refetch } = useSignaturesForAddress({
+  address,
+  config: {
+    limit: 20,
+    before: lastSignature, // For pagination
+    until: firstSignature,
+  },
+});
+```
+
+### Parameters
+
+```typescript
+interface UseSignaturesForAddressOptions {
+  address: Address | string;
+  config?: {
+    limit?: number;
+    before?: string;
+    until?: string;
+    commitment?: Commitment;
+  };
+  enabled?: boolean;
+  // ... other TanStack Query options
+}
+```
+
+## useProgramAccounts
+
+Get all accounts owned by a program using the
+[`getProgramAccounts`](https://solana.com/docs/rpc/http/getProgramAccounts) RPC method.
+
+<Callout type="warn" title="Important getProgramAccounts (GPA) Limitations">
+
+- `getProgramAccounts` can return large amounts of data and is resource-intensive
+- Many RPC providers return errors or have rate limits due to the system load
+- Public RPC endpoints often have `getProgramAccounts` disabled entirely
+- Consider using filters to reduce the data returned and always use a paid RPC provider for
+  production
+- For large programs, consider alternative approaches like indexers or specialized APIs
+
+</Callout>
+
+### Usage
+
+```tsx
+"use client";
+
+import { useProgramAccounts } from "@gillsdk/react";
+
+export function ProgramAccounts() {
+  const { accounts, isLoading } = useProgramAccounts({
+    program: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
+    config: {
+      encoding: "base64",
+      filters: [
+        { dataSize: 165n }, // Token account size
+      ],
+    },
+  });
+
+  if (isLoading) return <div>Loading accounts...</div>;
+
+  return (
+    <div>
+      <p>Found {accounts?.length} accounts</p>
+    </div>
+  );
+}
+```
+
+### With Memcmp Filter
+
+```tsx
+const { accounts } = useProgramAccounts({
+  program: programId,
+  config: {
+    filters: [
+      {
+        memcmp: {
+          offset: 0n,
+          bytes: base58.encode(ownerPubkey),
+          encoding: "base58",
+        },
+      },
+    ],
+  },
+});
+```
+
+### Parameters
+
+```typescript
+interface UseProgramAccountsOptions {
+  program: Address | string;
+  config?: {
+    encoding?: "base64" | "jsonParsed";
+    filters?: Filter[];
+    commitment?: Commitment;
+    minContextSlot?: number;
+    withContext?: boolean;
+  };
+  decoder?: Decoder<T>;
+  enabled?: boolean;
+  // ... other TanStack Query options
+}
+```
+
+## useTokenMint
+
+Get a decoded [Mint account](https://solana.com/docs/tokens#mint-account) for a token.
+
+### Usage
+
+```tsx
+"use client";
+
+import { useTokenMint } from "@gillsdk/react";
+
+export function TokenInfo() {
+  const { account, isLoading } = useTokenMint({
+    // USDC mint on mainnet
+    mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
+  });
+
+  if (isLoading) return <div>Loading...</div>;
+
+  const mintData = account?.data;
+  return (
+    <div>
+      <p>Supply: {mintData?.supply.toString()}</p>
+      <p>Decimals: {mintData?.decimals}</p>
+      <p>Mint Authority: {mintData?.mintAuthority?.toString()}</p>
+    </div>
+  );
+}
+```
+
+### Parameters
+
+```typescript
+interface UseTokenMintOptions {
+  mint: Address | string;
+  commitment?: Commitment;
+  enabled?: boolean;
+  // ... other TanStack Query options
+}
+```
+
+## useTokenAccount
+
+Get the decoded [Token Account](https://solana.com/docs/tokens#token-account) for a given mint and
+owner, automatically deriving the Associated Token Account (ATA) address:
+
+### Usage
+
+```tsx
+"use client";
+
+import { useTokenAccount } from "@gillsdk/react";
+
+export function TokenBalance() {
+  const { account, isLoading } = useTokenAccount({
+    mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
+    owner: "nicktrLHhYzLmoVbuZQzHUTicd2sfP571orwo9jfc8c",
+  });
+
+  if (isLoading) return <div>Loading...</div>;
+
+  const tokenData = account?.data;
+  return (
+    <div>
+      <p>Balance: {tokenData?.amount.toString()}</p>
+      <p>Delegated: {tokenData?.delegatedAmount.toString()}</p>
+    </div>
+  );
+}
+```
+
+### With ATA Address
+
+If you need to fetch and decode a specific Token Account's address (like for ancillary token
+accounts), you can manually derive the address and provide it to the hook:
+
+```tsx
+const { account } = useTokenAccount({
+  ata: "CCMCWh4FudPEmY6Q1AVi5o8mQMXkHYkJUmZfzRGdcJ9P",
+});
+```
+
+### Parameters
+
+```typescript
+interface UseTokenAccountOptions {
+  // Option 1: Provide mint and owner
+  mint?: Address | string;
+  owner?: Address | string;
+
+  // Option 2: Provide ATA address directly
+  ata?: Address | string;
+
+  commitment?: Commitment;
+  enabled?: boolean;
+  // ... other TanStack Query options
+}
+```
+
+## Performance Optimization
+
+### Conditional Fetching
+
+```tsx
+const { balance } = useBalance({
+  address: walletAddress,
+  enabled: !!walletAddress, // Only fetch if address exists
+});
+```
+
+### Stale Time Configuration
+
+```tsx
+const { account } = useAccount({
+  address,
+  staleTime: 30000, // Consider data fresh for 30 seconds
+  cacheTime: 300000, // Keep in cache for 5 minutes
+});
+```
+
+### Query Invalidation
+
+```tsx
+import { useQueryClient } from "@tanstack/react-query";
+
+function RefreshButton() {
+  const queryClient = useQueryClient();
+
+  const refreshAll = () => {
+    // Invalidate all `@gillsdk/react` queries
+    queryClient.invalidateQueries({ queryKey: ["gill"] });
+  };
+
+  return <button onClick={refreshAll}>Refresh All</button>;
+}
+```

+ 158 - 0
docs/content/docs/react/hooks/index.mdx

@@ -0,0 +1,158 @@
+---
+title: React Hooks Reference
+description: Complete reference for all available hooks in @gillsdk/react
+---
+
+`@gillsdk/react` provides a comprehensive set of React hooks for interacting with the Solana
+blockchain. All hooks are built on top of TanStack Query, providing automatic caching, background
+refetching, and error handling.
+
+## Hook Categories
+
+### Client Management
+
+Hooks for managing and accessing the Solana client connection:
+
+- [`useSolanaClient`](/docs/react/hooks/client#usesolanaclient) - Get the current Solana client
+- [`useUpdateSolanaClient`](/docs/react/hooks/client#useupdatesolanaclient) - Update the Solana
+  client
+
+### Data Fetching
+
+Hooks for fetching data from the Solana blockchain:
+
+- [`useAccount`](/docs/react/hooks/data-fetching#useaccount) - Get account info and data
+- [`useBalance`](/docs/react/hooks/data-fetching#usebalance) - Get an account's balance
+- [`useLatestBlockhash`](/docs/react/hooks/data-fetching#uselatestblockhash) - Get the latest
+  blockhash
+- [`useSignatureStatuses`](/docs/react/hooks/data-fetching#usesignaturestatuses) - Get the statuses
+  of signatures
+- [`useSignaturesForAddress`](/docs/react/hooks/data-fetching#usesignaturesforaddress) - Get
+  signatures for an address
+- [`useProgramAccounts`](/docs/react/hooks/data-fetching#useprogramaccounts) - Get all accounts
+  owned by a program
+- [`useTokenMint`](/docs/react/hooks/data-fetching#usetokenmint) - Get a token mint account
+- [`useTokenAccount`](/docs/react/hooks/data-fetching#usetokenaccount) - Get a token account
+
+### Transaction Handling
+
+Hooks for sending and managing transactions (coming soon):
+
+- `useSendTransaction` - Send transactions with automatic confirmation
+- `useSimulateTransaction` - Simulate transactions before sending
+
+## Common Hook Options
+
+All data fetching hooks in `@gillsdk/react` accept standard TanStack Query options:
+
+```typescript
+interface HookOptions {
+  // Query key for caching
+  queryKey?: QueryKey;
+
+  // How often to refetch in milliseconds
+  refetchInterval?: number;
+
+  // Whether to refetch on window focus
+  refetchOnWindowFocus?: boolean;
+
+  // Whether to refetch on reconnect
+  refetchOnReconnect?: boolean;
+
+  // Time in milliseconds before data is considered stale
+  staleTime?: number;
+
+  // Time in milliseconds to keep data in cache
+  cacheTime?: number;
+
+  // Whether to enable the query
+  enabled?: boolean;
+
+  // Retry configuration
+  retry?: boolean | number | RetryConfig;
+}
+```
+
+## Common Return Values
+
+All data fetching hooks return a consistent shape based on TanStack Query:
+
+```typescript
+interface HookResult<T> {
+  // The fetched data - hooks return specific field names like `account`, `balance`, etc. instead of generic `data`
+  account?: T; // or balance, signatures, etc. depending on the hook
+
+  // Loading state
+  isLoading: boolean;
+  isFetching: boolean;
+
+  // Error state
+  isError: boolean;
+  error: Error | null;
+
+  // Success state
+  isSuccess: boolean;
+
+  // Refetch function
+  refetch: () => void;
+
+  // Query status
+  status: "loading" | "error" | "success" | "idle";
+}
+```
+
+## Error Handling
+
+All hooks provide built-in error handling. You can handle errors at the component level:
+
+```tsx
+function MyComponent() {
+  const { balance, isError, error } = useBalance({
+    address: "...",
+  });
+
+  if (isError) {
+    return <div>Error: {error?.message}</div>;
+  }
+
+  // ... rest of component
+}
+```
+
+Or configure global error handling through TanStack Query:
+
+```tsx
+import { QueryClient } from "@tanstack/react-query";
+
+const queryClient = new QueryClient({
+  defaultOptions: {
+    queries: {
+      onError: (error) => {
+        console.error("Query error:", error);
+        // Handle error globally
+      },
+    },
+  },
+});
+```
+
+## Performance Tips
+
+1. **Use `staleTime` wisely** - Set appropriate stale times to reduce unnecessary refetches
+2. **Enable queries conditionally** - Use the `enabled` option to prevent unnecessary queries
+3. **Batch requests** - Multiple hooks can share the same query key to deduplicate requests
+4. **Implement pagination** - For large datasets, use pagination parameters
+
+## TypeScript Support
+
+All hooks are fully typed with TypeScript. Import types from `gill` for complete type safety:
+
+```typescript
+import type { Address, Lamports } from "gill";
+import { useBalance } from "@gillsdk/react";
+
+function Balance({ address }: { address: Address }) {
+  const { balance } = useBalance({ address });
+  // balance is typed as `Lamports | undefined`
+}
+```

+ 5 - 0
docs/content/docs/react/hooks/meta.json

@@ -0,0 +1,5 @@
+{
+  "title": "Hooks",
+  "defaultOpen": false,
+  "pages": ["index", "client", "data-fetching", "transactions"]
+}

+ 148 - 0
docs/content/docs/react/hooks/transactions.mdx

@@ -0,0 +1,148 @@
+---
+title: Transaction Hooks
+description: Hooks for sending and managing Solana transactions
+---
+
+<Callout type="info">
+  Transaction hooks are coming soon to `@gillsdk/react`. This page documents the planned API.
+</Callout>
+
+These hooks will provide an easy way to send, confirm, and manage Solana transactions with automatic
+error handling and retry logic.
+
+## Planned Hooks
+
+### useSendTransaction
+
+Send and confirm transactions with automatic retry and confirmation handling.
+
+```tsx
+// Coming soon
+const { sendTransaction, isLoading, error } = useSendTransaction();
+
+const handleTransfer = async () => {
+  const signature = await sendTransaction({
+    transaction,
+    signers: [keypair],
+    options: {
+      skipPreflight: false,
+      commitment: "confirmed",
+    },
+  });
+};
+```
+
+### useSimulateTransaction
+
+Simulate transactions before sending to check for errors.
+
+```tsx
+// Coming soon
+const { simulate, result } = useSimulateTransaction();
+
+const checkTransaction = async () => {
+  const simulation = await simulate({
+    transaction,
+    signers: [keypair],
+  });
+
+  if (simulation.err) {
+    console.error("Transaction would fail:", simulation.err);
+  }
+};
+```
+
+### useTransactionStatus
+
+Monitor transaction confirmation status in real-time.
+
+```tsx
+// Coming soon
+const { status, confirmations } = useTransactionStatus({
+  signature: "...",
+  targetConfirmations: 10,
+});
+```
+
+## Current Approach
+
+Until transaction hooks are available, you can use the Solana client directly:
+
+```tsx
+"use client";
+
+import { useSolanaClient } from "@gillsdk/react";
+import { createTransaction } from "gill";
+
+export function SendTransaction() {
+  const { rpc, sendAndConfirmTransaction } = useSolanaClient();
+  const [isLoading, setIsLoading] = useState(false);
+
+  const handleSend = async () => {
+    setIsLoading(true);
+    try {
+      // Create your transaction
+      const transaction = createTransaction({
+        // ... transaction params
+      });
+
+      // Send and confirm
+      const signature = await sendAndConfirmTransaction(transaction, { commitment: "confirmed" });
+
+      console.log("Transaction confirmed:", signature);
+    } catch (error) {
+      console.error("Transaction failed:", error);
+    } finally {
+      setIsLoading(false);
+    }
+  };
+
+  return (
+    <button onClick={handleSend} disabled={isLoading}>
+      {isLoading ? "Sending..." : "Send Transaction"}
+    </button>
+  );
+}
+```
+
+## With Wallet Adapter
+
+If you're using a wallet adapter, combine it with `@gillsdk/react`:
+
+```tsx
+"use client";
+
+import { useWallet } from "@solana/wallet-adapter-react";
+import { useSolanaClient } from "@gillsdk/react";
+import { createTransaction } from "gill";
+
+export function WalletTransaction() {
+  const { publicKey, signTransaction } = useWallet();
+  const { rpc } = useSolanaClient();
+
+  const sendTransaction = async () => {
+    if (!publicKey || !signTransaction) return;
+
+    // Create transaction
+    const transaction = createTransaction({
+      // ... params
+    });
+
+    // Sign with wallet
+    const signedTx = await signTransaction(transaction);
+
+    // Send via RPC
+    const signature = await rpc.sendTransaction(signedTx, { skipPreflight: false }).send();
+
+    console.log("Sent:", signature);
+  };
+
+  // ... rest of component
+}
+```
+
+## Contributing
+
+Transaction hooks are actively being developed. If you have suggestions or want to contribute,
+please check out the
+[`@gillsdk/react` repository](https://github.com/gillsdk/gill/tree/master/packages/react) on GitHub.

+ 87 - 0
docs/content/docs/react/index.mdx

@@ -0,0 +1,87 @@
+---
+title: React Hooks for Solana
+description:
+  A React hooks library for easily interacting with the Solana blockchain, built on top of gill
+---
+
+Welcome to `@gillsdk/react`, a React hooks library for easily interacting with the
+[Solana](http://solana.com/) blockchain.
+
+<Callout type="info">
+  `@gillsdk/react` is in active development. All APIs are subject to change until reaching the first
+  major version (v1.0.0).
+</Callout>
+
+This React hooks library is built on top of two core libraries:
+
+1. [`gill`](https://www.npmjs.com/package/gill) - modern JavaScript/TypeScript library for
+   interacting with the Solana blockchain.
+2. [`@tanstack/react-query`](https://www.npmjs.com/package/@tanstack/react-query) - popular and
+   powerful asynchronous state management for React.
+
+## Installation
+
+Install `@gillsdk/react` with your package manager of choice:
+
+```package-install
+gill @gillsdk/react @tanstack/react-query
+```
+
+<Callout>
+  `gill` and `@tanstack/react-query` are peer dependencies of `@gillsdk/react` so you need to
+  explicitly install them. This allows you have more control over managing dependencies yourself.
+</Callout>
+
+## Key Features
+
+- **Type-safe hooks** - Full TypeScript support with proper typing for all Solana data
+- **Built on TanStack Query** - Leverages the power of React Query for caching, background
+  refetching, and optimistic updates
+- **Easy to use** - Simple, intuitive API that follows React best practices
+- **Server component ready** - Works with Next.js and other React Server Component frameworks
+- **Lightweight** - Minimal bundle size with tree-shaking support
+
+## Available Hooks
+
+### Client Management
+
+- [`useSolanaClient`](/docs/react/hooks/client#usesolanaclient) - Get the current Solana client
+- [`useUpdateSolanaClient`](/docs/react/hooks/client#useupdatesolanaclient) - Update the Solana
+  client
+
+### Data Fetching
+
+- [`useAccount`](/docs/react/hooks/data-fetching#useaccount) - Get account info and data
+- [`useBalance`](/docs/react/hooks/data-fetching#usebalance) - Get an account's balance in lamports
+- [`useLatestBlockhash`](/docs/react/hooks/data-fetching#uselatestblockhash) - Get the latest
+  blockhash
+- [`useSignatureStatuses`](/docs/react/hooks/data-fetching#usesignaturestatuses) - Get the statuses
+  of signatures
+- [`useSignaturesForAddress`](/docs/react/hooks/data-fetching#usesignaturesforaddress) - Get
+  signatures for an address
+- [`useProgramAccounts`](/docs/react/hooks/data-fetching#useprogramaccounts) - Get all accounts
+  owned by a program (GPA)
+- [`useTokenMint`](/docs/react/hooks/data-fetching#usetokenmint) - Get a decoded token's Mint
+  account
+- [`useTokenAccount`](/docs/react/hooks/data-fetching#usetokenaccount) - Get a token account for a
+  mint and owner
+
+## Quick Example
+
+```tsx
+import { lamportsToSol } from "gill";
+import { useBalance } from "@gillsdk/react";
+
+export function WalletBalance({ address }: { address: string }) {
+  const { balance, isLoading, isError, error } = useBalance({ address });
+
+  if (isLoading) return <div>Loading...</div>;
+  if (isError) return <div>Error: {error?.message}</div>;
+
+  return (
+    <div>
+      <p>Balance: {lamportsToSol(balance)} SOL</p>
+    </div>
+  );
+}
+```

+ 5 - 0
docs/content/docs/react/meta.json

@@ -0,0 +1,5 @@
+{
+  "title": "React",
+  "defaultOpen": false,
+  "pages": ["index", "getting-started", "hooks", "examples"]
+}

+ 4 - 1
docs/src/app/docs/[[...slug]]/page.tsx

@@ -48,6 +48,9 @@ export default async function Page(props: { params: Promise<{ slug?: string[] }>
 
   const MDX = page.data.body;
   const hasCategory = page.file.name === "index" && page.slugs.length > 0;
+  // Don't show cards for the React section index page
+  const isReactIndex = page.slugs.join("/") === "react";
+  const showCards = !isReactIndex;
 
   return (
     <DocsPage toc={page.data.toc} full={page.data.full}>
@@ -69,7 +72,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[] }>
           }}
         />
       </DocsBody>
-      {hasCategory && (
+      {hasCategory && showCards && (
         <Cards>
           {getPageTreePeers(docsSource.pageTree, page.url).map((peer) => (
             <Card key={peer.url} title={peer.name} href={peer.url}>

+ 1 - 2
docs/src/lib/source.ts

@@ -1,4 +1,4 @@
-import { api, docs,} from "@/.source";
+import { api, docs } from "@/.source";
 import { loader } from "fumadocs-core/source";
 
 export const docsSource = loader({
@@ -10,4 +10,3 @@ export const apiSource = loader({
   baseUrl: "/api",
   source: api.toFumadocsSource(),
 });
-

+ 1 - 1
packages/react/README.md

@@ -261,7 +261,7 @@ object:
 "use client";
 
 import { useAccount } from "@gillsdk/react";
-import { getMintDecoder } from "gill/programs/token";
+import { getMintDecoder } from "gill/programs";
 
 export function PageClient() {
   const { account, isLoading, isError, error } = useAccount({