Skip to main content
This guide shows how to integrate SofIA SDK in React applications, leveraging native support for Web Components.

Initial setup

1. Installation

npm install @omniloy/sofia-sdk

2. Import in your application

// In your App.tsx or main file
import '@omniloy/sofia-sdk';

Basic React component

Implementation with hooks

import React, { useRef, useState, useEffect } from 'react';

interface SofiaComponentProps {
  userId: string;
  patientId: string;
  apiKey: string;
  baseUrl: string;
  wssUrl: string;
}

const SofiaComponent: React.FC<SofiaComponentProps> = ({
  userId,
  patientId,
  apiKey,
  baseUrl,
  wssUrl
}) => {
  const sofiaRef = useRef<any>(null);
  const [isOpen, setIsOpen] = useState(false);
  const [lastReport, setLastReport] = useState<any>(null);

  // Example schema for clinical notes
  const toolsArgs = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "clinical_notes",
    "type": "object",
    "required": ["diagnosis"],
    "properties": {
      "diagnosis": {
        "type": "string",
        "description": "Main diagnosis"
      },
      "symptoms": {
        "type": "array",
        "items": {"type": "string"},
        "description": "Observed symptoms"
      }
    }
  };

  // Configure callbacks when component mounts
  useEffect(() => {
    if (sofiaRef.current) {
      // Callback to receive reports
      sofiaRef.current.handleReport = (report: any) => {
        console.log('Report received:', report);
        setLastReport(report);
        
        // Process report (send to API, show notification, etc.)
        processReport(report);
      };

      // Callback for visibility changes
      sofiaRef.current.setIsOpen = (open: boolean) => {
        setIsOpen(open);
      };
    }
  }, []);

  const processReport = async (report: any) => {
    try {
      // Send report to your backend
      const response = await fetch('/api/clinical-reports', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(report)
      });
      
      if (response.ok) {
        console.log('Report saved successfully');
      }
    } catch (error) {
      console.error('Error processing report:', error);
    }
  };

  const toggleSofia = () => {
    if (sofiaRef.current) {
      sofiaRef.current.isopen = !isOpen;
    }
  };

  return (
    <div className="sofia-container">
      <sofia-sdk
        ref={sofiaRef}
        baseurl={baseUrl}
        wssurl={wssUrl}
        apikey={apiKey}
        userid={userId}
        patientid={patientId}
        isopen={isOpen.toString()}
        toolsargs={JSON.stringify(toolsArgs)}
      />
      
      <div className="sofia-controls">
        <button onClick={toggleSofia} className="toggle-button">
          {isOpen ? 'Close SofIA' : 'Open SofIA'}
        </button>
        
        {lastReport && (
          <div className="last-report">
            <h3>Last report:</h3>
            <pre>{JSON.stringify(lastReport, null, 2)}</pre>
          </div>
        )}
      </div>
    </div>
  );
};

export default SofiaComponent;

Custom hook

useSofia Hook

import { useRef, useState, useCallback } from 'react';

interface UseSofiaOptions {
  onReport?: (report: any) => void;
  onVisibilityChange?: (isOpen: boolean) => void;
}

export const useSofia = (options: UseSofiaOptions = {}) => {
  const sofiaRef = useRef<any>(null);
  const [isOpen, setIsOpen] = useState(false);
  const [isLoading, setIsLoading] = useState(false);

  const initializeSofia = useCallback(() => {
    if (sofiaRef.current) {
      sofiaRef.current.handleReport = (report: any) => {
        setIsLoading(false);
        options.onReport?.(report);
      };

      sofiaRef.current.setIsOpen = (open: boolean) => {
        setIsOpen(open);
        options.onVisibilityChange?.(open);
      };
    }
  }, [options]);

  const openSofia = useCallback(() => {
    if (sofiaRef.current) {
      sofiaRef.current.isopen = true;
    }
  }, []);

  const closeSofia = useCallback(() => {
    if (sofiaRef.current) {
      sofiaRef.current.isopen = false;
    }
  }, []);

  const generateReport = useCallback(() => {
    if (sofiaRef.current) {
      setIsLoading(true);
      // Triggering report generation if supported
      sofiaRef.current.dispatchEvent(new CustomEvent('generate'));
    }
  }, []);

  return {
    sofiaRef,
    isOpen,
    isLoading,
    initializeSofia,
    openSofia,
    closeSofia,
    generateReport
  };
};

Using the hook

const MyComponent: React.FC = () => {
  const { 
    sofiaRef, 
    isOpen, 
    isLoading, 
    initializeSofia, 
    openSofia, 
    closeSofia 
  } = useSofia({
    onReport: (report) => {
      console.log('Report processed:', report);
    },
    onVisibilityChange: (isOpen) => {
      console.log('State changed:', isOpen);
    }
  });

  useEffect(() => {
    initializeSofia();
  }, [initializeSofia]);

  return (
    <div>
      <sofia-sdk
        ref={sofiaRef}
        baseurl="https://api.example.com/v1"
        wssurl="wss://ws.example.com"
        apikey="sk-your-api-key-here"
        userid="user_12345"
        patientid="patient_67890"
        isopen={isOpen.toString()}
        toolsargs={JSON.stringify(mySchema)}
      />
      
      <button onClick={openSofia} disabled={isOpen}>
        Open SofIA
      </button>
      <button onClick={closeSofia} disabled={!isOpen}>
        Close SofIA
      </button>
      
      {isLoading && <div>Generating report...</div>}
    </div>
  );
};

State management with Context

Sofia Context Provider

import React, { createContext, useContext, useState, ReactNode } from 'react';

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

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

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

  const addReport = (report: any) => {
    setReports(prev => [...prev, { ...report, timestamp: new Date() }]);
  };

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

TypeScript Support

Type declarations

// types/sofia.d.ts
declare global {
  namespace JSX {
    interface IntrinsicElements {
      'sofia-sdk': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
        baseurl?: string;
        wssurl?: string;
        apikey?: string;
        userid?: string;
        patientid?: string;
        isopen?: string;
        toolsargs?: string;
        patientdata?: string;
        ref?: React.Ref<any>;
      };
    }
  }
}

Complete example

For a complete implementation with React and TypeScript, check our examples repository: View complete React example → The example includes:
  • Complete TypeScript setup
  • Global state with Context API
  • Reusable components
  • React Router integration
  • Testing with Jest and React Testing Library

Best practices

Performance

  • Use React.memo() for components wrapping sofia-sdk
  • Implement lazy loading for components on non-critical routes

State

  • Keep SofIA state synchronized with React state
  • Use useCallback for stable callbacks

Error Boundaries

class SofiaErrorBoundary extends React.Component {
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('Error in SofIA SDK:', error, errorInfo);
  }

  render() {
    return this.props.children;
  }
}