Skip to main content
This guide shows how to integrate SofIA SDK in React applications using the <Omniscribe> component.

Initial setup

1. Installation

npm install @omniloy/sofia-sdk

2. Import in your application

import { Omniscribe, LanguageCode } from '@omniloy/sofia-sdk/react';
import '@omniloy/sofia-sdk/react/index.css';

Basic React component

Implementation with hooks

import { useCallback, useState } from 'react';
import { Omniscribe, LanguageCode } from '@omniloy/sofia-sdk/react';
import '@omniloy/sofia-sdk/react/index.css';

const SofiaComponent = () => {
  const [isOpen, setIsOpen] = useState(true);
  const [lastReport, setLastReport] = useState<unknown>(null);
  const [getLastReportFn, setGetLastReportFn] = useState<(() => Promise<unknown>) | null>(null);

  const handleReport = useCallback((report: unknown) => {
    console.log('Report received:', report);
    setLastReport(report);
  }, []);

  const handleSetGetLastReport = useCallback((fn: () => Promise<unknown>) => {
    setGetLastReportFn(() => fn);
  }, []);

  const template = {
    title: 'clinical_notes',
    type: 'object',
    properties: {
      diagnosis: {
        type: 'string',
        description: 'Primary diagnosis',
        source: 'ICD10',
      },
      treatment_plan: {
        type: 'string',
        description: 'Treatment plan discussed during the consultation',
      },
    },
  };

  return (
    <div>
      <Omniscribe
        baseurl="https://api.example.com/v1"
        wssurl="wss://ws.example.com"
        apikey="sk-your-api-key"
        userid="user_12345"
        patientid="patient_67890"
        templateid="clinical-notes-v1"
        template={template}
        isopen={isOpen}
        setIsOpen={setIsOpen}
        handleReport={handleReport}
        setGetLastReport={handleSetGetLastReport}
        language={LanguageCode.en}
      />

      <button onClick={() => setIsOpen((prev) => !prev)}>
        {isOpen ? 'Close SofIA' : 'Open SofIA'}
      </button>

      {lastReport !== null && (
        <pre>{JSON.stringify(lastReport, null, 2)}</pre>
      )}
    </div>
  );
};

export default SofiaComponent;

Props

Required

PropTypeDescription
baseurlstringSofIA API endpoint
wssurlstringWebSocket URL for real-time features
apikeystringAuthentication key
useridstringUser identifier in the EHR/HIS system
patientidstringPatient identifier in the EHR/HIS system
templateobjectTemplate object for report generation
templateidstringTemplate identifier (both template and templateid are required)

Optional

PropTypeDescription
isopenbooleanShow/hide component
languageLanguageCodeInterface language (e.g., LanguageCode.en)
debugbooleanEnable debug logging
patientdataobjectPatient information (see structure below)
showconsentindicatorbooleanShow the consent status indicator in the header (default: false)

Callbacks

PropSignatureDescription
handleReport(report: unknown) => voidReceives generated reports
setIsOpen(value: boolean | (prev: boolean) => boolean) => voidControls widget visibility
setGetLastReport(fn: () => Promise<unknown>) => voidExposes async function to retrieve last report

Retrieving the last report

The setGetLastReport callback receives an async function that you can store and call later:
const [getLastReportFn, setGetLastReportFn] = useState<(() => Promise<unknown>) | null>(null);

const handleSetGetLastReport = useCallback((fn: () => Promise<unknown>) => {
  setGetLastReportFn(() => fn);
}, []);

const fetchLastReport = async () => {
  if (getLastReportFn) {
    const report = await getLastReportFn();
    console.log('Last report:', report);
  }
};

// In JSX:
<Omniscribe
  setGetLastReport={handleSetGetLastReport}
  // ...other props
/>

<button onClick={fetchLastReport} disabled={!getLastReportFn}>
  Get last report
</button>
Pass debug={true} to the <Omniscribe> component to enable verbose console logging. This is helpful during development to trace SDK lifecycle events, connection status, and configuration validation.
<Omniscribe debug={true} ... />

Patient data updates

Use React state to dynamically update patient context. The SDK automatically re-initializes when patientid or userid change.
const [patientId, setPatientId] = useState('patient_67890');
const [patientData, setPatientData] = useState({
  fullName: 'Jane Doe',
  birthDate: '15-03-1985',
  phone: '+1 555-987-6543',
  address: '456 Oak Ave, Example City, USA',
  extraData: {
    medical_practice: 'Internal Medicine',
    allergies: 'penicillin',
  },
});

// In JSX:
<Omniscribe
  patientid={patientId}
  patientdata={patientData}
  // ...other props
/>

// To switch patient:
function switchPatient(newId: string, newData: typeof patientData) {
  setPatientId(newId);
  setPatientData(newData);
}

Patient data structure

type TPatientData = {
  fullName: string | undefined;
  birthDate: string | undefined;
  phone: string | undefined;
  address: string | undefined;
  extraData: Record<string, unknown> | undefined;
  signedConsent?: { signed: boolean; date: string };
};

extraData fields

FieldDescription
medical_practiceThe doctor’s specialty (e.g., "Cardiology", "Pediatrics"). Helps SofIA tailor responses to clinical context
patient_medical_notesPrevious consultation notes. Include url fields if you want SofIA to cite the source
(custom fields)Any additional patient data relevant to the consultation (e.g., allergies, medications)

Full example

const patientData = {
  fullName: 'John Doe',
  birthDate: '01/15/1980',
  phone: '+1 555-123-4567',
  address: '123 Main St, Example City, USA',
  extraData: {
    medical_practice: 'Cardiology',
    patient_medical_notes: [
      {
        date: '2024-11-20',
        note: 'Patient presents with chest pain. ECG normal. Prescribed aspirin.',
        url: 'https://ehr.example.com/notes/12345',
      },
      {
        date: '2025-01-10',
        note: 'Follow-up visit. Chest pain resolved. Blood pressure 130/85.',
      },
    ],
    allergies: 'pollen, penicillin',
    medications: 'metformin, insulin, aspirin',
  },
  signedConsent: {
    signed: true,
    date: '2025-03-01',
  },
};
See Patient Data for the complete data structure reference.

State management with context

For applications that need to share SofIA state across multiple components:
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';

interface SofiaContextType {
  currentPatient: string | null;
  setCurrentPatient: (patientId: string) => void;
  reports: unknown[];
  addReport: (report: unknown) => void;
}

const SofiaContext = createContext<SofiaContextType | undefined>(undefined);

export const SofiaProvider = ({ children }: { children: ReactNode }) => {
  const [currentPatient, setCurrentPatient] = useState<string | null>(null);
  const [reports, setReports] = useState<unknown[]>([]);

  const addReport = useCallback((report: unknown) => {
    setReports((prev) => [...prev, report]);
  }, []);

  return (
    <SofiaContext.Provider value={{ currentPatient, setCurrentPatient, reports, addReport }}>
      {children}
    </SofiaContext.Provider>
  );
};

export const useSofiaContext = () => {
  const context = useContext(SofiaContext);
  if (!context) throw new Error('useSofiaContext must be used within SofiaProvider');
  return context;
};

Complete example

For a complete implementation with React and TypeScript, check our examples repository: View complete React example The example includes:
  • Complete setup with Vite + React 19 + TypeScript
  • Development console with real-time controls
  • Template and patient data editors with JSON validation
  • Dynamic User ID, Patient ID, and Template ID switching

Best practices

Performance

  • Use useCallback for stable callbacks (handleReport, setGetLastReport)
  • Implement lazy loading for components on non-critical routes

State

  • Pass setIsOpen directly — the SDK controls visibility
  • Changing userid or patientid automatically remounts the SDK

Error boundaries

import { Component, type ReactNode, type ErrorInfo } from 'react';

class SofiaErrorBoundary extends Component<
  { children: ReactNode },
  { hasError: boolean }
> {
  constructor(props: { children: ReactNode }) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Error in SofIA SDK:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <div>SofIA encountered an error. Check the browser console for details.</div>;
    }
    return this.props.children;
  }
}
For a complete list of error messages and their resolution steps, see the Error Reference.
Never expose your apikey in client code in production. Use a backend proxy to inject the API key server-side. See the Installation guide for details.

Next steps

  1. JavaScript integration
  2. Angular integration
  3. Required properties reference
  4. Optional properties reference