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 gill-react.
bun add @macalinao/grill gill-react gill
DataLoader implementation for batching Solana account fetches.
bun add @macalinao/solana-batch-accounts-loader
Compatibility layer between @solana/wallet-adapter and @solana/kit.
bun add @macalinao/wallet-adapter-compat
ES module compatible DataLoader implementation.
bun add @macalinao/dataloader-es
import { GrillProvider } from "@macalinao/grill";
import { WalletAdapterCompatProvider } from "@macalinao/wallet-adapter-compat";
import { createSolanaClient } from "gill";
import { SolanaProvider } from "gill-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 lint
Copyright (c) 2025 Ian Macalinao. Licensed under the Apache-2.0 License.