Skip to main content

@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​

Cross-VM (Flow EVM ↔ Cadence) Hooks​

  • useCrossVmBatchTransaction – Execute mutliple EVM transactions in a single atomic Cadence transaction
  • useCrossVmTokenBalance – Query fungible token balances across Cadence and Flow EVM
  • useCrossVmSpendNft – Bridge NFTs from Cadence to Flow EVM and execute arbitrary EVM transactions to atomically spend them
  • useCrossVmSpendToken – Bridge fungible tokens from Cadence to Flow EVM and execute arbitrary EVM transactions

Installation​


_10
npm 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.


_24
import React from "react"
_24
import App from "./App"
_24
import { FlowProvider } from "@onflow/kit"
_24
import flowJSON from "../flow.json"
_24
_24
function 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
_24
export 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​

info

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​


_10
import { useCurrentFlowUser } from "@onflow/kit"

Returns:​

  • user: CurrentUser – The current user object from FCL
  • authenticate: () => Promise<CurrentUser> – Triggers wallet authentication
  • unauthenticate: () => void – Logs the user out

_16
function 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​


_10
import { useFlowAccount } from "@onflow/kit"

Parameters:​

  • address?: string – Flow address (with or without 0x prefix)
  • query?: UseQueryOptions<Account | null, Error> – Optional TanStackQuery options

Returns: UseQueryResult<Account | null, Error>​


_19
function 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​


_10
import { useFlowBlock } from "@onflow/kit"

Parameters:​

  • sealed?: boolean – If true, fetch latest sealed block
  • id?: string – Block by ID
  • height?: number – Block by height
  • query?: UseQueryOptions<Block | null, Error> – Optional TanStackQuery options

Only one of sealed, id, or height should be provided.

Returns: UseQueryResult<Block | null, Error>​


_14
function 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​


_10
import { 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 like staleTime, 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).


_10
function 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​


_10
import { useFlowConfig } from "@onflow/kit"

Returns: FlowConfig​


_10
function 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​


_10
import { useFlowEvents } from "@onflow/kit"

Parameters:​

  • startBlockId?: string – Optional ID of the block to start listening from
  • startHeight?: number – Optional block height to start listening from
  • eventTypes?: string[] – Array of event type strings (e.g., A.0xDeaDBeef.Contract.EventName)
  • addresses?: string[] – Filter by Flow addresses
  • contracts?: string[] – Filter by contract identifiers
  • opts?: { heartbeatInterval?: number } – Options for subscription heartbeat
  • onEvent: (event: Event) => void – Callback for each event received
  • onError?: (error: Error) => void – Optional error handler

Example:​


_10
function 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​


_10
import { useFlowQuery } from "@onflow/kit"

Parameters:​

  • cadence: string – Cadence script to run
  • args?: (arg, t) => unknown[] – Function returning FCL arguments
  • query?: UseQueryOptions<unknown, Error> – Optional TanStackQuery options

Returns: UseQueryResult<unknown, Error>​


_22
function 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​


_10
import { useFlowMutate } from "@onflow/kit"

Parameters:​

  • mutation?: UseMutationOptions<string, Error, FCLMutateParams> – Optional TanStackQuery mutation options

Returns: UseMutationResult<string, Error, FCLMutateParams>​


_33
function 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​


_10
import { 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 to 1.
  • query?: Omit<UseQueryOptions<any, Error>, "queryKey" | "queryFn"> – Optional TanStack Query settings like staleTime, 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.

_26
function 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​


_10
import { useFlowTransactionStatus } from "@onflow/kit"

Parameters:​

  • id: string – Transaction ID to subscribe to

Returns:​

  • transactionStatus: TransactionStatus | null
  • error: Error | null

_10
function 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​

info

This feature is currently only supported on Testnet & Mainnet networks. Emulator support will be added in a future release.


_10
import { 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:


_10
interface 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:


_10
interface UseCrossVmBatchTransactionMutateArgs {
_10
calls: EvmBatchCall[]
_10
mustPass?: boolean
_10
}

Where EvmBatchCall is defined as:


_14
interface 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
}


_35
function 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​

info

This feature is currently only supported on Testnet & Mainnet networks. Emulator support will be added in a future release.


_10
import { 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 balance
  • erc20AddressHexArg?: string – Optional bridged ERC-20 contract address (hex) for EVM/COA balance
  • query?: Omit<UseQueryOptions<unknown, Error>, "queryKey" | "queryFn"> – Optional TanStack Query config (e.g. staleTime, enabled)

Note: You must pass owner, and one of vaultIdentifier or erc20AddressHexArg.

Returns: UseQueryResult<UseCrossVmTokenBalanceData | null, Error>​

Where UseCrossVmTokenBalanceData is defined as:


_10
interface 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:


_10
interface 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
}


_20
function 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​

info

This feature is currently only supported on Testnet & Mainnet networks. Emulator support will be added in a future release.


_10
import { 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:


_10
interface 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:


_10
interface 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
}


_31
function 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​

info

This feature is currently only supported on Testnet & Mainnet networks. Emulator support will be added in a future release.


_10
import { 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:


_10
interface 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:


_10
interface 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
}


_31
function 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
}