@onflow/kit
@onflow/kit is a lightweight React utility library that simplifies interacting with the Flow blockchain. It provides a collection of hooks, similar to those in other popular web3 libraries, that make it easier to build frontends that understand blockchain interactions. In the future, it will also provide components designed to make authentication, script execution, transactions, event subscriptions, and network configuration seamless in React apps.
π Included React Hooksβ
useCurrentFlowUserβ Authenticate and manage the current Flow useruseFlowAccountβ Fetch Flow account details by addressuseFlowBlockβ Query latest or specific Flow blocksuseFlowChainIdβ Retrieve the current Flow chain IDuseFlowConfigβ Access the current Flow configurationuseFlowEventsβ Subscribe to Flow events in real-timeuseFlowQueryβ Execute Cadence scripts with optional argumentsuseFlowMutateβ Send transactions to the Flow blockchainuseFlowRevertibleRandomβ Generate pseudorandom values tied to block heightuseFlowTransactionStatusβ Track transaction status updates
Cross-VM (Flow EVM β Cadence) Hooksβ
useCrossVmBatchTransactionβ Execute mutliple EVM transactions in a single atomic Cadence transactionuseCrossVmTokenBalanceβ Query fungible token balances across Cadence and Flow EVMuseCrossVmSpendNftβ Bridge NFTs from Cadence to Flow EVM and execute arbitrary EVM transactions to atomically spend themuseCrossVmSpendTokenβ Bridge fungible tokens from Cadence to Flow EVM and execute arbitrary EVM transactions
Installationβ
_10npm install @onflow/kit
Usageβ
Wrapping Your App With FlowProviderβ
Begin by wrapping your application with the FlowProvider to initialize FCL configuration. This sets up FCL and maps its configuration keys to a strictly typed format for your hooks.
_24import React from "react"_24import App from "./App"_24import { FlowProvider } from "@onflow/kit"_24import flowJSON from "../flow.json"_24_24function Root() {_24 return (_24 <FlowProvider_24 config={{_24 accessNodeUrl: "https://access-mainnet.onflow.org",_24 flowNetwork: "mainnet",_24 appDetailTitle: "My On Chain App",_24 appDetailIcon: "https://example.com/icon.png",_24 appDetailDescription: "A decentralized app on Flow",_24 appDetailUrl: "https://myonchainapp.com",_24 }}_24 flowJson={flowJSON}_24 >_24 <App />_24 </FlowProvider>_24 )_24}_24_24export default Root
If you're using Next.js, place the FlowProvider inside your layout.tsx. Since React hooks must run on the client, you may need to wrap the provider in a separate file that begins with 'use client' to avoid issues with server-side rendering. Adjust this setup as needed for other frontend frameworks.
π Learn more about configuring flow.json in the Configuration Guide.
Hooksβ
Many of these hooks are built using @tanstack/react-query, which provides powerful caching, revalidation, and background refetching features. As a result, youβll see return types like UseQueryResult and UseMutationResult throughout this section. Other typesβsuch as Account, Block, and CurrentUserβare from the Flow Client Library (FCL) TypeDefs. Refer to their respective documentation for full type definitions and usage patterns.
useCurrentFlowUserβ
_10import { useCurrentFlowUser } from "@onflow/kit"
Returns:β
user: CurrentUserβ The current user object from FCLauthenticate: () => Promise<CurrentUser>β Triggers wallet authenticationunauthenticate: () => voidβ Logs the user out
_16function AuthComponent() {_16 const { user, authenticate, unauthenticate } = useCurrentFlowUser()_16_16 return (_16 <div>_16 {user.loggedIn ? (_16 <>_16 <p>Logged in as {user.addr}</p>_16 <button onClick={unauthenticate}>Logout</button>_16 </>_16 ) : (_16 <button onClick={authenticate}>Login</button>_16 )}_16 </div>_16 )_16}
useFlowAccountβ
_10import { useFlowAccount } from "@onflow/kit"
Parameters:β
address?: stringβ Flow address (with or without0xprefix)query?: UseQueryOptions<Account | null, Error>β Optional TanStackQuery options
Returns: UseQueryResult<Account | null, Error>β
_19function AccountDetails() {_19 const { data: account, isLoading, error, refetch } = useFlowAccount({_19 address: "0x1cf0e2f2f715450",_19 query: { staleTime: 5000 },_19 })_19_19 if (isLoading) return <p>Loading account...</p>_19 if (error) return <p>Error fetching account: {error.message}</p>_19 if (!account) return <p>No account data</p>_19_19 return (_19 <div>_19 <h2>Account: {account.address}</h2>_19 <p>Balance: {account.balance}</p>_19 <pre>{account.code}</pre>_19 <button onClick={refetch}>Refetch</button>_19 </div>_19 )_19}
useFlowBlockβ
_10import { useFlowBlock } from "@onflow/kit"
Parameters:β
sealed?: booleanβ Iftrue, fetch latest sealed blockid?: stringβ Block by IDheight?: numberβ Block by heightquery?: UseQueryOptions<Block | null, Error>β Optional TanStackQuery options
Only one of sealed, id, or height should be provided.
Returns: UseQueryResult<Block | null, Error>β
_14function LatestBlock() {_14 const { data: block, isLoading, error } = useFlowBlock({ query: { staleTime: 10000 } })_14_14 if (isLoading) return <p>Loading...</p>_14 if (error) return <p>Error: {error.message}</p>_14 if (!block) return <p>No block data.</p>_14_14 return (_14 <div>_14 <h2>Block {block.height}</h2>_14 <p>ID: {block.id}</p>_14 </div>_14 )_14}
useFlowChainIdβ
_10import { useFlowChainId } from "@onflow/kit"
This hook retrieves the Flow chain ID, which is useful for identifying the current network.
Parameters:β
queryOptions?: Omit<UseQueryOptions<string | null>, "queryKey" | "queryFn">β Optional TanStack Query options likestaleTime,enabled, etc.
Returns: UseQueryResult<string | null, Error>β
Valid chain IDs include: testnet (Flow Testnet), mainnet (Flow Mainnet), and emulator (Flow Emulator). The flow- prefix will be stripped from the chain ID returned by the access node (e.g. flow-testnet will return testnet).
_10function ChainIdExample() {_10 const { data: chainId, isLoading, error } = useFlowChainId({_10 query: { staleTime: 10000 },_10 })_10_10 if (isLoading) return <p>Loading chain ID...</p>_10 if (error) return <p>Error fetching chain ID: {error.message}</p>_10_10 return <div>Current Flow Chain ID: {chainId}</div>_10}
useFlowConfigβ
_10import { useFlowConfig } from "@onflow/kit"
Returns: FlowConfigβ
_10function MyComponent() {_10 const config = useFlowConfig()_10_10 return (_10 <div>_10 <p>Current network: {config.flowNetwork}</p>_10 <p>Current access node: {config.accessNodeUrl}</p>_10 </div>_10 )_10}
useFlowEventsβ
_10import { useFlowEvents } from "@onflow/kit"
Parameters:β
startBlockId?: stringβ Optional ID of the block to start listening fromstartHeight?: numberβ Optional block height to start listening fromeventTypes?: string[]β Array of event type strings (e.g.,A.0xDeaDBeef.Contract.EventName)addresses?: string[]β Filter by Flow addressescontracts?: string[]β Filter by contract identifiersopts?: { heartbeatInterval?: number }β Options for subscription heartbeatonEvent: (event: Event) => voidβ Callback for each event receivedonError?: (error: Error) => voidβ Optional error handler
Example:β
_10function EventListener() {_10 useFlowEvents({_10 eventTypes: ["A.0xDeaDBeef.SomeContract.SomeEvent"],_10 onEvent: (event) => console.log("New event:", event),_10 onError: (error) => console.error("Error:", error),_10 })_10_10 return <div>Listening for events...</div>_10}
useFlowQueryβ
_10import { useFlowQuery } from "@onflow/kit"
Parameters:β
cadence: stringβ Cadence script to runargs?: (arg, t) => unknown[]β Function returning FCL argumentsquery?: UseQueryOptions<unknown, Error>β Optional TanStackQuery options
Returns: UseQueryResult<unknown, Error>β
_22function QueryExample() {_22 const { data, isLoading, error, refetch } = useFlowQuery({_22 cadence: `_22 access(all)_22 fun main(a: Int, b: Int): Int {_22 return a + b_22 }_22 `,_22 args: (arg, t) => [arg(1, t.Int), arg(2, t.Int)],_22 query: { staleTime: 10000 },_22 })_22_22 if (isLoading) return <p>Loading query...</p>_22 if (error) return <p>Error: {error.message}</p>_22_22 return (_22 <div>_22 <p>Result: {data}</p>_22 <button onClick={refetch}>Refetch</button>_22 </div>_22 )_22}
useFlowMutateβ
_10import { useFlowMutate } from "@onflow/kit"
Parameters:β
mutation?: UseMutationOptions<string, Error, FCLMutateParams>β Optional TanStackQuery mutation options
Returns: UseMutationResult<string, Error, FCLMutateParams>β
_33function CreatePage() {_33 const { mutate, isPending, error, data: txId } = useFlowMutate({_33 mutation: {_33 onSuccess: (txId) => console.log("TX ID:", txId),_33 },_33 })_33_33 const sendTransaction = () => {_33 mutate({_33 cadence: `transaction() {_33 prepare(acct: &Account) {_33 log(acct.address)_33 }_33 }`,_33 args: (arg, t) => [],_33 proposer: fcl.currentUser,_33 payer: fcl.currentUser,_33 authorizations: [],_33 limit: 100,_33 })_33 }_33_33 return (_33 <div>_33 <button onClick={sendTransaction} disabled={isPending}>_33 Send Transaction_33 </button>_33 {isPending && <p>Sending transaction...</p>}_33 {error && <p>Error: {error.message}</p>}_33 {txId && <p>Transaction ID: {txId}</p>}_33 </div>_33 )_33}
useFlowRevertibleRandomβ
_10import { useFlowRevertibleRandom } from "@onflow/kit"
Parameters:β
min?: stringβ Minimum random value (inclusive), as a UInt256 decimal string. Defaults to"0".max: stringβ Maximum random value (inclusive), as a UInt256 decimal string. Required.count?: numberβ Number of random values to fetch (must be at least 1). Defaults to1.query?: Omit<UseQueryOptions<any, Error>, "queryKey" | "queryFn">β Optional TanStack Query settings likestaleTime,enabled,retry, etc.
Returns: UseQueryResult<RevertibleRandomResult[], Error>β
Each RevertibleRandomResult includes:
blockHeight: stringβ The block height from which the random value was generated.value: stringβ The random UInt256 value, returned as a decimal string.
_26function RandomValues() {_26 const { data: randoms, isLoading, error, refetch } = useFlowRevertibleRandom({_26 min: "0",_26 max: "1000000000000000000000000", // Example large max_26 count: 3,_26 query: { staleTime: 10000 },_26 })_26_26 if (isLoading) return <p>Loading random numbers...</p>_26 if (error) return <p>Error fetching random numbers: {error.message}</p>_26 if (!randoms) return <p>No random values generated.</p>_26_26 return (_26 <div>_26 <h2>Generated Random Numbers</h2>_26 <ul>_26 {randoms.map((rand, idx) => (_26 <li key={idx}>_26 Block {rand.blockHeight}: {rand.value}_26 </li>_26 ))}_26 </ul>_26 <button onClick={refetch}>Regenerate</button>_26 </div>_26 )_26}
Notes:β
- Randomness is generated using the on-chain
revertibleRandomfunction on Flow, producing pseudorandom values tied to block and script execution. - Values are deterministic: The values returned for identical calls within the same block will be identical.
- If
countis larger than one, the returned values are distinct. - This hook is designed for simple use cases that don't require unpredictability, such as randomized UIs. Since the hook uses script executions on existing blocks, the random source is already public and the randoms are predictable.
- For more advanced use cases that do require on-chain randomness logic via transactions, Flow provides built-in support using Cadence's
revertibleRandomand commit-reveal scheme.
useFlowTransactionStatusβ
_10import { useFlowTransactionStatus } from "@onflow/kit"
Parameters:β
id: stringβ Transaction ID to subscribe to
Returns:β
transactionStatus: TransactionStatus | nullerror: Error | null
_10function TransactionStatusComponent() {_10 const txId = "your-transaction-id-here"_10 const { transactionStatus, error } = useFlowTransactionStatus({ id: txId })_10_10 if (error) return <div>Error: {error.message}</div>;_10_10 return <div>Status: {transactionStatus?.statusString}</div>;_10}
Cross-VM Hooksβ
useCrossVmBatchTransactionβ
This feature is currently only supported on Testnet & Mainnet networks. Emulator support will be added in a future release.
_10import { useCrossVmBatchTransaction } from "@onflow/kit"
This hook allows you to execute multiple EVM transactions in a single atomic Cadence transaction. It is useful for batch processing EVM calls while ensuring they are executed together, either all succeeding or allowing for some to fail without affecting the others.
Parameters:β
mutation?: UseMutationOptions<string, Error, UseCrossVmBatchTransactionMutateArgs>β Optional TanStackQuery mutation options
Returns: UseCrossVmBatchTransactionResultβ
Where UseCrossVmBatchTransactionResult is defined as:
_10interface UseCrossVmBatchTransactionResult extends Omit<_10 UseMutationResult<string, Error, UseCrossVmBatchTransactionMutateArgs>,_10 "mutate" | "mutateAsync"_10> {_10 mutate: (calls: UseCrossVmBatchTransactionMutateArgs) => void_10 mutateAsync: (calls: UseCrossVmBatchTransactionMutateArgs) => Promise<string>_10}
Where UseCrossVmBatchTransactionMutateArgs is defined as:
_10interface UseCrossVmBatchTransactionMutateArgs {_10 calls: EvmBatchCall[]_10 mustPass?: boolean_10}
Where EvmBatchCall is defined as:
_14interface EvmBatchCall {_14 // The target EVM contract address (as a string)_14 address: string_14 // The contract ABI fragment_14 abi: Abi_14 // The name of the function to call_14 functionName: string_14 // The function arguments_14 args?: readonly unknown[]_14 // The gas limit for the call_14 gasLimit?: bigint_14 // The value to send with the call_14 value?: bigint_14}
_35function CrossVmBatchTransactionExample() {_35 const { sendBatchTransaction, isPending, error, data: txId } = useCrossVmBatchTransaction({_35 mutation: {_35 onSuccess: (txId) => console.log("TX ID:", txId),_35 },_35 })_35_35 const sendTransaction = () => {_35 const calls = [_35 {_35 address: "0x1234567890abcdef",_35 abi: {_35 // ABI definition for the contract_35 },_35 functionName: "transfer",_35 args: ["0xabcdef1234567890", 100n], // Example arguments_35 gasLimit: 21000n, // Example gas limit_35 },_35 // Add more calls as needed_35 ]_35_35 sendBatchTransaction({calls})_35 }_35_35 return (_35 <div>_35 <button onClick={sendTransaction} disabled={isPending}>_35 Send Cross-VM Transaction_35 </button>_35 {isPending && <p>Sending transaction...</p>}_35 {error && <p>Error: {error.message}</p>}_35 {txId && <p>Transaction ID: {txId}</p>}_35 </div>_35 )_35}
useCrossVmTokenBalanceβ
This feature is currently only supported on Testnet & Mainnet networks. Emulator support will be added in a future release.
_10import { useFlowQuery } from '@onflow/kit';
Fetch the balance of a token balance for a given user across both Cadence and EVM environments.
Parameters:β
owner: stringβ Cadence address of the account whose token balances you want.vaultIdentifier?: stringβ Optional Cadence resource identifier (e.g. "0x1cf0e2f2f715450.FlowToken.Vault") for on-chain balanceerc20AddressHexArg?: stringβ Optional bridged ERC-20 contract address (hex) for EVM/COA balancequery?: Omit<UseQueryOptions<unknown, Error>, "queryKey" | "queryFn">β Optional TanStack Query config (e.g. staleTime, enabled)
Note: You must pass
owner, and one ofvaultIdentifierorerc20AddressHexArg.
Returns: UseQueryResult<UseCrossVmTokenBalanceDataβ|βnull, Error>β
Where UseCrossVmTokenBalanceData is defined as:
_10interface UseCrossVmTokenBalanceData {_10 cadence: TokenBalance // Token balance of Cadence vault_10 evm: TokenBalance // Token balance of EVM (COA stored in /storage/coa)_10 combined: TokenBalance // Combined balance of both Cadence and EVM_10}
Where TokenBalance is defined as:
_10interface TokenBalance {_10 value: bigint // Balance value in smallest unit_10 formatted: string // Formatted balance string (e.g. "123.45")_10 precision: number // Number of decimal places for the token_10}
_20function QueryExample() {_20 const { data, isLoading, error, refetch } = useCrossVmTokenBalance({_20 owner: '0x1cf0e2f2f715450',_20 vaultIdentifier: '0x1cf0e2f2f715450.FlowToken.Vault',_20 query: { staleTime: 10000 },_20 });_20_20 if (isLoading) return <p>Loading token balance...</p>;_20 if (error) return <p>Error fetching token balance: {error.message}</p>;_20_20 return (_20 <div>_20 <h2>Token Balances</h2>_20 <p>Cadence Balance: {data.cadence.formatted} (Value: {data.cadence.value})</p>_20 <p>EVM Balance: {data.evm.formatted} (Value: {data.evm.value})</p>_20 <p>Combined Balance: {data.combined.formatted} (Value: {data.combined.value})</p>_20 <button onClick={refetch}>Refetch</button>_20 </div>_20 )_20}
useCrossVmSpendNftβ
This feature is currently only supported on Testnet & Mainnet networks. Emulator support will be added in a future release.
_10import { useCrossVmSpendNft } from "@onflow/kit"
Bridge NFTs from Cadence to Flow EVM and execute arbitrary EVM transactions to atomically spend them.
Parameters:β
mutation?: UseMutationOptions<string, Error, UseCrossVmSpendFtMutateArgs>β Optional TanStackQuery mutation options
Where UseCrossVmSpendFtMutateArgs is defined as:
_10interface UseCrossVmSpendFtMutateArgs {_10 nftIdentifier: string // Cadence NFT identifier (e.g. "0x1cf0e2f2f715450.FlowNFT")_10 nftIds: string[] // Array of NFT IDs to bridge_10 calls: EVMBatchCall[] // Array of EVM calls to execute atomically_10}
Returns: UseCrossVmSpendNftResultβ
Where UseCrossVmSpendNftResult is defined as:
_10interface UseCrossVmSpendNftResult extends Omit<_10 UseMutationResult<string, Error, CrossVmSpendNftParams>,_10 "mutate" | "mutateAsync"_10> {_10 spendNft: (params: CrossVmSpendNftParams) => Promise<string>_10 spendNftAsync: (params: CrossVmSpendNftParams) => Promise<string>_10}
_31function CrossVmSpendNftExample() {_31 const { spendNft, isPending, error, data: txId } = useCrossVmSpendNft()_31_31 const handleSpendNft = () => {_31 spendNft({_31 nftIdentifier: "0x1cf0e2f2f715450.FlowNFT", // Cadence NFT identifier_31 nftIds: ["1"], // Array of NFT IDs to bridge_31 calls: [_31 {_31 abi: contractAbi, // ABI of the EVM contract_31 contractAddress: "0x1234567890abcdef1234567890abcdef12345678", // EVM contract address_31 functionName: "transferNFT",_31 args: ["123"], // Example args_31 value: "1000000000000000000", // Amount in wei (if applicable)_31 gasLimit: "21000", // Gas limit for the EVM call_31 },_31 ],_31 })_31 }_31_31 return (_31 <div>_31 <button onClick={handleSpendNft} disabled={isPending}>_31 Bridge and Spend NFT_31 </button>_31 {isPending && <p>Sending transaction...</p>}_31 {error && <p>Error: {error.message}</p>}_31 {txId && <p>Transaction ID: {txId}</p>}_31 </div>_31 )_31}
useCrossVmSpendTokenβ
This feature is currently only supported on Testnet & Mainnet networks. Emulator support will be added in a future release.
_10import { useCrossVmSpendToken } from "@onflow/kit"
Bridge FTs from Cadence to Flow EVM and execute arbitrary EVM transactions to atomically spend them.
Parameters:β
mutation?: UseMutationOptions<string, Error, UseCrossVmSpendTokenMutateArgs>β Optional TanStackQuery mutation options
Where UseCrossVmSpendTokenMutateArgs is defined as:
_10interface UseCrossVmSpendTokenMutateArgs {_10 vaultIdentifier: string; // Cadence vault identifier (e.g. "0x1cf0e2f2f715450.ExampleToken.Vault")_10 amount: string; // Amount of tokens to bridge, as a decimal string (e.g. "1.23")_10 calls: EVMBatchCall[]; // Array of EVM calls to execute after bridging_10}
Returns: UseCrossVmSpendTokenResultβ
Where UseCrossVmSpendTokenResult is defined as:
_10interface UseCrossVmSpendTokenResult extends Omit<_10 UseMutationResult<string, Error, UseCrossVmSpendTokenMutateArgs>,_10 "mutate" | "mutateAsync"_10> {_10 spendToken: (args: UseCrossVmSpendTokenMutateArgs) => void; // Function to trigger the FT bridging and EVM calls_10 spendTokenAsync: (args: UseCrossVmSpendTokenMutateArgs) => Promise<string>; // Async version of spendToken_10}
_31function CrossVmSpendTokenExample() {_31 const { spendToken, isPending, error, data: txId } = useCrossVmSpendToken()_31_31 const handleSpendToken = () => {_31 spendToken({_31 vaultIdentifier: "0x1cf0e2f2f715450.ExampleToken.Vault", // Cadence vault identifier_31 amount: "1.23", // Amount of tokens to bridge to EVM_31 calls: [_31 {_31 abi: myEvmContractAbi, // EVM contract ABI_31 address: "0x01234567890abcdef01234567890abcdef", // EVM contract address_31 function: "transfer", // EVM function to call_31 args: [_31 "0xabcdef01234567890abcdef01234567890abcdef", // Recipient address_31 ],_31 },_31 ],_31 })_31 }_31_31 return (_31 <div>_31 <button onClick={handleSpendToken} disabled={isPending}>_31 Bridge and Spend FTs_31 </button>_31 {isPending && <p>Sending transaction...</p>}_31 {error && <p>Error: {error.message}</p>}_31 {txId && <p>Cadence Transaction ID: {txId}</p>}_31 </div>_31 )_31}