> ## Documentation Index
> Fetch the complete documentation index at: https://omniloy.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# React

> SofIA SDK integration with React

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

## Initial setup

### 1. Installation

```bash theme={null}
npm install @omniloy/sofia-sdk
```

### 2. Import in your application

```typescript theme={null}
import { Omniscribe, LanguageCode } from '@omniloy/sofia-sdk/react';
import '@omniloy/sofia-sdk/react/index.css';
```

## Basic React component

### Implementation with hooks

```tsx theme={null}
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
        apikey="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

| Prop        | Type   | Description                                                                 |
| ----------- | ------ | --------------------------------------------------------------------------- |
| `apikey`    | string | Authentication key from Omniloy                                             |
| `userid`    | string | User identifier in the EHR/HIS system (internal surrogate id, never PII)    |
| `patientid` | string | Patient identifier in the EHR/HIS system (internal surrogate id, never PII) |

### Report generation (required together)

| Prop         | Type   | Description                                                         |
| ------------ | ------ | ------------------------------------------------------------------- |
| `template`   | object | Template object for report generation                               |
| `templateid` | string | Template identifier (both `template` and `templateid` are required) |

### Optional

| Prop          | Type         | Description                                                                     |
| ------------- | ------------ | ------------------------------------------------------------------------------- |
| `baseurl`     | string       | SofIA API endpoint. Optional for newer keys (auto-resolved); required otherwise |
| `isopen`      | boolean      | Show/hide component                                                             |
| `language`    | LanguageCode | Interface language (e.g., `LanguageCode.en`)                                    |
| `debug`       | boolean      | Enable debug logging                                                            |
| `patientdata` | object       | Patient information (see structure below)                                       |

### Callbacks

| Prop               | Signature                                                | Description                                    |
| ------------------ | -------------------------------------------------------- | ---------------------------------------------- |
| `handleReport`     | `(report: unknown) => void`                              | Receives generated reports                     |
| `setIsOpen`        | `(value: boolean \| (prev: boolean) => boolean) => void` | Controls widget visibility                     |
| `setGetLastReport` | `(fn: () => Promise<unknown>) => void`                   | Exposes async function to retrieve last report |

## Retrieving the last report

The `setGetLastReport` callback receives an async function that you can store and call later:

```tsx theme={null}
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>
```

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

  ```tsx theme={null}
  <Omniscribe debug={true} ... />
  ```
</Tip>

## Patient data updates

Use React state to dynamically update patient context. The SDK automatically re-initializes when `patientid` or `userid` change.

```tsx theme={null}
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

```typescript theme={null}
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

| Field                   | Description                                                                                                     |
| ----------------------- | --------------------------------------------------------------------------------------------------------------- |
| `medical_practice`      | The doctor's specialty (e.g., `"Cardiology"`, `"Pediatrics"`). Helps SofIA tailor responses to clinical context |
| `patient_medical_notes` | Previous 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

```typescript theme={null}
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](/sofia/en/sdk/patient-data) for the complete data structure reference.

## State management with context

For applications that need to share SofIA state across multiple components:

```tsx theme={null}
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](https://github.com/Omniloy/sofia-sdk-examples/tree/main/examples/react)**

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

```tsx theme={null}
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](/sofia/en/sdk/error-reference).

<Warning>
  Never expose your `apikey` in client code in production. Use a backend proxy to inject the API key server-side. See the [Installation guide](/sofia/en/sdk/installation#production-security) for details.
</Warning>

## Next steps

1. [JavaScript integration](/sofia/en/sdk/vanilla)
2. [Angular integration](/sofia/en/sdk/angular)
3. [Required properties reference](/sofia/en/sdk/required-properties)
4. [Optional properties reference](/sofia/en/sdk/optional-properties)
