
Company News
Socket Partners with Replit to Block Malicious Packages in AI-Powered Development
Replit is integrating Socket Firewall into its AI-powered development experience to help protect builders from malicious open source packages.
@unith-ai/react
Advanced tools
A React hooks library for building complex digital human experiences that run on Unith AI.
Before proceeding with using this library, you're expected to have an account on Unith AI, create a digital human and take note of your API key. You can create an account here in minutes!
Install the package in your project through package manager.
npm install @unith-ai/react
# or
yarn add @unith-ai/react
# or
pnpm install @unith-ai/react
If using microphoneProvider: "azure", install the Azure Speech SDK:
npm install microsoft-cognitiveservices-speech-sdk
The SDK is dynamically imported only when the Azure provider is used, so it won't affect bundle size if you use a different provider like eleven_labs.
This library provides React hooks for integrating Unith AI digital humans into your React applications.
The useConversation hook manages the digital human conversation state and provides methods to control the session.
import { useConversation } from "@unith-ai/react";
function MyComponent() {
const conversation = useConversation({
orgId: "your-org-id",
headId: "your-head-id",
apiKey: "your-api-key",
});
// Use conversation methods and state
}
The hook accepts a configuration object with the following properties:
"azure" | "eleven_labs"boolean (default: false)string Custom data to be used for analytics.string Custom identifier for user..sendMessage method with your transcript as microphone doesn't automatically commit / send users text to our AI.speaking) and trigger the stop response method (.stopResponse)."ON" | "OFF" | "PROCESSING" Shows current status of microphone.ElevenLabsOptions - Custom options to customize behavior of ElevenLabs STT provider
BooleanNumberNumberNumberNumberBoolean - This disables ElevenLabs dynamic speech recognition language detection and uses the language specified during digital human creation.The hook returns an object containing methods and state:
HTMLElement - DOM element where the video will be renderedPartial<ConversationEvents> - Optional event callbacksPromise<string | undefined> - The user IDPromise<string> - Video URLPromise<void>string - Message text to sendPromise<void>number | undefined - New volume (0 for muted, 1 for unmuted)Promise<void>Promise<void>string | undefinedPromise<void>onStoppingStart() - Called immediately when stop is initiated.onStoppingEnd() - Called once the current response is stopped."connecting" | "connected" | "disconnecting" | "disconnected" - Current WebSocket connection statusboolean - True if status is "connected"boolean - True if status is "disconnected"boolean - True if status is not "connected"boolean - True if session has been started"listening" | "speaking" | "thinking" | "stopping" - Current conversation modeboolean - True if mode is "speaking"MessageEventData[] - Array of conversation messagesstring[] - Array of suggestion strings.number - Count of messages sentstring | null - Current user's unique identifierConnectHeadType | null - Information about the digital human
string - Digital human head namestring[] - Array with phrases set during digital human creationstring - Language code setup during digital human creationstring - Static image URL for digital humanboolean - True if microphone access was grantedboolean - True if audio is mutedboolean - True when session timeout warning is activeboolean - True when session has timed outboolean - True if a capacity error occurredimport { useConversation } from "@unith-ai/react";
import { useRef, useEffect } from "react";
function DigitalHumanChat() {
const videoRef = useRef(null);
const conversation = useConversation({
orgId: "your-org-id",
headId: "your-head-id",
apiKey: "your-api-key",
});
useEffect(() => {
if (videoRef.current) {
conversation.startDigitalHuman(videoRef.current, {
onConnect: ({ userId, headInfo, microphoneAccess }) => {
console.log("Connected:", userId);
},
onMessage: ({ timestamp, sender, text, visible }) => {
console.log("Message:", text);
},
onError: ({ message, endConversation, type }) => {
console.error("Error:", message);
},
});
}
}, []);
const handleSendMessage = () => {
conversation.sendMessage("Hello!");
};
const handleStartSession = () => {
conversation.startSession();
};
return (
<div>
<div ref={videoRef} style={{ width: "100%", height: "500px" }} />
{conversation.isConnected && !conversation.sessionStarted && (
<button onClick={handleStartSession}>Start Conversation</button>
)}
{conversation.sessionStarted && (
<button onClick={handleSendMessage}>Send Message</button>
)}
<div>
{conversation.messages.map((msg, index) => (
<div key={index}>
<strong>{msg.sender}:</strong> {msg.text}
</div>
))}
</div>
</div>
);
}
import { useConversation } from "@unith-ai/react";
import { useRef, useEffect, useState } from "react";
function AdvancedChat() {
const videoRef = useRef(null);
const conversationRef = useRef<ReturnType<typeof useConversation> | null>(null);
const [inputText, setInputText] = useState("");
const [micStatus, setMicStatus] = useState("OFF");
const conversation = useConversation({
orgId: "your-org-id",
headId: "your-head-id",
apiKey: "your-api-key",
mode: "default",
language: "en-US",
username: "test-username"
});
useEffect(() => {
conversationRef.current = conversation;
}, [conversation]);
useEffect(() => {
if (videoRef.current && conversationRef.current) {
conversationRef.current.startDigitalHuman(videoRef.current, {
microphoneProvider: 'eleven_labs',
voiceInterruptions: true,
microphoneEvents: {
onMicrophoneError(prop) {
console.log(`Microphone error: ${prop.message}`);
},
onMicrophoneSpeechRecognitionResult(prop) {
conversationRef.current.sendMessage(prop.transcript);
},
onMicrophonePartialSpeechRecognitionResult(){
conversationRef.current.stopResponse();
},
onMicrophoneStatusChange(prop) {
console.log('Microphone status changed:', prop.status);
setMicStatus(prop.status);
},
},
onConnect: ({ userId, headInfo, microphoneAccess }) => {
console.log("Connected with user ID:", userId);
console.log("Digital human:", headInfo.name);
},
onMessage: ({ timestamp, sender, text, visible }) => {
console.log(`[${sender}] ${text}`);
},
onSpeakingStart: () => {
console.log("Digital human started speaking");
},
onSpeakingEnd: () => {
console.log("Digital human finished speaking");
},
onSuggestions: ({ suggestions }) => {
console.log({ suggestions });
},
onTimeoutWarning: () => {
console.log("Session will timeout soon");
},
onTimeout: () => {
console.log("Session timed out");
},
onError: ({ message, type }) => {
if (type === "toast") {
alert(message);
}
},
onStoppingStart: () => {
console.log("stop started");
},
onStoppingEnd: () => {
console.log("response stopped.");
}
});
}
}, []);
const handleSendMessage = async e => {
e.preventDefault();
if (inputText.trim()) {
await conversation.sendMessage(inputText);
setInputText("");
}
};
const handleKeepSession = () => {
conversation.keepSession();
};
return (
<div>
<div ref={videoRef} style={{ width: "100%", height: "500px" }} />
<div>
<p>Status: {conversation.status}</p>
<p>Mode: {conversation.mode}</p>
{conversation.isSpeaking && <p>Digital human is speaking...</p>}
</div>
{conversation.isConnected && !conversation.sessionStarted && (
<button onClick={() => conversation.startSession()}>
Start Conversation
</button>
)}
{conversation.timeOutWarning && (
<div>
<p>Your session will timeout soon</p>
<button onClick={handleKeepSession}>Keep Session Active</button>
</div>
)}
{conversation.sessionStarted && (
<form onSubmit={handleSendMessage}>
<input
type="text"
value={inputText}
onChange={e => setInputText(e.target.value)}
disabled={conversation.mode !== "listening"}
placeholder="Type your message..."
/>
<button type="submit" disabled={conversation.mode !== "listening"}>
Send
</button>
<button type="button" onClick={() => conversation.toggleMute()}>
{conversation.isMuted ? "Unmute" : "Mute"}
</button>
</form>
)}
<div>
<h3>Messages ({conversation.messageCounter})</h3>
{conversation.messages.map(
(msg, index) =>
msg.visible && (
<div key={index}>
<strong>{msg.sender}:</strong> {msg.text}
<small> ({msg.timestamp.toLocaleTimeString()})</small>
</div>
)
)}
</div>
</div>
);
}
Messages in the conversation follow this structure:
interface MessageEventData {
timestamp: Date;
sender: "user" | "ai";
text: string;
visible: boolean;
}
When calling startDigitalHuman, you can pass event callbacks:
Boolean Unique Identifier for the users session.ConnectHeadType Object with data about the digital human.
String Digital human head nameString[] Array with phrases set during digital human creation.String Language code setup during digital human creation.String Static image url for digital human.Boolean True if microphone access was granted, False otherwise."connecting" | "connected" | "disconnecting" | "disconnected" Shows current websocket connection status.Date Timestamp when message was received/sent"user" | "ai" Shows who the message came from.String Message textBoolean Flag that you can use to control visibility of message. Sometimes, message comes before the video response starts playing. In such cases, this is usually false. Listen the onSpeakingStart event to change visibility when the video response starts playing.String[] A list of suggested query strings.keepSession method to extend the customers session.Handle errors using the onError callback:
conversation.startDigitalHuman(videoRef.current, {
onError: ({ message, endConversation, type }) => {
if (type === "toast") {
// Show toast notification
showToast(message);
if (endConversation) {
// Restart the session
conversation.endSession();
}
} else if (type === "modal") {
// Show modal dialog
showModal(message);
}
},
});
Retrieve the background video URL for welcome screens:
const { getBackgroundVideo } = useConversation({
orgId: "your-org-id",
headId: "your-head-id",
apiKey: "your-api-key",
});
useEffect(() => {
async function loadBackgroundVideo() {
const videoUrl = await getBackgroundVideo();
// Use videoUrl for your background/welcome screen
}
loadBackgroundVideo();
}, []);
To stop a response:
conversationRef.current.stopResponse();
Full TypeScript types are included with the library. Import types as needed:
import { useConversation } from "@unith-ai/react";
import type {
HeadConfigOptions,
MessageEventData,
Status,
Mode,
} from "@unith-ai/react";
mode === "listening" to avoid interrupting the digital humanendSession() on unmount, but you can call it manually if neededonTimeoutWarning by calling keepSession() to extend the sessiononError callback to handle connection and capacity errorsPlease refer to the README.md file in the root of this repository.
FAQs
React hooks for Unith AI digital humans
The npm package @unith-ai/react receives a total of 22 weekly downloads. As such, @unith-ai/react popularity was classified as not popular.
We found that @unith-ai/react demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Replit is integrating Socket Firewall into its AI-powered development experience to help protect builders from malicious open source packages.

Security News
npm confirmed a tooling bug incorrectly marked several one-character packages as security holders and said it was working on a rollback.

Research
/Security News
Newer packages in this compromise use native extensions and .pth loaders to execute JavaScript stealers in developer environments.