async
lifecycle
browser
utilities
API not supported, make sure to check for compatibility with different browsers when using this API
import { useDisplayMedia } from '@siberiacancode/reactuse';
import { MicIcon, MicOffIcon, MonitorUpIcon, PhoneOffIcon } from 'lucide-react';
import { useState } from 'react';
import { cn } from '@/utils/lib';
const PARTICIPANTS = [
{
id: 1,
name: 'siberiacancode',
initials: 'SC'
},
{
id: 2,
name: 'charmander',
avatar:
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/4.png'
}
];
const Demo = () => {
const displayMedia = useDisplayMedia();
const [muted, setMuted] = useState(false);
if (!displayMedia.supported) {
return (
<p>
API not supported, make sure to check for compatibility with different browsers when using
this{' '}
<a
href='https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia'
rel='noreferrer'
target='_blank'
>
API
</a>
</p>
);
}
return (
<section className='flex w-[500px] flex-col gap-3 p-4'>
<div className='border-border flex flex-col overflow-hidden rounded-xl border bg-neutral-950'>
<div className='relative flex aspect-[24/9] items-center justify-center bg-black'>
<video
autoPlay
muted
playsInline
ref={displayMedia.ref}
className={cn('size-full object-contain p-2', !displayMedia.sharing && 'hidden')}
/>
{!displayMedia.sharing && (
<div className='grid size-full grid-cols-2 gap-2 p-2'>
{PARTICIPANTS.map((participant) => (
<div
key={participant.id}
className='flex flex-col items-center justify-center gap-2 rounded-lg bg-neutral-900 p-3'
>
{participant.initials && (
<div className='flex size-12 items-center justify-center rounded-full bg-gradient-to-br from-neutral-700 to-neutral-900 text-sm font-semibold text-white'>
{participant.initials}
</div>
)}
{participant.avatar && (
<div className='flex size-12 items-center justify-center overflow-hidden rounded-full bg-neutral-800'>
<img
alt={participant.name}
className='size-full object-cover'
src={participant.avatar}
/>
</div>
)}
<span className='text-xs font-medium text-white'>{participant.name}</span>
</div>
))}
</div>
)}
{displayMedia.sharing && (
<div className='absolute top-3 left-3 flex items-center gap-1.5 rounded-full border border-neutral-200 bg-white px-2.5 py-1 text-[10px] font-semibold text-neutral-900 shadow-sm'>
SHARING
</div>
)}
</div>
<div className='flex items-center justify-between gap-2 p-3'>
<button
aria-label={muted ? 'Unmute' : 'Mute'}
data-variant={muted ? 'destructive' : 'secondary'}
type='button'
onClick={() => setMuted((value) => !value)}
>
{muted ? <MicOffIcon className='size-4' /> : <MicIcon className='size-4' />}
</button>
<div className='flex items-center gap-2'>
<button
aria-label={displayMedia.sharing ? 'Stop sharing' : 'Share screen'}
data-variant={displayMedia.sharing ? 'destructive' : 'secondary'}
type='button'
onClick={displayMedia.sharing ? displayMedia.stop : displayMedia.start}
>
<MonitorUpIcon className='size-4' />
</button>
<button data-variant='destructive' type='button'>
<PhoneOffIcon className='size-4' />
End
</button>
</div>
</div>
</div>
</section>
);
};
export default Demo;
This hook uses mediaDevices.getDisplayMedia browser api to provide enhanced functionality. Make sure to check for compatibility with different browsers when using this api
Installation
pnpm add @siberiacancode/reactuseUsage
const { stream, sharing, start, stop } = useDisplayMedia(ref);
// or
const { ref, stream, sharing, start, stop } = useDisplayMedia<HTMLVideoElement>();Type Declarations
import type { HookTarget } from '@/utils/helpers';
import type { StateRef } from '../useRefState/useRefState';
export interface UseDisplayMediaReturn {
/** Whether screen sharing is currently active */
sharing: boolean;
/** The media stream object */
stream: MediaStream | null;
/** Whether the display media API is supported */
supported: boolean;
/** Start screen sharing */
start: () => Promise<void>;
/** Stop screen sharing */
stop: () => void;
}
export interface UseDisplayMediaOptions {
/** Whether to enable audio sharing */
audio?: boolean | MediaTrackConstraints;
/** Whether to start immediately */
immediately?: boolean;
/** Whether to enable video sharing */
video?: boolean | MediaTrackConstraints;
}
export interface UseDisplayMedia {
(target: HookTarget, options?: UseDisplayMediaOptions): UseDisplayMediaReturn;
<Target extends HTMLVideoElement>(
options?: UseDisplayMediaOptions,
target?: never
): { ref: StateRef<Target> } & UseDisplayMediaReturn;
}API
Parameters
| Name | Type | Default | Note |
|---|---|---|---|
| target | HookTarget | - | The target video element to display the media stream |
| options.audio | boolean | MediaTrackConstraints | - | Whether to enable audio sharing |
| options.immediately | boolean | false | Whether to start immediately |
| options.video | boolean | MediaTrackConstraints | - | Whether to enable video sharing |
Returns
UseDisplayMediaReturnParameters
| Name | Type | Default | Note |
|---|---|---|---|
| options.audio | boolean | MediaTrackConstraints | - | Whether to enable audio sharing |
| options.immediately | boolean | false | Whether to start immediately |
| options.video | boolean | MediaTrackConstraints | - | Whether to enable video sharing |
Returns
UseDisplayMediaReturn & { ref: StateRef<HTMLVideoElement> }Contributors
ddebabin
Last updated on