Skip to content

React Integration

This guide walks you through integrating Circadify into a React application using the official React bindings.

The @circadify/react package provides React components and hooks. It requires @circadify/web-sdk as a peer dependency, so install both.

@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.

The granted GitHub user authenticates to GitHub Packages with a Personal Access Token (PAT) they create themselves — Circadify never sees this token.

  1. Sign in to GitHub as the granted user.

  2. Go to github.com/settings/tokens/new (or Settings → Developer settings → Personal access tokens → Tokens (classic) → Generate new token (classic)).

  3. Name the token (e.g. circadify-web-sdk-install), set an expiration (90 days recommended), and check the read:packages scope. No other scopes are required.

  4. Click Generate token and copy the value — it starts with ghp_… and is shown only once.

Add a project-level .npmrc file (next to your package.json):

.npmrc
@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:

Terminal window
export GITHUB_TOKEN=ghp_your_read_packages_token

This 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.

Terminal window
npm install @circadify/web-sdk @circadify/react

@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.

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>
);
}
PropTypeDefaultDescription
apiKeystringYour Circadify API key (ck_live_*). Required.
onProgress(progress: ProgressEvent) => voidGlobal callback for scan progress updates.
onQualityWarning(warning: QualityWarning) => voidCalled when lighting, movement, or face positioning issues are detected.
onDeviceOnlybooleantrueRestrict processing to on-device only.

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)}
/>
);
}
PropTypeDefaultDescription
type'standard''standard'The scan session type.
onResult(result: VitalSignsResult) => voidCalled when a scan completes successfully.
onError(error: CircadifyError) => voidCalled when a scan fails.
onProgress(progress: ProgressEvent) => voidCalled during scan with progress updates.
onPhaseChange(phase: ScanPhase) => voidFires on every state-machine transition (idlereadinessscanningprocessinguploadingresults | error).
demographicsDemographics{ age?, sex?, fitzpatrick? }. Optional, improves accuracy.
autoStartbooleanfalseStart scanning immediately when the component mounts.
showReadinessbooleantrueRender the Readiness step (camera check + 4-channel quality pills) before scanning.
showResultsbooleantrueRender the Results view after a successful scan. Set false if you want to render results in your own UI via onResult.
showFeedbackbooleantrueRender the live quality meters and status banner during scanning.
resultsActionsReactNodeSlot rendered under the metric stack on the Results view (share/save buttons, external CTAs).
onRescan() => voidWhen supplied, a default outlined “Scan Again” button is appended to the Results actions slot and the orchestrator resets to readiness on click.
classNamestringCSS class applied to the root .cf-card element.
styleReact.CSSPropertiesInline styles for the root element.

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>
)}
</>
);
}

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.

ComponentPurpose
CircadifyReadinessCamera Check view: video preview, 4-channel quality pills (Face / Light / Still / Forward), optional gated Start button, in-frame loading/error overlay.
CircadifyScanViewActive-scan view: video + R3F thermal-glow overlay + quality meters + carded progress bar + processing-state spinner.
CircadifyResultsPost-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 })}
/>
);
}

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.

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.
}

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:

PropertyTypeDescription
sessionSession | nullThe 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.
resultVitalSignsResult | nullThe scan result once complete.
errorCircadifyError | nullThe error if the session failed.
progressnumberProgress value from 0 to 1.
isScanningbooleanWhether a scan is currently in progress.

When you’re driving the camera and feedback UI yourself, three additional helpers from @circadify/react make custom flows easier:

  • useCamera({ videoRef, timeoutMs }) — encapsulated getUserMedia lifecycle with permission and timeout handling. Returns { status, error, start, stop } with errors mapped to typed CircadifyError codes.
  • summarizeQuality({ state, faceDetected }) and detailFor(channel, state, faceDetected) — derive the four-channel UI summary (Face / Light / Still / Forward) from the SDK’s QualityState events. Use these to render your own quality pills or banners.
  • formatVitals(result) and metricToClipboard(metric) — produce metric-card-ready payloads from a VitalSignsResult, including pretty units and clipboard strings.

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>
);
}

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();