@hawcx/react
v2.4.1
Published
React bindings for the Hawcx headless SDK.
Keywords
Readme
@hawcx/react
React bindings for Hawcx Auth. Provides hooks and a drop-in component for authentication flows.
Installation
npm install @hawcx/reactQuick Start
Option 1: Drop-in Component
Add complete authentication UI with minimal code:
import { HawcxProvider, HawcxSignUpSignIn } from '@hawcx/react';
import '@hawcx/react/dist/styles.css';
function App() {
return (
<HawcxProvider config={{ configId: 'your-config-id' }}>
<HawcxSignUpSignIn
onSuccess={(authCode) => {
// Exchange authCode for tokens on your backend
fetch('/api/auth/token', {
method: 'POST',
body: JSON.stringify({ code: authCode }),
});
}}
/>
</HawcxProvider>
);
}Option 2: Custom UI with Hooks
Build your own UI using the provided hooks:
import { HawcxProvider, useAuthState, useAuthActions } from '@hawcx/react';
function App() {
return (
<HawcxProvider config={{ configId: 'your-config-id' }}>
<LoginForm />
</HawcxProvider>
);
}
function LoginForm() {
const state = useAuthState();
const { start, selectMethod, submitCode } = useAuthActions();
if (state.status === 'completed') {
return <div>Authenticated!</div>;
}
// Render UI based on state.status and state.step.type
// ...
}Provider
Wrap your app with HawcxProvider:
import { HawcxProvider } from '@hawcx/react';
<HawcxProvider config={{
configId: 'your-config-id',
apiBase: 'http://localhost:7998/v1', // Optional
logger: console, // Optional, for debugging
}}>
{children}
</HawcxProvider>Or provide a pre-created client:
import { createHawcxClient } from '@hawcx/core';
const client = createHawcxClient({ configId: 'your-config-id' });
<HawcxProvider client={client}>
{children}
</HawcxProvider>Hooks
useAuthState()
Get the current auth state and subscribe to changes:
const state = useAuthState();
switch (state.status) {
case 'idle':
return <StartForm />;
case 'loading':
return <Spinner />;
case 'step':
return <RenderStep step={state.step} />;
case 'completed':
return <Success authCode={state.authCode} />;
case 'error':
return <Error message={state.error.message} />;
}useAuthActions()
Access all client action methods:
const actions = useAuthActions();
// Start a flow
await actions.start('signin', '[email protected]');
// Select an auth method
await actions.selectMethod('email_otp');
// Submit OTP code
await actions.submitCode('123456');
// Submit TOTP code
await actions.submitTotp('123456');
// Submit phone for SMS enrollment
await actions.submitPhone('+14155551234');
// Request new code
await actions.resend();
// Cancel flow
await actions.cancel();
// Reset to idle
actions.reset();
// Sign out
actions.signOut();useAuthFlags()
Boolean flags for state checking:
const { isIdle, isLoading, isStep, isCompleted, isError } = useAuthFlags();
if (isLoading) {
return <Spinner />;
}useSession()
Access and manage the authentication session:
const { session, isLoading, error, actions } = useSession();
if (session) {
// User is authenticated
// session.authCode - exchange for tokens
// session.codeVerifier - for PKCE
}
// Sign out
actions.signOut();useAuthClient()
Access the underlying HawcxClient instance:
const client = useAuthClient();
// Direct client access for advanced use cases
const hasCreds = await client.hasDeviceCredentials('[email protected]');Form Hooks
Pre-built form state management for common UI patterns.
useIdentifierForm(options)
Manage the email/phone input form:
function LoginStart() {
const { identifier, setIdentifier, isSubmitting, submit } = useIdentifierForm({
flowType: 'signin', // or 'signup'
});
return (
<form onSubmit={(e) => { e.preventDefault(); submit(); }}>
<input
type="email"
value={identifier}
onChange={(e) => setIdentifier(e.target.value)}
placeholder="Email"
/>
<button type="submit" disabled={isSubmitting}>
Continue
</button>
</form>
);
}useOtpForm()
Manage OTP/TOTP input. Auto-detects regular OTP vs TOTP:
function OtpInput() {
const {
code,
setCode,
submit,
resend,
destination, // Masked email/phone for OTP
isTotp, // True if TOTP (not regular OTP)
isSubmitting,
} = useOtpForm();
return (
<form onSubmit={(e) => { e.preventDefault(); submit(); }}>
{destination && <p>Code sent to {destination}</p>}
<input
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder={isTotp ? 'Authenticator code' : 'Enter code'}
maxLength={6}
/>
<button type="submit" disabled={isSubmitting}>Verify</button>
{!isTotp && <button type="button" onClick={resend}>Resend</button>}
</form>
);
}usePhoneForm()
Manage phone number input for SMS enrollment:
function PhoneInput() {
const { phone, setPhone, submit, isSubmitting } = usePhoneForm();
return (
<form onSubmit={(e) => { e.preventDefault(); submit(); }}>
<input
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="+1 555 123 4567"
/>
<button type="submit" disabled={isSubmitting}>Continue</button>
</form>
);
}useMethodSelection(state)
Extract methods from a select_method step:
function MethodSelector() {
const state = useAuthState();
const { methods } = useMethodSelection(state);
const { selectMethod } = useAuthActions();
return (
<div>
{methods.map((method) => (
<button key={method.name} onClick={() => selectMethod(method.name)}>
{method.label}
</button>
))}
</div>
);
}useMethodDisplayName(method)
Get a display name for a method:
const label = useMethodDisplayName(method); // method.label or method.nameDrop-in Component
HawcxSignUpSignIn
Complete authentication UI with unified sign-in flow:
import { HawcxSignUpSignIn } from '@hawcx/react';
import '@hawcx/react/dist/styles.css';
<HawcxSignUpSignIn
onSuccess={(authCode) => {
// Exchange code for tokens
}}
className="my-auth-form" // Optional custom class
/>The component handles:
- Unified sign-in flow (server determines new vs existing user)
- Email/phone identifier input
- Method selection
- OTP input with resend
- TOTP setup with QR code
- SMS phone enrollment
- QR code display for push/QR auth
- Loading states
- Error display
Building Custom UI
Example of a complete custom auth form:
import {
HawcxProvider,
useAuthState,
useAuthActions,
useAuthFlags,
getStepType,
getMethods,
isEnterCodeStep,
isSetupTotpStep,
} from '@hawcx/react';
function AuthFlow() {
const state = useAuthState();
const actions = useAuthActions();
const { isLoading, isCompleted, isError } = useAuthFlags();
if (isCompleted) {
return <Success authCode={state.authCode} />;
}
if (isError) {
return <Error error={state.error} onRetry={actions.reset} />;
}
if (isLoading) {
return <Spinner />;
}
const stepType = getStepType(state);
switch (stepType) {
case null:
return <IdentifierInput onSubmit={(email) => actions.start('signin', email)} />;
case 'select_method':
return (
<MethodList
methods={getMethods(state)}
onSelect={actions.selectMethod}
/>
);
case 'enter_code':
return (
<OtpInput
destination={state.step.destination}
onSubmit={actions.submitCode}
onResend={actions.resend}
/>
);
case 'enter_totp':
return <TotpInput onSubmit={actions.submitTotp} />;
case 'setup_totp':
return (
<TotpSetup
secret={state.step.secret}
otpauthUrl={state.step.otpauthUrl}
onSubmit={actions.submitTotp}
/>
);
case 'setup_sms':
return <PhoneInput onSubmit={actions.submitPhone} />;
case 'await_approval':
return <WaitingForApproval qrData={state.step.qrData} />;
default:
return <div>Unknown step: {stepType}</div>;
}
}Styling
Import the default styles:
import '@hawcx/react/dist/styles.css';CSS classes for customization:
| Class | Element |
|-------|---------|
| .hawcx-auth-container | Main container |
| .hawcx-form | Form wrapper |
| .hawcx-input | Input fields |
| .hawcx-button | Buttons |
| .hawcx-button-primary | Primary action button |
| .hawcx-title | Section headings |
| .hawcx-error | Error messages |
| .hawcx-method-list | Method selection list |
| .hawcx-method-btn | Method button |
| .hawcx-loading | Loading indicator |
| .hawcx-success | Success message |
| .hawcx-qr-container | QR code container |
| .hawcx-help-text | Help text |
Type Guards & Helpers
Re-exported from @hawcx/core for convenience:
import {
// AuthState type guards
isIdle,
isLoading,
isStep,
isCompleted,
isError,
// AuthState getters
getStepType,
getMethods,
getError,
// Step type guards
isSelectMethodStep,
isEnterCodeStep,
isEnterTotpStep,
isSetupTotpStep,
isSetupSmsStep,
isAwaitApprovalStep,
isCompletedStep,
isErrorStep,
} from '@hawcx/react';TypeScript
All types are exported:
import type {
AuthState,
AuthStateStep,
AuthStateCompleted,
SessionArtifact,
HawcxClientConfig,
HawcxClient,
AuthStep,
Method,
AuthFlowType,
AuthFlags,
HawcxSignUpSignInProps,
} from '@hawcx/react';Requirements
- React 18+
- Modern browser with IndexedDB support (for device trust)
