import { useEffect } from "react";
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const isMacOS = () => !!navigator.userAgent.match(/(Mac\s?OS)/g)?.length;
export const useKeyboardShortcuts = () => {
const { useCameraState, useMicrophoneState } = useCallStateHooks();
const { microphone } = useMicrophoneState();
const { camera } = useCameraState();
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (
e.key !== "d" ||
(!e.metaKey && isMacOS()) ||
(!e.ctrlKey && !isMacOS())
)
return;
e.preventDefault();
microphone.toggle().catch(console.error);
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [microphone]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (
e.key !== "e" ||
(!e.metaKey && isMacOS()) ||
(!e.ctrlKey && !isMacOS())
)
return;
e.preventDefault();
camera.toggle().catch(console.error);
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [camera]);
};Keyboard shortcuts
Add keyboard shortcuts for common call actions like mute/unmute.
Best Practices
- Use
hotkeys-jslibrary for cleaner shortcut registration. - Detect macOS with
navigator.userAgentto use⌘instead ofCtrl. - Place shortcut hook in same component as CallControls for logical grouping.
- Always clean up event listeners in useEffect return function.
- Common shortcuts:
Ctrl/⌘ + D(toggle audio),Ctrl/⌘ + E(toggle video).
Define custom useKeyboardShortcuts hook
As this hook will introduce keyboard shortcuts related to CallControls (mute/unmute audio) we should ideally invoke it in the same component to group logical parts together for easier debugging later. The same goes for your other keyboard shortcuts you might introduce to your application. Learn how to adjust your CallControls component in the Replacing Call Controls guide.
Refactor with the use of hotkeys-js
The previous implementation looks a bit messy and hard to read so we'll refactor this hook with the help of the hotkeys-js which makes shortcut registering and handling much easier.
import { useEffect } from "react";
import hotkeys from "hotkeys-js";
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const isMacOS = () => !!navigator.userAgent.match(/(Mac\s?OS)/g)?.length;
export const useKeyboardShortcuts = () => {
const { useCameraState, useMicrophoneState } = useCallStateHooks();
const { microphone } = useMicrophoneState();
const { camera } = useCameraState();
useEffect(() => {
const shortcuts = "cmd+d,ctrl+d";
hotkeys(shortcuts, (e, ke) => {
if (isMacOS() && ke.shortcut !== "cmd+d") return;
e.preventDefault();
microphone.toggle().catch(console.error);
});
return () => hotkeys.unbind(shortcuts);
}, [microphone]);
useEffect(() => {
const shortcuts = "cmd+e,ctrl+e";
hotkeys(shortcuts, (e, ke) => {
if (isMacOS() && ke.shortcut !== "cmd+e") return;
e.preventDefault();
camera.toggle().catch(console.error);
});
return () => hotkeys.unbind(shortcuts);
}, [camera]);
};