Kevo Docs

React Integration

Kevo's React bindings provide a pre-built modal, hooks for every operation, and a context provider that manages session and project configuration automatically.

KevoProvider

KevoProvider is the root context for all Kevo hooks and components. It must wrap any component that uses Kevo's hooks.

typescript
import { KevoProvider } from '@kevo-ws/sdk/react'

function App() {
  return (
    <KevoProvider
      config={{
        publishableKey: 'pk_live_xxxxxxxxxxxx',
        // Multi-chain EVM (recommended):
        defaultChainId: 1,
        chains: {
          1:    'https://eth-mainnet.infura.io/v3/YOUR_KEY',
          8453: 'https://base-mainnet.infura.io/v3/YOUR_KEY',
          137:  'https://polygon-mainnet.infura.io/v3/YOUR_KEY',
        },
        // Single-chain shorthand (simple projects):
        // evmRpcUrl: 'https://eth.llamarpc.com',
        solanaRpcUrl: 'https://api.mainnet-beta.solana.com',
      }}
    >
      <YourApp />
    </KevoProvider>
  )
}
PropTypeDescription
config*KevoConfigConfiguration object (see below).

KevoConfig fields:

PropTypeDescription
publishableKey*stringYour project's publishable key from the Kevo Portal.
apiUrlstringBase URL of the Kevo API.
iframeUrlstringURL of the signing iframe.
iframeTimeoutMsnumberTimeout in ms for iframe signing operations.
chainsRecord<number, string>Multi-chain EVM RPC map — chainId → JSON-RPC URL. Enables useChain() and auto-RPC resolution.
defaultChainIdnumberActive chain on startup. Defaults to the first key in chains.
evmRpcUrlstringSingle-chain fallback RPC. Ignored when chains is set. Kept for backwards compatibility.
solanaRpcUrlstringDefault Solana JSON-RPC URL. Used by sendSolanaTransaction, getSolanaBalance, etc.
uiConfigPartial<UIConfig>Override UI configuration. Merged with project-level config from the portal.

KevoModal

KevoModal is the pre-built sign-in modal. Place it once anywhere inside the provider. It renders as nothing until useKevoModal().open() is called.

typescript
import { KevoModal } from '@kevo-ws/sdk/react'

// Place once inside KevoProvider
function RootLayout({ children }) {
  return (
    <>
      <KevoModal />
      {children}
    </>
  )
}

The modal automatically reads your project's enabledAuthMethods and enabledChains from the Kevo Portal and shows only the relevant options.

Controlling the modal

typescript
import { useKevoModal } from '@kevo-ws/sdk/react'

function SignInButton() {
  const { open, close, isOpen } = useKevoModal()
  return <button onClick={open}>Sign In</button>
}

Session Management

useKevo

The main hook for session state and auth methods. isLoading is true during the initial session restoration from storage.

typescript
import { useKevo } from '@kevo-ws/sdk/react'

function Profile() {
  const { isAuthenticated, isLoading, session, logout } = useKevo()

  if (isLoading) return <Spinner />
  if (!isAuthenticated) return <p>Not signed in</p>

  return (
    <div>
      <p>User ID: {session?.userId}</p>
      <button onClick={logout}>Sign Out</button>
    </div>
  )
}
PropTypeDescription
isAuthenticatedbooleanTrue when the user has a valid session.
isLoadingbooleanTrue during initial session restoration.
sessionKevoSession | nullActive session object, or null.
enabledChainsSupportedChain[]The project's enabled chains (e.g. ['evm', 'solana']).
logout() => Promise<void>Sign out and clear session.
sendEmailOtp(email: string) => Promise<void>Send OTP to email (programmatic auth).
verifyEmailOtp(email: string, code: string) => Promise<void>Verify OTP code.
loginWithGoogle() => voidOpen Google OAuth popup.
getDelegation() => Promise<KevoDelegation | null>Check delegation status.
grantDelegation(opts?) => Promise<KevoDelegation>Grant backend signing delegation.
revokeDelegation() => Promise<void>Revoke active delegation.

Sign out

typescript
import { useKevo } from '@kevo-ws/sdk/react'

function SignOutButton() {
  const { logout } = useKevo()
  return <button onClick={logout}>Sign Out</button>
}

Complete Example

A minimal but complete authenticated app with wallet display and transaction sending:

typescript
import {
  KevoProvider, KevoModal,
  useKevoModal, useKevo, useWallets, useSignTransaction, useBalance,
} from '@kevo-ws/sdk/react'
import { nativeTransfer } from '@kevo-ws/sdk/helpers'

function App() {
  return (
    <KevoProvider config={{
      publishableKey: 'pk_live_xxxxxxxxxxxx',
      evmRpcUrl: 'https://eth.llamarpc.com',
    }}>
      <KevoModal />
      <MainPage />
    </KevoProvider>
  )
}

function MainPage() {
  const { isAuthenticated, isLoading } = useKevo()
  const { open } = useKevoModal()

  if (isLoading) return <div>Loading…</div>
  if (!isAuthenticated) return <button onClick={open}>Sign In</button>
  return <Dashboard />
}

function Dashboard() {
  const { evmWallet } = useWallets()
  const { sendTransaction, isLoading, error } = useSignTransaction()
  const { balance } = useBalance()

  const handleSend = async () => {
    if (!evmWallet) return
    const txHash = await sendTransaction(
      nativeTransfer({
        to: '0xRecipientAddress',
        value: 1_000_000_000_000_000n, // 0.001 ETH
        chainId: 1,
      })
    )
    console.log('TX hash:', txHash)
  }

  return (
    <div>
      <p>EVM address: {evmWallet?.address}</p>
      <p>Balance: {balance !== null
        ? (Number(balance) / 1e18).toFixed(6) + ' ETH'
        : 'Loading…'}</p>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
      <button onClick={handleSend} disabled={isLoading || !evmWallet}>
        {isLoading ? 'Sending…' : 'Send 0.001 ETH'}
      </button>
    </div>
  )
}
All hooks must be used inside a KevoProvider. If you call a hook outside the provider, it throws an error with a descriptive message.