Quick Start
Get users signing in with Kevo in under 5 minutes.
1. Install the SDK
npm install @kevo-ws/sdk
2. Get your API key
Sign in to the Kevo Portal. Create a project, configure your auth methods and supported chains, then copy the Publishable Key from the project settings.
3. Wrap your app with KevoProvider
Add KevoProvider at the root of your React tree. Pass a config object with your publishable key and optional RPC URLs. This initializes the client, loads your project configuration, and makes all hooks available.
import { KevoProvider } from '@kevo-ws/sdk/react'
export default function App() {
return (
<KevoProvider
config={{
publishableKey: 'pk_live_xxxxxxxxxxxx',
// Multi-chain EVM — map chainId → RPC URL
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 (use instead of chains for simple projects)
// evmRpcUrl: 'https://eth.llamarpc.com',
solanaRpcUrl: 'https://api.mainnet-beta.solana.com',
}}
>
<YourApp />
</KevoProvider>
)
}chains configured, sendTransaction, useBalance, and estimateGas automatically pick the right RPC from tx.chainId or the active chain — no per-call config needed. Use useChain() to switch chains at runtime.4. Add the sign-in modal
Place KevoModal anywhere inside the provider tree. It renders nothing until opened. Use useKevoModal() to control it and useKevo() for session state.
import { KevoModal, useKevoModal, useKevo, useUserProfile } from '@kevo-ws/sdk/react'
function Header() {
const { open } = useKevoModal()
const { isAuthenticated } = useKevo()
const { profile } = useUserProfile()
return (
<header>
{isAuthenticated ? (
<span>
<img src={profile.avatarUrl} alt="" />
{profile.name}
</span>
) : (
<button onClick={open}>Sign In</button>
)}
</header>
)
}
function RootLayout() {
return (
<>
<KevoModal />
<Header />
{/* rest of your app */}
</>
)
}4.1 Render user profile
Kevo exposes a normalized profile for display. The primary name is always the shortened wallet address. If the login provider returns a username/name or avatar, Kevo exposes it too; otherwise the SDK generates a deterministic avatar from the wallet address.
import { useUserProfile } from '@kevo-ws/sdk/react'
function UserChip() {
const { profile, rawProfile } = useUserProfile()
return (
<div>
<img src={profile.avatarUrl} alt="" />
<strong>{profile.name}</strong>
{profile.username && <span>{profile.username}</span>}
{rawProfile?.email && <small>{rawProfile.email}</small>}
</div>
)
}useUserProfile() to link an email first.5. Access the wallet and send transactions
After sign-in, use useWallets() to get wallet addresses, useSignTransaction() to sign & broadcast, and useBalance() for live balance display. The SDK auto-fills nonce, gas limit, and fee data — just specify to, value, and chainId.
import { useWallets, useSignTransaction, useBalance } from '@kevo-ws/sdk/react'
import { nativeTransfer } from '@kevo-ws/sdk/helpers'
function WalletView() {
const { evmWallet } = useWallets()
const { sendTransaction, isLoading } = useSignTransaction()
const { balance } = useBalance() // auto-refreshes every 15s
if (!evmWallet) return <p>No wallet yet</p>
const handleSend = async () => {
const tx = nativeTransfer({
to: '0xRecipientAddress',
value: 1_000_000_000_000_000n, // 0.001 ETH
chainId: 1,
})
const txHash = await sendTransaction(tx)
console.log('TX hash:', txHash)
}
return (
<div>
<p>Address: {evmWallet.address}</p>
<p>Balance: {balance !== null
? (Number(balance) / 1e18).toFixed(6) + ' ETH'
: 'Loading…'}</p>
<button onClick={handleSend} disabled={isLoading}>
{isLoading ? 'Sending…' : 'Send 0.001 ETH'}
</button>
</div>
)
}That's it
Your users can now sign in without passwords or seed phrases, and you can send transactions on their behalf with their approval — nonce, gas, and fees are handled automatically by the SDK.