React Integration
The Arkiv SDK works in the browser out of the box. This page covers the two most common React patterns:
- Reading data — query entities with a public client, cached via TanStack Query
- Writing data — sign transactions with the user’s wallet via wagmi
Reading Data with TanStack Query
Section titled “Reading Data with TanStack Query”Use a public client for queries — no private key required, safe to run in the browser.
Setup the Query Layer
Section titled “Setup the Query Layer”First, define fetcher functions separate from hooks so they’re reusable and testable:
import { createPublicClient, http } from "@arkiv-network/sdk"import { kaolin } from "@arkiv-network/sdk/chains"import { eq } from "@arkiv-network/sdk/query"
const PROJECT_ATTRIBUTE = { key: "project", value: "my-app" }
export const publicClient = createPublicClient({ chain: kaolin, transport: http(),})
export async function fetchEntitiesByType<T>( entityType: string): Promise<(T & { arkivEntityKey: string })[]> { const query = publicClient.buildQuery() const result = await query .where([ eq(PROJECT_ATTRIBUTE.key, PROJECT_ATTRIBUTE.value), eq("entityType", entityType), ]) .withPayload(true) .withMetadata(true) .limit(50) .fetch()
return result.entities .map((entity: any) => { try { return { arkivEntityKey: entity.key, ...entity.toJson() } } catch { return null } }) .filter((item): item is T & { arkivEntityKey: string } => item !== null)}
export async function fetchEntityByKey<T>(entityKey: string): Promise<T> { const entity = await publicClient.getEntity(entityKey) return entity.toJson()}Create Hooks
Section titled “Create Hooks”Wrap the fetchers with TanStack Query for caching, deduplication, and background refetching:
npm install @tanstack/react-queryimport { useQuery } from "@tanstack/react-query"import { fetchEntitiesByType, fetchEntityByKey } from "@/lib/arkiv-queries"
export function useArkivQuery<T>(entityType: string) { return useQuery<(T & { arkivEntityKey: string })[]>({ queryKey: ["arkiv", "entities", entityType], queryFn: () => fetchEntitiesByType<T>(entityType), })}
export function useArkivEntity<T>(entityKey: string | null) { return useQuery<T>({ queryKey: ["arkiv", "entity", entityKey], queryFn: () => fetchEntityByKey<T>(entityKey!), enabled: !!entityKey, })}Use in Components
Section titled “Use in Components”import { useArkivQuery } from "@/hooks/useArkivQuery"
interface Post { title: string content: string}
function PostList() { const { data: posts, isLoading, error } = useArkivQuery<Post>("post")
if (isLoading) return <p>Loading...</p> if (error) return <p>Error: {error.message}</p>
return ( <ul> {posts?.map((post) => ( <li key={post.arkivEntityKey}>{post.title}</li> ))} </ul> )}Writing Data with wagmi
Section titled “Writing Data with wagmi”If your project uses wagmi (or a wagmi-powered kit like RainbowKit or ConnectKit), you can derive an Arkiv wallet client directly from the wagmi wallet client. This avoids duplicate wallet connection logic.
Derive an Arkiv Wallet Client
Section titled “Derive an Arkiv Wallet Client”import { useAccount, useWalletClient } from "wagmi"import { createWalletClient as createArkivWalletClient, custom,} from "@arkiv-network/sdk"import { kaolin } from "@arkiv-network/sdk/chains"
function useArkivWalletClient() { const { address } = useAccount() const { data: wagmiWalletClient } = useWalletClient()
if (!wagmiWalletClient || !address) return null
return createArkivWalletClient({ chain: kaolin, transport: custom(wagmiWalletClient.transport), account: address, })}Then use the returned client like any other Arkiv wallet client:
import { jsonToPayload, ExpirationTime } from "@arkiv-network/sdk/utils"
function CreatePostButton() { const arkivClient = useArkivWalletClient()
async function handleCreate() { if (!arkivClient) return
const { entityKey, txHash } = await arkivClient.createEntity({ payload: jsonToPayload({ title: "My Post", content: "Hello!" }), contentType: "application/json", attributes: [ { key: "project", value: "my-app" }, { key: "entityType", value: "post" }, ], expiresIn: ExpirationTime.fromDays(30), })
console.log("Created:", entityKey, txHash) }
return ( <button onClick={handleCreate} disabled={!arkivClient}> Create Post </button> )}Add the Kaolin Network to wagmi Config
Section titled “Add the Kaolin Network to wagmi Config”Register the Arkiv Kaolin testnet in your wagmi config so wallet switching works:
import { http, createConfig } from "wagmi"import { kaolin } from "@arkiv-network/sdk/chains"
export const config = createConfig({ chains: [kaolin], transports: { [kaolin.id]: http(), },})If your app supports multiple chains, add kaolin alongside them:
import { mainnet } from "wagmi/chains"import { kaolin } from "@arkiv-network/sdk/chains"
export const config = createConfig({ chains: [mainnet, kaolin], transports: { [mainnet.id]: http(), [kaolin.id]: http(), },})Writing Data without wagmi
Section titled “Writing Data without wagmi”If you’re not using wagmi, you can connect MetaMask directly:
import { createWalletClient, custom } from "@arkiv-network/sdk"import { kaolin } from "@arkiv-network/sdk/chains"
// Prompt MetaMask to add the networkawait window.ethereum.request({ method: "wallet_addEthereumChain", params: [{ chainId: "0xe0087f821", chainName: "Arkiv Kaolin Testnet", nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 }, rpcUrls: ["https://kaolin.hoodi.arkiv.network/rpc"], blockExplorerUrls: ["https://explorer.kaolin.hoodi.arkiv.network"], }],})
// Connectconst [address] = await window.ethereum.request({ method: "eth_requestAccounts",})
const walletClient = createWalletClient({ chain: kaolin, transport: custom(window.ethereum),})