@crypto-signerkit/react
v0.6.0
Published
React SDK for SignerKit — embedded wallets, email login, MFA, and reusable step-up ceremonies for sensitive actions.
Maintainers
Readme
@crypto-signerkit/react
React SDK for SignerKit — embedded custodial wallets (EVM + Solana) with email login. Privy-compatible hooks and a built-in login modal (Privy-style layout; fully brandable copy).
Current public contract: 0.6.0.
Install
npm i @crypto-signerkit/reactPeer deps: react and react-dom ≥ 18.
Quick start
import { SignerKitProvider, useSignerKit, LoginButton, UserPill } from '@crypto-signerkit/react'
<SignerKitProvider
config={{
apiUrl: 'https://api.signerkit.com',
appId: 'pk_...',
appearance: {
theme: 'dark', // default; use 'light' for white Privy card
accentColor: '#818CF8', // focus ring on the capsule input
logo: 'https://yoursite.com/logo.svg', // optional; also used in footer if set
copy: {
emailTitle: 'Log in or sign up',
emailPlaceholder: '[email protected]',
submitEmail: 'Submit',
codeTitle: 'Enter confirmation code',
codeSubtitle: 'We sent a code to {email}',
submitCode: 'Submit',
loginButton: 'Log in',
logout: 'Log out',
footerProtectedBy: 'Protected by',
footerBrand: 'YourBrand', // or '' to hide wordmark
},
},
}}
>
<App />
</SignerKitProvider>
function App() {
const { ready, authenticated, user, login, logout } = useSignerKit()
if (!ready) return null
if (!authenticated) return <button onClick={login}>Log in</button>
return <div>Hi {user?.email} <button onClick={() => logout()}>Out</button></div>
}Modal layout (Privy-like)
- Light rounded card, centered title
- Single capsule row: mail icon · email input · Submit (inline)
- Code step same pattern; 6-digit auto-submit
- Footer:
Protected by · brand - All strings overridable via
appearance.copy(importDEFAULT_COPYfor a full template)
appearance.copy keys
| Key | Default |
|-----|---------|
| emailTitle | Log in or sign up |
| emailPlaceholder | [email protected] |
| submitEmail / submitEmailLoading | Submit / … |
| codeTitle | Enter confirmation code |
| codeSubtitle | We sent a code to {email} |
| codePlaceholder | 123456 |
| submitCode / submitCodeLoading | Submit / … |
| resend / backToEmail | Resend code / Use a different email |
| errorSendCode / errorInvalidCode | … |
| loginButton / logout | Log in / Log out |
| footerProtectedBy / footerBrand | Protected by / SignerKit |
Headless hooks
const { sendCode, loginWithCode } = useLoginWithEmail()
await sendCode('[email protected]')
await loginWithCode('[email protected]', '123456')Verify a backend-created step-up intent (0.6.0)
Your backend creates the intent and sends only its opaque ID to the authenticated browser. The hook remains bound to that ID:
Step-up proves recent reauthentication, not informed consent to hidden transaction details. Before showing the code form, render the exact pending action, resource, amount, destination, network, and fees (as applicable) from your own backend in a trusted confirmation UI. Your backend must hash those same authoritative operation details into the intent binding. The public intent intentionally omits action and binding, so a generic OTP prompt is never a transaction approval screen.
import { useStepUp } from '@crypto-signerkit/react'
function SensitiveActionConfirmation({ intentId }: { intentId: string }) {
const { intent, refresh, resend, verify, loading, error, reset } = useStepUp(intentId)
async function submit(code: string) {
await verify(code)
// Tell your backend to consume the intent. Do not execute the action locally.
}
return (
<>
<TrustedPendingOperationSummary />
<StepUpForm
method={intent?.method}
maskedDestination={intent?.method === 'email_otp' ? intent.maskedDestination : undefined}
onResend={resend}
onSubmit={submit}
loading={loading}
error={error}
onDismissError={reset}
/>
</>
)
}The browser cannot create an authoritative binding, consume an intent, or
receive an authorization bearer. verify(code) returns only public ceremony
facts; the partner backend must consume online, recheck authorization, and run
its own operation idempotently.
Staged TOTP enrollment
Enrollment first proves account-email control, then stages an authenticator, then activates it with a TOTP code:
import { useMfa } from '@crypto-signerkit/react'
const { requestTotpEnrollmentCode, enrollTotp, verifyTotp } = useMfa()
await requestTotpEnrollmentCode()
const { secret, uri } = await enrollTotp(emailCode)
await verifyTotp(authenticatorCode)Get your App ID: dashboard. Docs: https://signerkit.com.
License
MIT
