Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
baaee68
Tims pre-join patch
tbscode Apr 29, 2024
f4ec238
updated patch package version
tbscode Apr 30, 2024
2064805
update ParticipantTile to alllow for custom placeholder
Simba14 Apr 30, 2024
b1638ef
update packages
Simba14 Apr 30, 2024
29afb7a
add build of new version
Simba14 Apr 30, 2024
acb1a4f
reversed version
tbscode May 4, 2024
a5f3ff3
update lock file
tbscode May 4, 2024
45e37fc
merged seans updates
tbscode May 4, 2024
7a7507e
resync fork with upstream
Simba14 Mar 25, 2025
ccd2775
Improve PreJoin screen
Simba14 Apr 4, 2025
2e43693
bump version
Simba14 Apr 4, 2025
ae713f3
pack react 2.9.0 package
Simba14 Apr 4, 2025
5296dc6
Merge pull request #1 from a-little-world/feat/pre-join-improvements
Simba14 Apr 4, 2025
785e4ec
remove packed packages
Simba14 Apr 15, 2025
8a0c30c
add changeset
Simba14 Apr 15, 2025
5945086
update with upstream/main
Simba14 Aug 11, 2025
14f87e1
WIP: connect device availability
Simba14 Aug 22, 2025
ebbba30
Improve PreJoin to when there are permission errors
Simba14 Nov 6, 2025
b2e8c43
add permissions logic to livekit room and toggle
Simba14 Nov 6, 2025
4e18da3
add more styles for prejoin
Simba14 Nov 7, 2025
d2de0a5
Update prejoin example
Simba14 Nov 8, 2025
84a53e4
Improve track toggle and Prejoin to allow for better permission handling
Simba14 Nov 11, 2025
76ec6df
Improve track toggle and Prejoin to allow for better permission handling
Simba14 Nov 11, 2025
5abc83f
update livekit to allow for re-call example in video call
Simba14 Nov 12, 2025
a18d9ac
delete old react build
Simba14 Nov 12, 2025
1b0ccd0
Merge pull request #3 from a-little-world/feat/improve-prejoin
Simba14 Apr 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/odd-files-talk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@livekit/components-react': minor
'@livekit/components-styles': minor
---

Improves the PreJoin component and allows for custom placeholders for each participant
4 changes: 4 additions & 0 deletions examples/nextjs/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const EXAMPLE_ROUTES = {
title: 'Example usage of @livekit/track-processors for background blur',
href: () => `/processors`,
},
prejoin: {
title: 'Example usage of @livekit/prejoin',
href: () => `/prejoin`,
},
} as const;

const Home: NextPage = () => {
Expand Down
7 changes: 7 additions & 0 deletions packages/react/etc/components-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,13 @@ export interface UseRoomInfoOptions {
room?: Room;
}

// @public
export function useSelectedDevice({
kind: 'videoinput' | 'audioinput';
track?: T;
deviceId?: string;
}): { device: any, deviceError: any };

// @public
export function useSortedParticipants(participants: Array<Participant>): Participant[];

Expand Down
2 changes: 1 addition & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@livekit/components-react",
"version": "2.8.1",
"version": "2.9.0",
"license": "Apache-2.0",
"author": "LiveKit",
"repository": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export interface ParticipantTileProps extends React.HTMLAttributes<HTMLDivElemen
disableSpeakingIndicator?: boolean;

onParticipantClick?: (event: ParticipantClickEvent) => void;
placeholders?: { [index: string]: React.ReactNode };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a big fan of passing the whole map to each participant tile.
A pattern where the component can take a single placeholder as a ReactNode seems preferable to me.
In general the codebase tries to avoid passing of components as props and rather works with slottable components (as children). We're already using those for ParticipantTile as custom tile renderings though, so I'm not totally against an exception here.

}

/**
Expand Down Expand Up @@ -100,6 +101,7 @@ export const ParticipantTile: (
children,
onParticipantClick,
disableSpeakingIndicator,
placeholders,
...htmlProps
}: ParticipantTileProps,
ref,
Expand Down Expand Up @@ -156,7 +158,9 @@ export const ParticipantTile: (
)
)}
<div className="lk-participant-placeholder">
<ParticipantPlaceholder />
{placeholders?.[trackReference.participant?.identity] ?? (
<ParticipantPlaceholder />
)}
</div>
<div className="lk-participant-metadata">
<div className="lk-participant-metadata-item">
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export { usePinnedTracks } from './usePinnedTracks';
export { type UseRemoteParticipantOptions, useRemoteParticipant } from './useRemoteParticipant';
export { type UseRemoteParticipantsOptions, useRemoteParticipants } from './useRemoteParticipants';
export { type UseRoomInfoOptions, useRoomInfo } from './useRoomInfo';
export { useSelectedDevice } from './useSelectedDevice';
export { useSortedParticipants } from './useSortedParticipants';
export { useSpeakingParticipants } from './useSpeakingParticipants';
export { type UseStartAudioProps, useStartAudio } from './useStartAudio';
Expand Down
72 changes: 72 additions & 0 deletions packages/react/src/hooks/useSelectedDevice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { type LocalAudioTrack, type LocalVideoTrack } from 'livekit-client';
import * as React from 'react';
import { useMediaDevices } from './useMediaDevices';

/**
* /**
* The `useSelectedDevice` hook returns the current selected device (audio or video) of the participant.
*
* @example
* ```tsx
* const { selectedDevice } = useSelectedDevice({
* kind: 'videoinput',
* track: track,
* });
*
* <div>
* {selectedDevice?.label}
* </div>
* ```
* @public
*/
export function useSelectedDevice<T extends LocalVideoTrack | LocalAudioTrack>({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

within the livekit-client SDK there's the concept of an active device which is currently used by the user.
That's not as useful for prejoin as of now, but it would mirror the implementation in the client SDK more closely.

kind,
track,
deviceId,
}: {
kind: 'videoinput' | 'audioinput';
track?: T;
deviceId?: string;
}) {
const [deviceError, setDeviceError] = React.useState<Error | null>(null);

const devices = useMediaDevices({ kind });
const [selectedDevice, setSelectedDevice] = React.useState<MediaDeviceInfo | undefined>(
undefined,
);
const [localDeviceId, setLocalDeviceId] = React.useState<string | undefined>(deviceId);

const prevDeviceId = React.useRef(localDeviceId);

const getDeviceId = async () => {
try {
const newDeviceId = await track?.getDeviceId(false);
if (newDeviceId && localDeviceId !== newDeviceId) {
prevDeviceId.current = newDeviceId;
setLocalDeviceId(newDeviceId);
}
} catch (e) {
if (e instanceof Error) {
setDeviceError(e);
}
}
};

React.useEffect(() => {
if (track) getDeviceId();
}, [track]);

React.useEffect(() => {
// in case track doesn't exist, utilize the deviceId passed in
if (!track) setLocalDeviceId(deviceId);
}, [deviceId]);

React.useEffect(() => {
setSelectedDevice(devices?.find((dev) => dev.deviceId === localDeviceId));
}, [localDeviceId, devices]);

return {
selectedDevice,
deviceError,
};
}
Loading