A beautiful live transcription component for your calls, crafted with React and Tailwind CSS.
Using hook such as, use-webrtc.ts , is required. Notice how the transcription component accesses the live transcript as it streams.
import React from "react" ;
import Transcriber from "@/components/examples/transcriber" ; // Adjust the import path as needed
import useWebRTCAudioSession from "@/hooks/use-webrtc" ;
function TranscriberExample () {
const { handleStartStopClick , isSessionActive , conversation , msgs } = useWebRTCAudioSession ( 'alloy' );
console. log ( JSON . stringify (msgs));
return (
< section className = "p-6 relative w-full mx-auto flex flex-col justify-center items-center gap-8" >
< div className = "flex flex-col gap-5 text-center animate-hero-in" >
< button
onClick = {handleStartStopClick}
className = "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-secondary shadow hover:bg-indigo/30 hover:text-indigo h-9 px-4 py-2"
>
{ isSessionActive ? "Stop Call" : "Start Call" }
</ button >
</ div >
{ isSessionActive && (
< div className = "relative flex justify-center items-center aspect-video w-full" >
< span className = "absolute top-48 w-[calc(100%-70%)] h-[calc(100%-70%)] bg-purple-700 blur-[120px]" > </ span >
< div className = "size-full mx-auto" >
< Transcriber conversation = {conversation} />
</ div >
</ div >
)}
</ section >
);
}
export default TranscriberExample;
Show More
Note: This is not the entire configuration file, only the relevant parts are shown. Make sure to include the new configuration inside the theme object.
Copy and paste the following code into your component, example transcriber.tsx .
"use client" ;
import * as React from "react" ;
import * as AvatarPrimitive from "@radix-ui/react-avatar" ;
import { cn } from "@/lib/utils" ;
import { Message } from "@/lib/conversations" ;
interface TranscriberProps {
conversation : Message [];
}
const Avatar = React.forwardRef <
React.ElementRef <typeof AvatarPrimitive.Root > ,
React.ComponentPropsWithoutRef <typeof AvatarPrimitive.Root >
> (({ className , ... props }, ref ) => (
< AvatarPrimitive.Root
ref = {ref}
className = { cn (
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full" ,
className
)}
{ ... props}
/>
));
Avatar.displayName = AvatarPrimitive.Root.displayName;
const AvatarImage = React.forwardRef <
React.ElementRef <typeof AvatarPrimitive.Image > ,
React.ComponentPropsWithoutRef <typeof AvatarPrimitive.Image >
> (({ className , ... props }, ref ) => (
< AvatarPrimitive.Image
ref = {ref}
className = { cn ( "aspect-square h-full w-full" , className)}
{ ... props}
/>
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
const AvatarFallback = React.forwardRef <
React.ElementRef <typeof AvatarPrimitive.Fallback > ,
React.ComponentPropsWithoutRef <typeof AvatarPrimitive.Fallback >
> (({ className , ... props }, ref ) => (
< AvatarPrimitive.Fallback
ref = {ref}
className = { cn (
"flex h-full w-full items-center justify-center rounded-full bg-muted" ,
className
)}
{ ... props}
/>
));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
function Transcriber ({ conversation } : TranscriberProps ) {
const scrollRef = React. useRef < HTMLDivElement >( null );
React. useEffect (() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}
}, [conversation]);
return (
< div className = "flex flex-col size-full max-w-full mx-auto bg-background rounded-lg shadow-lg overflow-hidden dark:bg-background" >
< div className = "bg-secondary px-4 py-3 flex items-center justify-between dark:bg-secondary" >
< div className = "font-medium text-foreground dark:text-foreground" >Live Transcript</ div >
</ div >
< div ref = {scrollRef} className = "flex-1 overflow-y-auto p-4 space-y-4 z-50" >
{conversation. map (( message , index ) => (
< div key = {index} className = { `flex items-start gap-3 ${ message . role === 'user' ? 'justify-end' : ''}` }>
{message.role === 'assistant' && (
< Avatar className = "w-8 h-8 shrink-0" >
< AvatarImage src = "/placeholder-user.jpg" />
< AvatarFallback >AI</ AvatarFallback >
</ Avatar >
)}
< div className = { `bg-${ message . role === 'user' ? 'primary' : 'secondary'} px-4 py-1 rounded-lg max-w-[70%] ${ message . role === 'user' ? 'text-background' : 'dark:text-foreground'}` }>
< p >{message.text ? message.text : 'User is speaking...' }</ p >
< div className = "text-xs text-muted-foreground" >{ new Date (message.timestamp). toLocaleTimeString ( 'en-US' , { hour: 'numeric' , minute: 'numeric' })}</ div >
</ div >
{message.role === 'user' && (
< Avatar className = "w-8 h-8 shrink-0" >
< AvatarImage src = "/placeholder-user.jpg" />
< AvatarFallback >You</ AvatarFallback >
</ Avatar >
)}
</ div >
))}
</ div >
</ div >
);
}
export default Transcriber;
export { Avatar, AvatarImage, AvatarFallback };
Show More
Import the component in your file.
import Transcriber from "@/components/openai-blocks/transcriber" ;
export default function Home () {
return (
< main className = "w-full min-h-screen" >
< Transcriber conversation = {conversation} />
</ main >
);
}
Show More