Building a React App with Huddle01
Walkthrough
The following guide explains how you can integrate audio/video into your React web-app seamlessly using the Huddle01 React SDK.
Install @huddle01/react
in your react app
To power your React DApp with audio/video communication using Huddle01 install the following:
pnpm i @huddle01/react
Initialization of project
Head over to API Keys Page and connect your wallet to get your project credentials:
API Key
projectId
Once done, initialize your project by calling the initialize()
method and pass projectId
in params.
import { useHuddle01 } from '@huddle01/react';
const App = () => {
const { initialize, isInitialized } = useHuddle01();
useEffect(() => {
// its preferable to use env vars to store projectId
initialize('YOUR_PROJECT_ID');
}, []);
return <div>{isInitialized ? 'Hello World!' : 'Please initialize'}</div>;
};
Joining the lobby
Room Id can be generated using the : Create Room API
Add the following button to your jsx and call the joinLobby()
method.
import { useHuddle01 } from '@huddle01/react';
import { useLobby } from '@huddle01/react/hooks';
const App = () => {
const { initialize, isInitialized } = useHuddle01();
const { joinLobby } = useLobby();
useEffect(() => {
// its preferable to use env vars to store projectId
initialize('YOUR_PROJECT_ID');
}, []);
return (
<div>{isInitialized ? 'Hello World!' : 'Please initialize'}
<button
disabled={joinLobby.isCallable}
onClick={() => joinLobby('YOUR_ROOM_ID');
}>
Join Lobby
</button>
</div>
);
};
The isCallable
is an attribute available on all methods to check whether the function is callable or not.
Room creation using the Create Room API can be done in the following ways:
NextJs API Route to create room (NodeJs)
This is how you can create a room using NextJS API Route (opens in a new tab) and pass the roomId
to the joinLobby()
method.
import axios from 'axios';
import type { NextApiRequest, NextApiResponse } from 'next';
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { data } = await axios.post(
'https://api.huddle01.com/api/v1/create-room',
{
title: 'Huddle01-Test',
roomLock: false,
},
{
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.API_KEY,
},
}
);
res.status(200).json(data);
} catch (error) {
res.status(500).json(error);
}
};
export default handler;
NextJS getServerSideProps()
room creation example
getServerSideProps()
room creation exampleThis is how you can create a room using getServerSideProps() (opens in a new tab) and pass the roomId
to the joinLobby()
method.
export const getServerSideProps: GetServerSideProps<{
roomId: string;
}> = async () => {
const { data } = await axios.post(
'https://api.huddle01.com/api/v1/create-room',
{
title: 'Huddle01-SDK-Test',
roomLock: false,
},
{
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.API_KEY,
},
}
);
return {
props: {
roomId: data.roomId,
},
};
};
Enabling and Disabling Media Devices
If cam and mic aren’t enabled in the lobby, video and audio won’t be shareable inside the room
These methods are to be called only when in lobby state.
Then, add the locale codes to your file extensions (required for the default locale too):
import { useHuddle01 } from '@huddle01/react';
import { useLobby, useAudio, useVideo } from '@huddle01/react/hooks';
const App = () => {
const { initialize, isInitialized } = useHuddle01();
const { joinLobby } = useLobby();
const { fetchAudioStream, stopAudioStream, error: micError } = useAudio();
const { fetchVideoStream, stopVideoStream, error: camError } = useVideo();
// Show error if it exists
useEffect(() => {
// its preferable to use env vars to store projectId
initialize('YOUR_PROJECT_ID');
}, []);
return (
<div>{isInitialized ? 'Hello World!' : 'Please initialize'}
<button
disabled={joinLobby.isCallable}
onClick={() => joinLobby('YOUR_ROOM_ID');
}>
Join Lobby
</button>
{/* Mic */}
<button disabled={!fetchAudioStream.isCallable} onClick={fetchAudioStream}>
FETCH_AUDIO_STREAM
</button>
{/* Webcam */}
<button disabled={!fetchVideoStream.isCallable} onClick={fetchVideoStream}>
FETCH_VIDEO_STREAM
</button>
</div>
);
};
The isCallable
is an attribute available on all methods to check whether the function is callable or not.
Render your own video and audio streams
This is how can render your own video and audio streams
import { useHuddle01 } from '@huddle01/react';
import { Audio, Video } from '@huddle01/react/components';
const Media = () => {
const { meId } = useHuddle01();
const { stream: videoStream } = useVideo();
const { stream: audioStream } = useAudio();
return (
<div>
<Video peerId={meId} stream={videoStream} />
<Audio peerId={meId} stream={audioStream} />
</div>
);
};
Joining and leaving the room
The joinRoom() method can be called to enter the room, once the lobby has been joined. To leave that respective room you can call the leaveRoom() method in the ROOM state.
import { useHuddle01 } from '@huddle01/react';
import { useLobby, useAudio, useVideo, useRoom } from '@huddle01/react/hooks';
const App = () => {
const { initialize, isInitialized } = useHuddle01();
const { joinLobby } = useLobby();
const { fetchAudioStream, stopAudioStream, error: micError } = useAudio();
const { fetchVideoStream, stopVideoStream, error: camError } = useVideo();
const { joinRoom, leaveRoom } = useRoom();
useEffect(() => {
// its preferable to use env vars to store projectId
initialize('YOUR_PROJECT_ID');
}, []);
return (
<div>{isInitialized ? 'Hello World!' : 'Please initialize'}
<button
disabled={joinLobby.isCallable}
onClick={() => joinLobby('YOUR_ROOM_ID');
}>
Join Lobby
</button>
{/* Mic */}
<button disabled={!fetchAudioStream.isCallable} onClick={fetchAudioStream}>
FETCH_AUDIO_STREAM
</button>
{/* Webcam */}
<button disabled={!fetchVideoStream.isCallable} onClick={fetchVideoStream}>
FETCH_VIDEO_STREAM
</button>
<button disabled={!joinRoom.isCallable} onClick={joinRoom}>
JOIN_ROOM
</button>
<button disabled={!leaveRoom.isCallable} onClick={leaveRoom}>
LEAVE_ROOM
</button>
</div>
);
};
The isCallable
is an attribute available on all methods to check whether the function is callable or not.
Clicking on the JOIN_ROOM button, will make the user join the room and will allow them to send/receive media with other participants.
Sending media across to other participants
Here, we are using a term PRODUCE which means sending your audio/video stream across to the other peer who will CONSUME (or receive) the streams.
import { useHuddle01 } from '@huddle01/react';
import { useLobby, useAudio, useVideo, useRoom } from '@huddle01/react/hooks';
const App = () => {
const { initialize, isInitialized } = useHuddle01();
const { joinLobby } = useLobby();
const {
fetchAudioStream, stopAudioStream, error: micError,
produceAudio, stopProducingAudio
} = useAudio();
const {
fetchVideoStream, stopVideoStream, error: camError,
produceVideo, stopProducingVideo
} = useVideo();
const { joinRoom, leaveRoom } = useRoom();
useEffect(() => {
// its preferable to use env vars to store projectId
initialize('YOUR_PROJECT_ID');
}, []);
return (
<div>{isInitialized ? 'Hello World!' : 'Please initialize'}
<button
disabled={joinLobby.isCallable}
onClick={() => joinLobby('YOUR_ROOM_ID');
}>
Join Lobby
</button>
{/* Mic */}
<button disabled={!fetchAudioStream.isCallable} onClick={fetchAudioStream}>
FETCH_AUDIO_STREAM
</button>
{/* Webcam */}
<button disabled={!fetchVideoStream.isCallable} onClick={fetchVideoStream}>
FETCH_VIDEO_STREAM
</button>
<button disabled={!joinRoom.isCallable} onClick={joinRoom}>
JOIN_ROOM
</button>
<button disabled={!leaveRoom.isCallable} onClick={leaveRoom}>
LEAVE_ROOM
</button>
<button disabled={!produceVideo.isCallable} onClick={() => produceVideo(camStream)}>
Produce Cam
</button>
<button disabled={!produceAudio.isCallable} onClick={() => produceAudio(micStream)}>
Produce Mic
</button>
<button disabled={!stopProducingVideo.isCallable} onClick={stopProducingVideo}>
Stop Producing Cam
</button>
<button disabled={!stopProducingAudio.isCallable} onClick={stopProducingAudio}>
Stop Producing Mic
</button>
</div>
);
};
Receiving the audio and video streams
Here, we are using a term PRODUCE which means sending your audio/video stream across to the other peer who will CONSUME (or receive) the streams.
Getting access to data related to peers
JSX:
import { usePeers } from '@huddle01/react/hooks';
export default function() {
const peers = usePeers();
return <div>{JSON.stringify(peers)}</div>
}
Output: (Example Output for 1 peer)
[
{
peerId: "sdksjdsjd",
mic: MediaStreamTrack,
cam: MediaStreamTrack
}
]
Using the audio and video tags
Importing the Audio and Video tags:
// importing
import { Video, Audio } from '@huddle01/react/components'
There are three ways to use the audio and video tags
1. Using peerId
:
<Video peerId="PEER_ID" />
<Audio peerId="PEER_ID" />
2. Using track
or stream
:
The stream
value for mic and cam are available on the useAudio
and useVideo
hooks respectively.
The tracks
for peers can be found from the usePeers
hook.
To be used only when you want to make changes to streams from your side otherwise use peerId
method!
<Video peerId="PEER_ID" track={camTrack} />
<Audio peerId="PEER_ID" track={micTrack} />
<Video peerId="PEER_ID" stream={camStream} />
<Audio peerId="PEER_ID" stream={micStream} />
The video and audio streams for the peers can be accessed using the usePeers
hook. The usePeers
hook returns an array of objects with the following structure:
[
{
peerId: "sdksjdsjd",
mic: MediaStreamTrack,
cam: MediaStreamTrack
}
]
The peerId
is the unique identifier for the peer. The mic
and cam
are the MediaStreamTrack
objects for the audio and video streams respectively.
The usePeers
hook can be used to access the audio and video streams for the peers and can be used in the following way to render the audio and video streams:
import { useHuddle01 } from '@huddle01/react';
import { useLobby, useAudio, useVideo, useRoom, usePeers } from '@huddle01/react/hooks';
const App = () => {
const { initialize, isInitialized } = useHuddle01();
const { joinLobby } = useLobby();
const {
fetchAudioStream, stopAudioStream, error: micError,
produceAudio, stopProducingAudio
} = useAudio();
const {
fetchVideoStream, stopVideoStream, error: camError,
produceVideo, stopProducingVideo
} = useVideo();
const { joinRoom, leaveRoom } = useRoom();
const { peerIds } = usePeers();
useEffect(() => {
// its preferable to use env vars to store projectId
initialize('YOUR_PROJECT_ID');
}, []);
return (
<div>{isInitialized ? 'Hello World!' : 'Please initialize'}
<div className="grid grid-cols-4">
{peerIds.map(peerId => (
<Video key={peer.peerId} peerId={peer.peerId} debug />
))}
{peerIds.map(peerId => (
<Audio key={peer.peerId} peerId={peer.peerId} debug />
))}
</div>
<button
disabled={joinLobby.isCallable}
onClick={() => joinLobby('YOUR_ROOM_ID');
}>
Join Lobby
</button>
{/* Mic */}
<button disabled={!fetchAudioStream.isCallable} onClick={fetchAudioStream}>
FETCH_AUDIO_STREAM
</button>
{/* Webcam */}
<button disabled={!fetchVideoStream.isCallable} onClick={fetchVideoStream}>
FETCH_VIDEO_STREAM
</button>
<button disabled={!joinRoom.isCallable} onClick={joinRoom}>
JOIN_ROOM
</button>
<button disabled={!leaveRoom.isCallable} onClick={leaveRoom}>
LEAVE_ROOM
</button>
<button disabled={!produceVideo.isCallable} onClick={() => produceVideo(camStream)}>
Produce Cam
</button>
<button disabled={!produceAudio.isCallable} onClick={() => produceAudio(micStream)}>
Produce Mic
</button>
<button disabled={!stopProducingVideo.isCallable} onClick={stopProducingVideo}>
Stop Producing Cam
</button>
<button disabled={!stopProducingAudio.isCallable} onClick={stopProducingAudio}>
Stop Producing Mic
</button>
</div>
);
};
You're all set! Happy Hacking! 🎉
For more information, please refer to the SDK Reference.