Tutorials
React Query Integration
Wrap Honeycluster proxy calls in TanStack React Query hooks with caching, background refetch, and typed errors.

If you're using TanStack Start or any React app with @tanstack/react-query, you probably want Honeycluster responses to flow through the Query cache alongside the rest of your data. This tutorial shows the shape of a reusable useLedger hook — and how to keep the API key safely on your server side.

1. Backend assumption
##

This tutorial shows both patterns:

  • Public cluster: call https://honeycluster.io directly from the browser. No proxy, no key, no server code.
  • Private / enterprise cluster: call your own proxy's /api/ledger/:index route instead, and let the proxy forward to the private endpoint with the X-API-Key header attached. See the Node.js proxy tutorial if you need to build that layer.

The hook structure is the same either way — only the URL differs.

2. Typed fetch helper
##
TypeScript
// lib/honeycluster.ts
export interface LedgerSummary {
  ledger_index: number
  ledger_hash: string
  close_time_human: string
  transactions?: readonly unknown[]
}

// Public cluster — call directly from the browser.
// For a private endpoint, swap this for `/api/ledger/${index}` and let
// your proxy forward to the private hostname with the key attached.
export async function fetchLedger(index: number): Promise<LedgerSummary> {
  const res = await fetch('https://honeycluster.io', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      method: 'ledger',
      params: [{ ledger_index: index, transactions: true, expand: true }],
    }),
  })
  if (!res.ok) {
    const body = await res.json().catch(() => ({}))
    throw new Error(body?.message ?? `Ledger request failed: ${res.status}`)
  }
  const payload = await res.json()
  return payload.result.ledger as LedgerSummary
}

Keep this in a plain module so it's testable without React.

3. The hook
##
TypeScript
// hooks/useLedger.ts
import { useQuery } from '@tanstack/react-query'
import { fetchLedger, type LedgerSummary } from '~/lib/honeycluster'

export function useLedger(index: number | null) {
  return useQuery<LedgerSummary, Error>({
    queryKey: ['honeycluster', 'ledger', index],
    queryFn: () => fetchLedger(index!),
    enabled: index !== null,
    // Historical ledgers are immutable — cache effectively forever.
    staleTime: Infinity,
    gcTime: 1000 * 60 * 60, // 1 hour in memory
  })
}

Two important defaults:

  • staleTime: Infinity — historical ledger data never changes, so there's no point refetching. New React Query consumers hit the cache instantly.
  • enabled: index !== null — lets components call the hook unconditionally and still defer the fetch until they have a valid index.
4. Using it
##
Tsx
import { useLedger } from '~/hooks/useLedger'

export function LedgerCard({ index }: { index: number }) {
  const { data, isPending, error } = useLedger(index)

  if (isPending) return <p>Loading ledger {index}…</p>
  if (error) return <p className="text-danger9">{error.message}</p>
  if (!data) return null

  return (
    <div>
      <h3>Ledger {data.ledger_index}</h3>
      <p>Closed at {data.close_time_human}</p>
      <p>{data.transactions?.length ?? 0} transactions</p>
    </div>
  )
}
5. Streaming data
##

For live streams — ledgerClosed, account subscriptions, etc. — React Query isn't the right primitive. Those are push-based. Use a long-lived xrpl.js client on your server and relay events to the browser via Server-Sent Events or WebSockets. React Query can still hold the seeded "last known" value, and your stream handler can call queryClient.setQueryData(['ledger', 'latest'], event) to keep it fresh.

See Subscribe to Account Events for the server-side subscription pattern.