Displaying On-Chain Data
Render balances, tokens, NFTs, and program state in React.
Recipe
import { createSolanaRpc, address, lamports } from "@solana/kit";
import { fetchMaybeTokenAccount } from "@solana-program/token";When to reach for this:
- Portfolio pages and NFT galleries.
- Game inventory and DeFi positions.
- Admin dashboards.
Working Example
"use client";
import { useEffect, useState } from "react";
import { createSolanaRpc, address } from "@solana/kit";
export function SolBalance({ owner }: { owner: string }) {
const [sol, setSol] = useState<number | null>(null);
useEffect(() => {
const rpc = createSolanaRpc(process.env.NEXT_PUBLIC_RPC_URL!);
rpc.getBalance(address(owner)).send().then(({ value }) => setSol(Number(value) / 1e9));
}, [owner]);
if (sol === null) return <span>…</span>;
return <span>{sol.toFixed(4)} SOL</span>;
}What this demonstrates:
- Loading skeleton while RPC in flight.
- Format lamports for display only at UI boundary.
- Refetch when owner changes.
Deep Dive
How It Works
- SOL:
getBalance. - SPL tokens: ATAs + token program decoders.
- NFTs: DAS
getAsseton supporting RPC providers. - Program state: Codama fetch helpers.
Gotchas
- Float math on token amounts - precision loss. Fix: bigint until final formatting.
- Wrong token decimals - 1000x display bugs. Fix: read mint decimals.
- DAS on non-DAS RPC - method missing. Fix: provider capability check.
- No empty state - confusing zero vs loading. Fix: explicit states.
- Mainnet images from untrusted URIs - XSS via metadata. Fix: sanitize URLs.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Server render initial | SEO | Wallet-only private data |
| Indexer GraphQL | Rich history | Simple balance chip |
FAQs
App Router?
Providers in client layout.tsx; pages mix server + client components.
Secrets?
Server env only for keypairs; NEXT_PUBLIC_ for RPC URLs.
kit 7.0.0?
All examples use @solana/kit, not web3.js v1.
Wallet?
Signing only in client components.
Caching?
Revalidate on-chain reads carefully - chain is source of truth.
Actions?
HTTPS endpoints returning Solana Actions JSON.
Mobile?
Separate React Native app with MWA.
Testing?
Surfpool for integration; mock RPC in unit tests.
Mainnet?
Separate env files and program IDs.
Errors?
Decode simulation logs for user-facing messages.
Related
- Server-Side Reads - RSC fetch
- The DAS API - NFT metadata
- Caching & Realtime - fresh data
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.