Skip to main content

@urblock/connect-react

Pre-built React components and hooks for integrating self-custody smart accounts (ERC-4337) into your frontend. Supports passkey, EOA (MetaMask), WalletConnect, and Google/Apple OAuth login.

npm install @urblock/connect-react
# peer deps
npm install react react-dom
info

This package depends on @urblock/connect-core (installed automatically). For WalletConnect support, also install @walletconnect/ethereum-provider.


Quick Start

import { UrblockProvider, ConnectButton, ConnectModal } from "@urblock/connect-react";
import "@urblock/connect-react/styles.css";

function App() {
return (
<UrblockProvider
apiKey="pk_test_abc123"
chainId={11155111}
rpcUrl="https://rpc.sepolia.org"
>
<ConnectButton />
<ConnectModal />
</UrblockProvider>
);
}

Components

<UrblockProvider>

Root provider — wraps your app and manages connection state, wallet persistence, and modal.

<UrblockProvider
apiKey="pk_test_abc123" // SaaS mode (routes through urblock API)
chainId={11155111} // default: Sepolia
rpcUrl="https://rpc.sepolia.org"
theme="dark" // "light" | "dark" | "auto" (default)
walletTypes={["passkey", "eoa", "walletconnect"]}
onConnect={(wallet) => console.log("Connected:", wallet.address)}
onDisconnect={() => console.log("Disconnected")}
onError={(err) => console.error(err)}
brandConfig={{
logo: "/logo.svg",
modalTitle: "Sign In",
primaryColor: "#6366f1",
borderRadius: "16px",
fontFamily: "Inter, sans-serif",
footerText: "Powered by MyApp",
}}
>
{children}
</UrblockProvider>

Props — UrblockProviderProps

PropTypeDefaultDescription
apiKeystringUrblock API key. When set, SDK runs in SaaS mode
apiUrlstring"https://api.urblock.com"API base URL
chainIdnumber11155111Chain ID
rpcUrlstringJSON-RPC URL. Required in standalone mode
factoryAddress0x${string}Factory contract. Required in standalone mode
bundlerUrlstringERC-4337 bundler. Required in standalone mode
rpIdstringwindow.location.hostnameWebAuthn Relying Party ID
rpNamestring"urblock"WebAuthn Relying Party name
theme"light" | "dark" | "auto""auto"Modal theme
walletTypesWalletType[]["passkey","eoa","walletconnect"]Enabled login methods
walletConnectProjectIdstringWalletConnect Cloud project ID
onConnect(wallet) => voidCallback on connect
onDisconnect() => voidCallback on disconnect
onError(error) => voidCallback on error
brandConfigBrandConfigVisual customization
sessionKeyValidatorAddress0x${string}SessionKeyValidator contract (standalone)

BrandConfig

FieldTypeDescription
logostringLogo URL for modal header
modalTitlestringModal title (default: "Connect Wallet")
primaryColorstringCSS accent color
primaryColorHoverstringAccent hover (auto-derived if omitted)
borderRadiusstringCSS border radius
fontFamilystringCSS font family
footerTextstring | nullFooter text. Set to null to hide

<ConnectButton>

Pre-styled connect/disconnect button. Shows address + wallet type when connected.

<ConnectButton />
<ConnectButton label="Sign In" />
<ConnectButton showType={false} className="my-btn" />
PropTypeDefaultDescription
labelstring"Connect Wallet"Button text when disconnected
showTypebooleantrueShow wallet type badge (Passkey, EOA, WC)
classNamestringAdditional CSS classes

<ConnectModal>

Modal overlay for choosing a connection method. Renders passkey, EOA, WalletConnect, and OAuth options based on walletTypes config.

<ConnectModal />
<ConnectModal className="custom-modal" />
PropTypeDescription
classNamestringAdditional CSS classes

The modal is controlled by openModal() / closeModal() from the provider context. ConnectButton opens it automatically.


<ConnectAuthCard> / <ConnectMethodButton>

Building blocks for fully custom login UIs. Exported from @urblock/connect-react/auth.

import { ConnectAuthCard, ConnectMethodButton } from "@urblock/connect-react/auth";

<ConnectAuthCard
title="Welcome"
subtitle="Choose a sign-in method"
methods={
<>
<ConnectMethodButton
icon="🔑"
label="Passkey"
description="Biometric login"
onClick={() => connectWithPasskey()}
/>
<ConnectMethodButton
icon="🦊"
label="MetaMask"
description="Browser wallet"
onClick={() => connectWithEOA()}
/>
</>
}
footer="Powered by MyApp"
/>

ConnectMethodButton Props

PropTypeDescription
iconReactNodeButton icon
labelReactNodePrimary label
descriptionReactNodeSecondary text
loadingbooleanShow spinner

ConnectAuthCard Props

PropTypeDescription
titleReactNodeCard title
subtitleReactNodeOptional subtitle
errorReactNodeError message
methodsReactNodeLogin method buttons
fallbackReactNodeFallback section (e.g., email login)
fallbackLabelReactNodeDivider label (default: "Legacy email")
footerReactNodeFooter text

Hooks

useUrblockWallet()

Primary hook for wallet state and connection actions.

const {
status, // "disconnected" | "connecting" | "connected" | "error"
wallet, // ConnectedWallet | null
address, // `0x${string}` | null (shortcut)
isConnected, // boolean
isConnecting, // boolean
isSaasMode, // boolean
chainId, // number
activeChainId, // number (may differ after switchChain)
error, // Error | null
openModal, // () => void
closeModal, // () => void
connectWithPasskey, // () => Promise<void>
connectWithEOA, // () => Promise<void>
switchChain, // (chainId: number) => Promise<void>
disconnect, // () => Promise<void>
} = useUrblockWallet();

ConnectedWallet

FieldTypeDescription
address`0x${string}`Smart account address
walletTypeWalletType"passkey" | "eoa" | "walletconnect" | "google" | "apple"
chainIdnumberChain ID
signersSignerInfo[]Active signers
passkeyWebAuthnCredentialPresent if passkey auth
eoaAddress`0x${string}`Present if EOA/WC auth

useSignMessage()

Sign an arbitrary message with the connected account.

const { signMessage, isLoading, signature, error, reset } = useSignMessage();

const sig = await signMessage("Hello, world!");
// sig: `0x${string}` | null
ReturnTypeDescription
signMessage(message: string) => Promise<0x… | null>Sign a message
isLoadingbooleanLoading state
signature`0x${string}` | nullLast signature
errorError | nullLast error
reset() => voidReset state

useSignTypedData()

Sign EIP-712 typed data.

const { signTypedData, isLoading, signature, error, reset } = useSignTypedData();

const sig = await signTypedData({
domain: { name: "MyApp", version: "1", chainId: 11155111 },
types: { Greeting: [{ name: "text", type: "string" }] },
primaryType: "Greeting",
message: { text: "Hello" },
});

Same return shape as useSignMessage.


useSendUserOp()

Build, sign, and submit ERC-4337 UserOperations.

const { sendTransaction, sendUserOp, isLoading, userOpHash, error, reset } = useSendUserOp();

// High-level: pass an array of calls, hook builds + signs + submits
const hash = await sendTransaction([
{ target: "0xRecipient...", value: 1000000000000000n }, // send ETH
{ target: "0xToken...", data: "0xa9059cbb000000000000000000000000..." }, // ERC-20 transfer
]);

// Low-level: pass a pre-built PackedUserOperation
const hash2 = await sendUserOp(preBuiltUserOp);
ReturnTypeDescription
sendTransaction(calls: Call[]) => Promise<0x… | null>Build + sign + submit
sendUserOp(userOp: PackedUserOperation) => Promise<0x… | null>Submit pre-built op
isLoadingbooleanLoading state
userOpHash`0x${string}` | nullLast UserOp hash
errorError | nullLast error
reset() => voidReset state

Call

interface Call {
target: `0x${string}`; // contract address
value?: bigint; // wei
data?: `0x${string}`; // calldata
}

useBalance()

Read native + ERC-20 balances for the connected account. Supports auto-polling.

const { balance, isLoading, error, refetch } = useBalance({
tokenAddresses: ["0xUSDC...", "0xDAI..."],
pollInterval: 10_000, // refresh every 10s
});

if (balance) {
console.log(`${balance.native.formatted} ${balance.native.symbol}`);
balance.tokens.forEach((t) => console.log(`${t.formatted} ${t.symbol}`));
}
OptionTypeDefaultDescription
tokenAddresses`0x${string}`[][]ERC-20 contracts to fetch
nativeSymbolstring"ETH"Override native symbol
pollIntervalnumber0Auto-refresh ms (0 = disabled)
enabledbooleantrueEnable fetch on mount
ReturnTypeDescription
balanceBalanceResult | null{ native, tokens[] }
isLoadingbooleanLoading state
errorError | nullLast error
refetch() => Promise<void>Manual refetch

useSessionKey()

Create scoped session keys for delegated access.

const { createSessionKey, isLoading, sessionKey, error, reset } = useSessionKey();

const key = await createSessionKey({
validDuration: 3600, // 1 hour
allowedTargets: ["0xTokenContract..."], // restrict to this contract
spendLimit: 1000000000000000000n, // 1 ETH max
label: "game-session", // optional label
});

if (key) {
console.log(key.sessionKeyId, key.key, key.validUntil);
}

CreateSessionKeyParams

FieldTypeDescription
validDurationnumberDuration in seconds
allowedTargets`0x${string}`[]Contracts the key can call
spendLimitbigintMax ETH spend in wei
labelstringHuman label

useSiwe()

Sign In With Ethereum (EIP-4361) authentication.

const { signIn, isLoading, isAuthenticated, token, error, reset } = useSiwe();

const success = await signIn({
statement: "Sign in to MyApp",
resources: ["https://myapp.com/tos"],
});

if (success) {
// `token` contains session token (SaaS mode) or "standalone-verified"
console.log("Authenticated!", token);
}
ReturnTypeDescription
signIn(options?) => Promise<boolean>Perform SIWE flow
isAuthenticatedbooleanAuth state
tokenstring | nullSession token
isLoading / error / resetStandard hook state

useRecovery()

On-chain social recovery for the connected smart account.

const { installRecoveryAction, executeRecovery, isLoading, error, reset } = useRecovery();

// Step 1: Install the RecoveryAction module
const installAction = await installRecoveryAction("0xRecoveryActionContract...");
// installAction.callData → submit via useSendUserOp

// Step 2: Execute recovery (rotate the validator key)
const recoveryAction = await executeRecovery({
validatorAddress: "0xWebAuthnValidator...",
newOwnerKeyData: "0x...", // ABI-encoded new P256 or ECDSA key
});
// recoveryAction.callData → submit via guardian
ReturnTypeDescription
installRecoveryAction(address) => Promise<RecoveryAction | null>Encode install callData
executeRecovery(params) => Promise<RecoveryAction | null>Encode recovery callData

useOAuthLogin()

Google/Apple OAuth login with automatic passkey creation for new users.

info

Requires SaaS mode (apiKey must be set in UrblockProvider).

const { loginWithOAuth, checkSafetyNet, loading, error, oauthResult } = useOAuthLogin();

// After obtaining an id_token from Google Identity Services:
await loginWithOAuth("google", googleIdToken);

// Check account safety
const safety = await checkSafetyNet();
if (safety && !safety.has_recovery) {
console.warn("Set up recovery for your account");
}

Flow for new users:

  1. DApp gets id_token from Google/Apple
  2. Hook verifies token with urblock API
  3. Automatically creates a passkey + smart account
  4. Links OAuth identity to the account
  5. User is connected — zero friction
ReturnTypeDescription
loginWithOAuth(provider, idToken) => Promise<void>Verify + connect
checkSafetyNet() => Promise<SafetyNetResponse | null>Check account security
loadingbooleanLoading state
errorError | nullLast error
oauthResultOAuthVerifyResponse | nullVerification result

Modes

SaaS Mode

Pass apiKey to UrblockProvider. The SDK routes all operations (account creation, relay, session keys, passkeys, OAuth) through the urblock API. No factory/bundler/validator addresses needed.

<UrblockProvider apiKey="pk_test_abc123" chainId={11155111} rpcUrl="...">

Standalone Mode

Omit apiKey. You must provide factoryAddress, bundlerUrl, and rpcUrl. Account creation and UserOp submission happen directly on-chain.

<UrblockProvider
chainId={11155111}
rpcUrl="https://rpc.sepolia.org"
factoryAddress="0xYourFactory..."
bundlerUrl="https://your-bundler.example.com"
>

CSS

Import the built-in stylesheet for pre-styled components:

import "@urblock/connect-react/styles.css";

All CSS classes are prefixed with ub-. Override CSS variables for theming:

:root {
--ub-accent: #6366f1;
--ub-accent-hover: #4f46e5;
--ub-radius: 12px;
--ub-font: "Inter", sans-serif;
}

Or use the brandConfig prop on UrblockProvider for JS-based theming.


Advanced

UrblockContext

For advanced use cases, access the raw context value:

import { UrblockContext } from "@urblock/connect-react";
import { useContext } from "react";

const ctx = useContext(UrblockContext);

UrblockApiClient

Low-level API client used internally. Available for custom integrations:

import { UrblockApiClient } from "@urblock/connect-react";

const client = new UrblockApiClient({
apiKey: "pk_test_abc123",
apiUrl: "https://api.urblock.com",
});

const account = await client.createAccount({ ... });