Video player
Un reproductor de vídeo con barra de búsqueda arrastrable, controles que se ocultan solos, atajos de teclado, velocidad, imagen en imagen y pantalla completa.
pnpm dlx shadcn@latest add https://hextaui.com/r/video-player.jsonAñade el componente, los tokens del tema de HextaUI y los componentes de HextaUI de los que depende.
Añade los tokens del tema a tu CSS global, si aún no lo has hecho.
Instala las dependencias.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopia y pega el siguiente código en tu proyecto.
components/ui/video-player.tsx components/ui/button.tsx components/ui/dropdown-menu.tsx components/ui/kbd.tsx components/ui/spinner.tsx components/ui/tooltip.tsx Actualiza las rutas de importación para que coincidan con la configuración de tu proyecto.
Barra
variant="bar" coloca los controles bajo la imagen, sobre la superficie de la página. Nunca ocultan ni cubren el vídeo.
Mínimo
Usa solo las partes que necesites. tooltips={false} desactiva las pistas al pasar el cursor, y type="remaining" cuenta hacia atrás en lugar de hacia delante.
Saltos de búsqueda y velocidades
offset define cuánto salta cada botón de búsqueda, y rates define las velocidades del menú.
Atajos en toda la página
Los atajos funcionan mientras el foco está dentro del reproductor. globalShortcuts también escucha en la página, pero nunca mientras escribes en un campo, usas un botón o tienes un menú o dialog abierto. Úsalo para un solo reproductor por página.
Subtítulos
Añade un <track> y VideoPlayerCaptionsButton. Los subtítulos los dibuja el reproductor, así que suben mientras se muestran los controles en lugar de quedar ocultos tras ellos. Una pista de otro origen necesita crossOrigin en el vídeo.
Controles personalizados
useVideoPlayer lee el estado y las acciones desde cualquier componente dentro del reproductor. Selecciona solo lo que uses, para que el componente se vuelva a renderizar únicamente cuando ese valor cambie.
Error
Cuando la fuente falla, el reproductor muestra errorMessage, lo anuncia y deshabilita los controles que no pueden funcionar.
De derecha a izquierda
Las etiquetas siguen el idioma de la página. La línea de tiempo y los controles de transporte permanecen de izquierda a derecha, como hacen los reproductores multimedia de las plataformas.
Funcionan mientras el foco está en cualquier lugar dentro del reproductor, o en la página con globalShortcuts. Se omiten mientras se mantiene una tecla modificadora o el foco está en un campo de texto.
| Key | Acción |
|---|---|
| SpaceK | Reproduce o pausa. |
| J | Retrocede 10 segundos. |
| L | Avanza 10 segundos. |
| ←→ | Retrocede o avanza 5 segundos. En la barra de búsqueda, Shift salta 10. |
| ↑↓ | Sube o baja el volumen un 5%. |
| M | Silencia o activa el sonido. |
| C | Activa o desactiva los subtítulos, cuando el vídeo los tiene. |
| F | Entra o sale de la pantalla completa. |
| I | Abre o cierra la imagen en imagen, donde sea compatible. |
| Shift+.Shift+, | Acelera o ralentiza la reproducción. |
| 0–9 | Salta del 0% al 90% del vídeo. |
| HomeEnd | Salta al inicio o al final. |
- El reproductor es una región con etiqueta. Cada botón tiene un nombre que sigue su estado (Play, Pause, Replay) y un tooltip con su atajo.
- La barra de búsqueda y el volumen son sliders. La barra de búsqueda lee su valor como “1 minuto 5 segundos de 3 minutos”.
- Las acciones de los atajos y de los clics en el vídeo se anuncian de forma educada, por ejemplo “Paused” o “Volume 40%”. Los errores de carga se anuncian como una alerta.
- En la variante overlay, los controles se desvanecen tras 2.5 segundos de reproducción sin movimiento del puntero. Permanecen visibles en pausa, mientras pasas el cursor o los usas con el teclado, y mientras hay un menú abierto.
- En pantallas táctiles, un toque muestra u oculta los controles y un doble toque en el tercio izquierdo o derecho retrocede o avanza 10 segundos. Sigue tocando para añadir 10 segundos cada vez.
- El botón de subtítulos es un toggle con
aria-pressed. Elige la última pista que usaste, luego una en el idioma del navegador y después la primera. - Con movimiento reducido, los controles y el feedback se desvanecen sin moverse ni escalar.
La barra de búsqueda y el volumen están construidos sobre el slider de Base UI, y los botones sobre Button, Tooltip y Dropdown menu de HextaUI.
| Prop | Tipo | Predeterminado |
|---|---|---|
variantoverlay coloca controles que se ocultan solos sobre el vídeo. bar los coloca debajo. | "overlay" | "bar" | "overlay" |
shortcutsAtajos de teclado mientras el foco está en el reproductor. | boolean | true |
globalShortcutsEscucha también los atajos en toda la página. | boolean | false |
errorMessage | ReactNode | "This video can’t be played." |
| Atributo | Descripción |
|---|---|
data-slot="video-player" | Apunta a la raíz en CSS. |
data-variant | La variante actual. |
data-controls | "visible" o "hidden". El cursor se oculta junto con los controles. |
data-fullscreen | Presente mientras el reproductor está a pantalla completa. |
aria-busy | Se define mientras la reproducción espera datos. |
El elemento <video>. Acepta todos los atributos de video, y hijos <source> o <track>. Un clic reproduce o pausa, un doble clic alterna la pantalla completa, un toque muestra u oculta los controles y un doble toque a cualquiera de los lados busca.
| Prop | Tipo | Predeterminado |
|---|---|---|
autoPlayInicia la reproducción al montar, salvo con movimiento reducido. | boolean | false |
playsInline | boolean | true |
preload | "none" | "metadata" | "auto" | "metadata" |
doubleTapSeekSegundos que salta un doble toque a cualquiera de los lados en pantallas táctiles. false lo desactiva. | number | false | 10 |
renderSustituye por otro elemento multimedia, como un elemento de vídeo HLS. | ReactElement | (props, state) => ReactElement | <video> |
| Atributo | Descripción |
|---|---|
data-slot="video-player-content" | Apunta al vídeo en CSS. |
| Prop | Tipo | Predeterminado |
|---|---|---|
tooltipsMuestra la etiqueta y el atajo de cada control al pasar el cursor. | boolean | true |
| Atributo | Descripción |
|---|---|
data-slot="video-player-controls" | Apunta a la barra de controles en CSS. |
data-hidden | Presente mientras los controles overlay están ocultos. |
Siempre ocupa su propia fila sobre los botones. Al pasar el cursor se muestra el tiempo bajo el puntero, y la pista más clara muestra lo que se ha cargado.
| Prop | Tipo | Predeterminado |
|---|---|---|
label | string | "Seek" |
onValueChange | (value: number, details) => void | – |
onValueCommitted | (value: number, details) => void | – |
disabled | boolean | false |
| Atributo | Descripción |
|---|---|
data-slot="video-player-seek-bar" | Apunta a la barra de búsqueda en CSS. |
data-dragging | Presente mientras arrastras la barra. |
data-previewing | Presente en el control mientras se muestra el tiempo del hover. |
--video-player-buffered | La parte cargada del vídeo, de 0 a 1. |
--video-player-hover | La posición del puntero a lo largo de la barra, de 0 a 1. |
| Prop | Tipo | Predeterminado |
|---|---|---|
playLabel | string | "Play" |
pauseLabel | string | "Pause" |
replayLabel | string | "Replay" |
...propsTodas las props de Button, incluidas variant y size. | ButtonProps | – |
| Atributo | Descripción |
|---|---|
data-slot="video-player-play-button" | Apunta al botón en CSS. |
data-state | "paused", "playing" o "ended". |
| Prop | Tipo | Predeterminado |
|---|---|---|
offsetSegundos que se salta. Los valores negativos retroceden. | number | 10 |
label | string | "Forward 10 seconds" |
...propsTodas las props de Button, incluidas variant y size. | ButtonProps | – |
| Atributo | Descripción |
|---|---|
data-slot="video-player-seek-button" | Apunta al botón en CSS. |
data-direction | "backward" o "forward". |
Un botón de silencio con un slider que se abre al pasar el cursor o al enfocar. En pantallas táctiles solo se muestra el botón de silencio, ya que los teléfonos controlan el volumen con sus propios botones.
| Prop | Tipo | Predeterminado |
|---|---|---|
label | string | "Volume" |
muteLabel | string | "Mute" |
unmuteLabel | string | "Unmute" |
| Atributo | Descripción |
|---|---|
data-slot="video-player-volume" | Apunta al grupo en CSS. |
data-slot="video-player-mute-button" | El botón de silencio. También se exporta como VideoPlayerMuteButton. |
data-state | En el botón de silencio: "muted", "low" o "high". |
| Prop | Tipo | Predeterminado |
|---|---|---|
type | "both" | "elapsed" | "remaining" | "duration" | "both" |
| Atributo | Descripción |
|---|---|
data-slot="video-player-time" | Apunta al tiempo en CSS. |
data-type | El tipo actual. |
| Prop | Tipo | Predeterminado |
|---|---|---|
rates | number[] | [0.5, 0.75, 1, 1.25, 1.5, 2] |
label | string | "Playback speed" |
normalLabel | string | "Normal" |
| Atributo | Descripción |
|---|---|
data-slot="video-player-playback-rate" | Apunta al trigger del menú en CSS. |
Pone a pantalla completa todo el reproductor, o el propio vídeo en iPhone. No renderiza nada donde la pantalla completa no está disponible.
| Prop | Tipo | Predeterminado |
|---|---|---|
enterLabel | string | "Full screen" |
exitLabel | string | "Exit full screen" |
| Atributo | Descripción |
|---|---|
data-slot="video-player-fullscreen-button" | Apunta al botón en CSS. |
data-state | "on" u "off". |
No renderiza nada hasta que el vídeo tiene una pista de subtítulos.
| Prop | Tipo | Predeterminado |
|---|---|---|
label | string | "Captions" |
| Atributo | Descripción |
|---|---|
data-slot="video-player-captions-button" | Apunta al botón en CSS. |
data-state | "on" u "off". |
data-slot="video-player-captions" | El texto del subtítulo sobre el vídeo. data-lifted está presente mientras queda sobre los controles. |
No renderiza nada en navegadores sin imagen en imagen.
| Prop | Tipo | Predeterminado |
|---|---|---|
enterLabel | string | "Picture in picture" |
exitLabel | string | "Exit picture in picture" |
| Atributo | Descripción |
|---|---|
data-slot="video-player-pip-button" | Apunta al botón en CSS. |
data-state | "on" u "off". |
Llena el espacio libre de la fila de controles, empujando al final los controles que vienen después.
Devuelve el estado y las acciones del reproductor. Pasa un selector que devuelva un único valor.
| Prop | Tipo | Predeterminado |
|---|---|---|
state | paused, ended, started, waiting, scrubbing, currentTime, duration, buffered, volume, muted, playbackRate, fullscreen, pictureInPicture, error, hasCaptions, captions, caption | – |
actions | play, pause, togglePaused, seek, seekBy, setVolume, toggleMuted, setPlaybackRate, toggleFullscreen, togglePictureInPicture, toggleCaptions | – |
- ButtonBotones en todas las variantes y tamaños, con un flujo integrado de carga, éxito y error que omite el spinner en las peticiones rápidas.
- Dropdown menuUn menú de acciones y opciones tras un botón, con grupos, submenús, elementos de casilla y de radio, y atajos.
- KbdTeclas para atajos que muestran los símbolos correctos en cada plataforma, se leen en voz alta correctamente y se hunden como las teclas reales.
- MotionLas curvas de easing, duraciones y la comprobación de movimiento reducido con las que se anima cada componente, además de hooks para transformaciones de tamaño y resaltados deslizantes.
- SpinnerUn indicador de carga con marcas al estilo Apple o un anillo que respira, que puede esperar antes de mostrarse y permanecer el tiempo suficiente para no parpadear.
- TooltipUna pista breve al pasar el cursor o enfocar con el teclado que se abre tras un breve reposo, cambia al instante entre vecinos y muestra atajos.