Device Selector

Choose which microphone, camera, or speaker a call uses. It composes Dropdown Menu — a radio group of devices with the active one checked — and docks against a Media Toggle as a split control.

Mic: MacBook Pro Microphone

tsx
"use client"

import * as React from "react"

import {
  DeviceSelector,
  DeviceSelectorContent,
  DeviceSelectorTrigger,
} from "@/registry/seam/ui/device-selector"

const DEVICES = [
  { deviceId: "default", label: "MacBook Pro Microphone" },
  { deviceId: "airpods", label: "AirPods Pro" },
  { deviceId: "yeti", label: "Blue Yeti" },
]

export default function DeviceSelectorDemo() {
  const [value, setValue] = React.useState("default")

  return (
    <div className="flex flex-col items-center gap-3">
      <DeviceSelector devices={DEVICES} value={value} onValueChange={setValue}>
        <DeviceSelectorTrigger />
        <DeviceSelectorContent />
      </DeviceSelector>
      <p className="text-muted-foreground text-sm">
        Mic: {DEVICES.find((d) => d.deviceId === value)?.label}
      </p>
    </div>
  )
}
bunx --bun @seamui/cli@latest add device-selector

Notes

  • Pass a devices array to control the list, or omit it and the owned useMediaDevices hook enumerates real hardware and re-lists on devicechange. The shape maps onto the LiveKit useMediaDeviceSelect hook with no runtime dependency.
  • Device labels are empty until mic/camera permission is granted, so the hook falls back to “Microphone 2”-style names until then.
  • No new motion — the trigger presses via the dogfooded Button and the menu rises at overlay depth, all inherited from Dropdown Menu.
  • Full menu semantics from Base UI: roving focus, typeahead, and a checked radio item for the active device. The trigger carries an aria-label.

Press feedback, reduced motion, and haptics follow the global policy — see Motion and Haptics.