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
apiKeystring—Urblock API key. When set, SDK runs in SaaS mode
apiUrlstring"https://api.urblock.com"API base URL
chainIdnumber11155111Chain ID
rpcUrlstring—JSON-RPC URL. Required in standalone mode
factoryAddress0x${string}—Factory contract. Required in standalone mode
bundlerUrlstring—ERC-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
walletConnectProjectIdstring—WalletConnect Cloud project ID
onConnect(wallet) => void—Callback on connect
onDisconnect() => void—Callback on disconnect
onError(error) => void—Callback on error
brandConfigBrandConfig—Visual 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)
classNamestring—Additional 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 / reset—Standard 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({ ... });