@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 blocksuseFlowConfig
β 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 heightuseFlowTransaction
β Track transaction status updates
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>
β
_24function AccountDetails() {_24 const {_24 data: account,_24 isLoading,_24 error,_24 refetch,_24 } = useFlowAccount({_24 address: '0x1cf0e2f2f715450',_24 query: { staleTime: 5000 },_24 });_24_24 if (isLoading) return <p>Loading account...</p>;_24 if (error) return <p>Error fetching account: {error.message}</p>;_24 if (!account) return <p>No account data</p>;_24_24 return (_24 <div>_24 <h2>Account: {account.address}</h2>_24 <p>Balance: {account.balance}</p>_24 <pre>{account.code}</pre>_24 <button onClick={refetch}>Refetch</button>_24 </div>_24 );_24}
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>
β
_18function LatestBlock() {_18 const {_18 data: block,_18 isLoading,_18 error,_18 } = useFlowBlock({ query: { staleTime: 10000 } });_18_18 if (isLoading) return <p>Loading...</p>;_18 if (error) return <p>Error: {error.message}</p>;_18 if (!block) return <p>No block data.</p>;_18_18 return (_18 <div>_18 <h2>Block {block.height}</h2>_18 <p>ID: {block.id}</p>_18 </div>_18 );_18}
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>
β
_38function CreatePage() {_38 const {_38 mutate,_38 isPending,_38 error,_38 data: txId,_38 } = useFlowMutate({_38 mutation: {_38 onSuccess: (txId) => console.log('TX ID:', txId),_38 },_38 });_38_38 const sendTransaction = () => {_38 mutate({_38 cadence: `transaction() {_38 prepare(acct: &Account) {_38 log(acct.address)_38 }_38 }`,_38 args: (arg, t) => [],_38 proposer: fcl.currentUser,_38 payer: fcl.currentUser,_38 authorizations: [],_38 limit: 100,_38 });_38 };_38_38 return (_38 <div>_38 <button onClick={sendTransaction} disabled={isPending}>_38 Send Transaction_38 </button>_38 {isPending && <p>Sending transaction...</p>}_38 {error && <p>Error: {error.message}</p>}_38 {txId && <p>Transaction ID: {txId}</p>}_38 </div>_38 );_38}
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.
_31function RandomValues() {_31 const {_31 data: randoms,_31 isLoading,_31 error,_31 refetch,_31 } = useFlowRevertibleRandom({_31 min: '0',_31 max: '1000000000000000000000000', // Example large max_31 count: 3,_31 query: { staleTime: 10000 },_31 });_31_31 if (isLoading) return <p>Loading random numbers...</p>;_31 if (error) return <p>Error fetching random numbers: {error.message}</p>;_31 if (!randoms) return <p>No random values generated.</p>;_31_31 return (_31 <div>_31 <h2>Generated Random Numbers</h2>_31 <ul>_31 {randoms.map((rand, idx) => (_31 <li key={idx}>_31 Block {rand.blockHeight}: {rand.value}_31 </li>_31 ))}_31 </ul>_31 <button onClick={refetch}>Regenerate</button>_31 </div>_31 );_31}
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 TransactionComponent() {_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}
useCrossVmTokenBalance
β
_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}
_21function QueryExample() {_21 const { data, isLoading, error, refetch } = useCrossVmTokenBalance({_21 owner: '0x1cf0e2f2f715450',_21 vaultIdentifier: '0x1cf0e2f2f715450.FlowToken.Vault',_21 erc20AddressHexArg: '0x1234567890abcdef1234567890abcdef12345678', // Optional_21 query: { staleTime: 10000 },_21 });_21_21 if (isLoading) return <p>Loading token balance...</p>;_21 if (error) return <p>Error fetching token balance: {error.message}</p>;_21_21 return (_21 <div>_21 <h2>Token Balances</h2>_21 <p>Cadence Balance: {data.cadence.formatted} (Value: {data.cadence.value})</p>_21 <p>EVM Balance: {data.evm.formatted} (Value: {data.evm.value})</p>_21 <p>Combined Balance: {data.combined.formatted} (Value: {data.combined.value})</p>_21 <button onClick={refetch}>Refetch</button>_21 </div>_21 );_21}