@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 without0x
prefix)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
revertibleRandom
function 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
count
is 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
revertibleRandom
and commit-reveal scheme.
useFlowTransactionStatus
β
_10import { useFlowTransactionStatus } from "@onflow/kit"
Parameters:β
id: string
β Transaction ID to subscribe to
Returns:β
transactionStatus: TransactionStatus | null
error: 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 ofvaultIdentifier
orerc20AddressHexArg
.
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}