A comprehensive toolkit for building Solana applications with React, featuring automatic account batching, type-safe account decoding, and seamless transaction management. Built on top of gill and @solana/kit.
React provider for Solana account management with automatic batching and caching, built on top of @gillsdk/react.
bun add @macalinao/grill @gillsdk/react gillDataLoader implementation for batching Solana account fetches.
bun add @macalinao/solana-batch-accounts-loaderCompatibility layer between @solana/wallet-adapter and @solana/kit.
bun add @macalinao/wallet-adapter-compatES module compatible DataLoader implementation.
bun add @macalinao/dataloader-esimport { GrillProvider } from "@macalinao/grill";
import { WalletAdapterCompatProvider } from "@macalinao/wallet-adapter-compat";
import { createSolanaClient } from "gill";
import { SolanaProvider } from "@gillsdk/react";
import {
  ConnectionProvider,
  WalletProvider,
} from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "sonner";
const queryClient = new QueryClient();
const solanaClient = createSolanaClient({ urlOrMoniker: "mainnet-beta" });
function App() {
  const wallets = useMemo(
    () => [new PhantomWalletAdapter(), new SolflareWalletAdapter()],
    []
  );
  return (
    <QueryClientProvider client={queryClient}>
      <SolanaProvider client={solanaClient}>
        <ConnectionProvider endpoint="https://api.mainnet-beta.solana.com">
          <WalletProvider wallets={wallets} autoConnect>
            <WalletModalProvider>
              <WalletAdapterCompatProvider>
                <GrillProvider>
                  {/* Your app components */}
                  <Toaster position="bottom-right" />
                </GrillProvider>
              </WalletAdapterCompatProvider>
            </WalletModalProvider>
          </WalletProvider>
        </ConnectionProvider>
      </SolanaProvider>
    </QueryClientProvider>
  );
}Multiple account requests are automatically batched into single RPC calls:
import { useAccount, useAssociatedTokenAccount } from "@macalinao/grill";
function Dashboard() {
  // All these requests are batched into 1 RPC call!
  const { data: userAccount } = useAccount({
    address: userAddress,
  });
  const { data: usdcAccount } = useAssociatedTokenAccount({
    mint: USDC_MINT,
    owner: userAddress,
  });
  const { data: solAccount } = useAccount({
    address: poolAddress,
  });
}Send transactions with automatic status notifications:
import { useSendTX, useKitWallet } from "@macalinao/grill";
function SwapButton() {
  const { signer } = useKitWallet();
  const sendTX = useSendTX();
  const handleSwap = async () => {
    const instructions = buildSwapInstructions();
    await sendTX("Swap USDC for SOL", instructions);
    // Automatic toast notifications for each stage!
  };
  return <button onClick={handleSwap}>Swap</button>;
}import { useAccount } from "@macalinao/grill";
import { getTokenAccountDecoder } from "@solana-program/token";
function TokenBalance({ tokenAccountAddress }) {
  const { data: account } = useAccount({
    address: tokenAccountAddress,
    decoder: getTokenAccountDecoder(),
  });
  // account.data is fully typed as TokenAccount!
  return <div>Balance: {account?.data.amount.toString()}</div>;
}Traditional Solana development suffers from the N+1 query problem. Every component that needs account data makes its own RPC request. Grill solves this with automatic batching using the DataLoader pattern from GraphQL.
Without Grill: 10 components = 10 RPC calls = Rate limiting & slow UX
With Grill: 10 components = 1 batched RPC call = Fast & efficient
- β‘ 10x fewer RPC calls through automatic batching
 - π― Type-safe everything - accounts, transactions, PDAs
 - π Automatic cache management with React Query
 - π¨ Beautiful transaction UX with toast notifications
 - ποΈ Incremental migration - works alongside existing code
 - π¦ Modern stack - Built on @solana/kit and gill
 
Grill pairs perfectly with Coda, our automated client generation tool for Solana programs. While Grill handles efficient account fetching and transaction management, Coda generates type-safe TypeScript clients from your Anchor IDLs automatically.
Together they provide:
- Zero boilerplate: Coda generates the program clients, Grill batches the account fetches
 - End-to-end type safety: From IDL to UI components
 - Automatic synchronization: Keep your clients in sync with on-chain programs
 - Production-ready code: Both tools generate code that's ready for production use
 
# Generate type-safe clients with Coda
coda generate
# Use the generated clients with Grill's batching
import { useAccount } from "@macalinao/grill";
import { getMyProgramDecoder } from "./generated/myProgram";
function MyComponent() {
  const { data } = useAccount({
    address: programAccountAddress,
    decoder: getMyProgramDecoder() // Generated by Coda!
  });
  // Fully typed account data with automatic batching
}Check out the comprehensive guides in docs/grill/:
- Introduction - Why Grill and the core concepts
 - Setup & Migration - Installation and incremental migration
 - Reading Accounts - Efficient account fetching with batching
 - Making Transactions - Clean transaction management
 - Advanced Patterns - Production patterns and best practices
 
This is a Bun monorepo using Turbo for task orchestration.
# Install dependencies
bun install
# Build all packages
bun run build
# Run tests
bun run test
# Lint code
bun run lintCopyright (c) 2025 Ian Macalinao. Licensed under the Apache-2.0 License.