# Add keyboard shortcuts and gestures

Add keyboard shortcuts, tap and double-tap gestures, and on-screen feedback for input actions.

Add keyboard shortcuts and pointer gestures to the player declaratively, with on-screen feedback and screen-reader announcements.

> **Note**
>
> Using a pre-built [skin](./skins.md)? It already includes the controls shown here. You may still need the media or player setup in this guide. The component examples are for building your own player UI from individual [components](./ui-components.md).

## Recommended approach

Declare shortcuts with `<media-hotkey>` and gestures with `<media-gesture>` inside the player container. Each maps an input to a player action; the prebuilt skins ship a full set.

```html
<video-player>
  <media-container>
    <video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" muted playsinline></video>

    <media-hotkey keys="Space" action="togglePaused"></media-hotkey>
    <media-hotkey keys="m" action="toggleMuted"></media-hotkey>
    <media-hotkey keys="f" action="toggleFullscreen"></media-hotkey>
    <media-hotkey keys="ArrowRight" action="seekStep" value="5"></media-hotkey>
    <media-hotkey keys="ArrowLeft" action="seekStep" value="-5"></media-hotkey>

    <media-gesture type="tap" action="togglePaused" pointer="mouse" region="center"></media-gesture>
    <media-gesture type="tap" action="toggleControls" pointer="touch"></media-gesture>
    <media-gesture type="doubletap" action="seekStep" value="-10" region="left"></media-gesture>
    <media-gesture type="doubletap" action="seekStep" value="10" region="right"></media-gesture>
  </media-container>
</video-player>
<script type="module">
  import '@videojs/html/video/player';
  import '@videojs/html/ui/hotkey';
  import { GestureElement } from '@videojs/html';

  // Gesture has no standalone define entry; register the element directly.
  customElements.define(GestureElement.tagName, GestureElement);
</script>
```

## How it works

- `<media-hotkey>` binds keys to player actions: `togglePaused`, `toggleMuted`, `toggleFullscreen`, `toggleSubtitles`, `togglePictureInPicture`, `seekStep`, `volumeStep`, `seekToPercent`, `speedUp`, and `speedDown`. Step actions have shared defaults and accept a custom `value`. The actions call the same feature state actions your components use.
- `<media-gesture>` binds pointer input — `tap` and `doubletap` — to the same actions, minus `seekToPercent` and plus `toggleControls`. Bindings are scoped by `pointer` (`mouse` or `touch`) and `region` (`left`, `center`, `right`) — regions split the container’s width, not the rendered video’s — so double-tap-left rewinds while double-tap-right skips forward.
- `<media-status-announcer>` announces action results to screen readers, and the input indicator components render transient on-screen feedback (volume level, captions on/off) the way native players do. The prebuilt skins wire these up.

The prebuilt skins bind the conventional set: Space and `k` toggle playback, `m` mutes, `f` fullscreens, `c` toggles captions, `i` toggles picture-in-picture, arrows seek and change volume, `j`/`l` seek in larger steps, `0`–`9` jump to a percentage, and `<`/`>` change speed.

## Availability and constraints

- Hotkeys listen on the player container by default, so multiple players on a page don’t fight over keys. Pass `target="document"` on a hotkey to make it page-wide — appropriate only when one player owns the page.
- Typing is protected: while focus is in an editable field, single-key bindings are skipped automatically. Modifier combinations (for example `Ctrl+Shift+f`) still fire.
- Repeatable actions (seeking, volume) fire on held keys; toggle actions don’t repeat.
- Touch tap-to-toggle-controls and tap-to-pause are mutually exclusive per pointer type — the skins pause on mouse tap and toggle controls on touch tap, matching platform conventions.
- Gestures and the built-in touch handling coordinate through one recognizer; register gestures declaratively instead of adding raw pointer listeners next to them.

## Common variations

### Customize a shortcut

Render your own `<media-hotkey>` set instead of a skin’s: every binding is just an element, so add, remove, or re-key them freely.

### Seek step size

`seekStep` takes its step from `value` — match your content: 5 seconds for short clips, 15 or 30 for long-form.

## Troubleshooting

### Shortcuts don’t fire

Focus is outside the player container. Click the player first, or move focus into it; hotkeys are player-scoped by design.

### Double-tap seek also toggles pause

Regions overlap: keep single-tap `togglePaused` on `center` only, and double-tap seek on `left` and `right`, as in the skin defaults.

### No visual feedback on keyboard actions

The input indicator components render that feedback; use a prebuilt skin or add `<media-status-announcer>` and the indicator components from the skin source to a custom UI.

## Related pages

### Components

- [media-controls](../reference/components/controls.md): Container component for composing and auto-hiding video player controls on user interaction
- [media-tooltip](../reference/components/tooltip.md): A tooltip component for displaying contextual labels on hover and focus

### API

- [Playback](../reference/api/feature-playback.md): Play/pause state and actions for the player store
- [Volume](../reference/api/feature-volume.md): Volume level and mute state for the player store
- [Fullscreen](../reference/api/feature-fullscreen.md): Fullscreen state and actions for the player store

### Guides

- [Automatically show and hide controls](./controls.md): Show controls while the user is active, hide them during playback, and toggle them by touch.
- [Accessibility](./accessibility.md): How Video.js approaches accessibility, and what you should consider if you're deeply customizing your player