React Integration
This guide walks you through integrating Circadify into a React application using the official React bindings.
Installation
Section titled “Installation”The @circadify/react package provides React components and hooks. It requires @circadify/web-sdk as a peer dependency, so install both.
1. Get access from Circadify
Section titled “1. Get access from Circadify”@circadify/react and @circadify/web-sdk are distributed as private packages on GitHub Packages under the @circadify scope. Once your enterprise agreement is signed, send Circadify the GitHub username of the engineer (or service account) who will install the SDK. We grant that user read access to both packages and confirm by email.
2. Mint a GitHub Personal Access Token
Section titled “2. Mint a GitHub Personal Access Token”The granted GitHub user authenticates to GitHub Packages with a Personal Access Token (PAT) they create themselves — Circadify never sees this token.
-
Sign in to GitHub as the granted user.
-
Go to github.com/settings/tokens/new (or Settings → Developer settings → Personal access tokens → Tokens (classic) → Generate new token (classic)).
-
Name the token (e.g.
circadify-web-sdk-install), set an expiration (90 days recommended), and check theread:packagesscope. No other scopes are required. -
Click Generate token and copy the value — it starts with
ghp_…and is shown only once.
3. Configure npm to use the token
Section titled “3. Configure npm to use the token”Add a project-level .npmrc file (next to your package.json):
@circadify:registry=https://npm.pkg.github.com//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}Then export the token as an environment variable so npm picks it up at install time:
export GITHUB_TOKEN=ghp_your_read_packages_tokenThis tells npm that anything under the @circadify scope should be fetched from GitHub Packages with your token, while every other package continues to come from the public npm registry.
4. Install the packages
Section titled “4. Install the packages”npm install @circadify/web-sdk @circadify/reactyarn add @circadify/web-sdk @circadify/reactpnpm add @circadify/web-sdk @circadify/react5. Import the stylesheet
Section titled “5. Import the stylesheet”@circadify/react ships an opt-in stylesheet for the bundled views (Readiness, Scan, Results). Import it once — typically alongside your global CSS or in your app entry point:
import '@circadify/react/styles.css';Without this import the components will render unstyled. If you’re building a fully custom UI with the hooks only and never mounting <CircadifyScan> (or the composable views), the stylesheet is optional.
Provider Setup
Section titled “Provider Setup”Wrap your application (or the relevant subtree) with the CircadifyProvider. This initializes the SDK and makes it available to all child components via React context.
import { CircadifyProvider } from '@circadify/react';
function App() { return ( <CircadifyProvider apiKey="ck_live_your_key_here"> <YourApp /> </CircadifyProvider> );}Provider Props
Section titled “Provider Props”| Prop | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Your Circadify API key (ck_live_*). Required. |
onProgress | (progress: ProgressEvent) => void | — | Global callback for scan progress updates. |
onQualityWarning | (warning: QualityWarning) => void | — | Called when lighting, movement, or face positioning issues are detected. |
onDeviceOnly | boolean | true | Restrict processing to on-device only. |
Using the Component
Section titled “Using the Component”The CircadifyScan component renders a complete scanning interface with a camera feed, face guide overlay, progress indicator, and quality warnings. It handles the full session lifecycle automatically.
import { CircadifyScan } from '@circadify/react';
function ScanPage() { const handleResult = (result) => { console.log('Heart Rate:', result.heartRate); console.log('Confidence:', result.confidence); };
return ( <CircadifyScan type="standard" onResult={handleResult} onError={(error) => console.error(error)} /> );}Component Props
Section titled “Component Props”| Prop | Type | Default | Description |
|---|---|---|---|
type | 'standard' | 'standard' | The scan session type. |
onResult | (result: VitalSignsResult) => void | — | Called when a scan completes successfully. |
onError | (error: CircadifyError) => void | — | Called when a scan fails. |
onProgress | (progress: ProgressEvent) => void | — | Called during scan with progress updates. |
onPhaseChange | (phase: ScanPhase) => void | — | Fires on every state-machine transition (idle → readiness → scanning → processing → uploading → results | error). |
demographics | Demographics | — | { age?, sex?, fitzpatrick? }. Optional, improves accuracy. |
autoStart | boolean | false | Start scanning immediately when the component mounts. |
showReadiness | boolean | true | Render the Readiness step (camera check + 4-channel quality pills) before scanning. |
showResults | boolean | true | Render the Results view after a successful scan. Set false if you want to render results in your own UI via onResult. |
showFeedback | boolean | true | Render the live quality meters and status banner during scanning. |
resultsActions | ReactNode | — | Slot rendered under the metric stack on the Results view (share/save buttons, external CTAs). |
onRescan | () => void | — | When supplied, a default outlined “Scan Again” button is appended to the Results actions slot and the orchestrator resets to readiness on click. |
className | string | — | CSS class applied to the root .cf-card element. |
style | React.CSSProperties | — | Inline styles for the root element. |
Handling Results
Section titled “Handling Results”The onResult callback receives a VitalSignsResult object:
function ScanPage() { const [vitals, setVitals] = useState(null);
return ( <> <CircadifyScan type="standard" demographics={{ age: 35, sex: 'M' }} onResult={(result) => setVitals(result)} onError={(error) => alert(error.message)} /> {vitals && ( <div> <p>Heart Rate: {vitals.heartRate} BPM</p> <p>SpO2: {vitals.spo2}%</p> <p>Confidence: {(vitals.confidence * 100).toFixed(0)}%</p> </div> )} </> );}Composable Subcomponents
Section titled “Composable Subcomponents”When the all-in-one <CircadifyScan> doesn’t fit — for example, you want a custom view between Readiness and Results, or you want to render Results inside a modal — import the underlying views and pair them with useSession() (or your own state).
Each subcomponent is presentational and props-driven; none of them own SDK lifecycle.
| Component | Purpose |
|---|---|
CircadifyReadiness | Camera Check view: video preview, 4-channel quality pills (Face / Light / Still / Forward), optional gated Start button, in-frame loading/error overlay. |
CircadifyScanView | Active-scan view: video + R3F thermal-glow overlay + quality meters + carded progress bar + processing-state spinner. |
CircadifyResults | Post-scan metric stack with per-card clipboard copy, an actions slot, and an onRescan shortcut. |
import { CircadifyProvider, CircadifyReadiness, CircadifyResults, useSession, useCamera,} from '@circadify/react';import { useRef } from 'react';import '@circadify/react/styles.css';
function CustomFlow() { const session = useSession(); const videoRef = useRef(null); const camera = useCamera({ videoRef });
if (session.result) { return <CircadifyResults result={session.result} onRescan={() => session.start()} />; } return ( <CircadifyReadiness videoRef={videoRef} landmarks={null} /* wire from your own subscription */ quality={null} cameraStatus={camera.status} cameraError={camera.error} onStart={() => session.start({ videoElement: videoRef.current })} /> );}Using Hooks
Section titled “Using Hooks”For more control over the scanning lifecycle, use the SDK hooks directly. This is useful when you need a custom UI or want to integrate scanning into an existing component.
useCircadify()
Section titled “useCircadify()”Returns the underlying CircadifySDK instance from the nearest CircadifyProvider. Use this for direct SDK access.
import { useCircadify } from '@circadify/react';
function DebugPanel() { const sdk = useCircadify(); // Access sdk.measureVitals(), sdk.destroy(), sdk.cancel(), sdk.getDeviceCapabilities(), etc.}useSession(options)
Section titled “useSession(options)”Manages a scan session with start/stop controls and reactive state. Returns the session state, control functions, and result data.
import { useCircadify, useSession } from '@circadify/react';
function CustomScan() { const client = useCircadify(); const { session, start, stop, result, error, progress, isScanning } = useSession({ type: 'standard', });
return ( <div> <button onClick={start} disabled={isScanning}>Start Scan</button> <button onClick={stop} disabled={!isScanning}>Stop</button>
{isScanning && <p>Progress: {Math.round(progress * 100)}%</p>} {error && <p>Error: {error.message}</p>} {result && <pre>{JSON.stringify(result, null, 2)}</pre>} </div> );}Return values:
| Property | Type | Description |
|---|---|---|
session | Session | null | The active session object, or null if not started. |
start | (opts?: UseSessionStartOptions) => Promise<void> | Start a new scan session. Optional opts: { signal?, demographics?, videoElement? }. Pass a videoElement to drive your own <video> instead of letting the SDK create one. |
stop | () => Promise<void> | Stop the active session. |
result | VitalSignsResult | null | The scan result once complete. |
error | CircadifyError | null | The error if the session failed. |
progress | number | Progress value from 0 to 1. |
isScanning | boolean | Whether a scan is currently in progress. |
Utility hooks and helpers
Section titled “Utility hooks and helpers”When you’re driving the camera and feedback UI yourself, three additional helpers from @circadify/react make custom flows easier:
useCamera({ videoRef, timeoutMs })— encapsulatedgetUserMedialifecycle with permission and timeout handling. Returns{ status, error, start, stop }with errors mapped to typedCircadifyErrorcodes.summarizeQuality({ state, faceDetected })anddetailFor(channel, state, faceDetected)— derive the four-channel UI summary (Face / Light / Still / Forward) from the SDK’sQualityStateevents. Use these to render your own quality pills or banners.formatVitals(result)andmetricToClipboard(metric)— produce metric-card-ready payloads from aVitalSignsResult, including pretty units and clipboard strings.
Cancellation with AbortController
Section titled “Cancellation with AbortController”You can pass an AbortController signal through the hook to allow cancellation:
function CancellableScan() { const controllerRef = useRef(null); const { start, stop, result } = useSession({ type: 'standard' });
const handleStart = () => { controllerRef.current = new AbortController(); start({ signal: controllerRef.current.signal }); };
const handleCancel = () => { controllerRef.current?.abort(); };
return ( <div> <button onClick={handleStart}>Start</button> <button onClick={handleCancel}>Cancel</button> </div> );}Cleanup
Section titled “Cleanup”The CircadifyProvider automatically calls sdk.destroy() when it unmounts, releasing camera streams, WASM modules, and event listeners. No manual cleanup is required in most cases.
If you need to manually destroy the SDK instance (for example, on logout), use the useCircadify hook:
const sdk = useCircadify();sdk.destroy();Next Steps
Section titled “Next Steps”- Custom UI — Build a fully custom interface
- SDK Methods — Full method reference