Astro Components
Type to search documentation.

VideoPlayer

Loop short clips while keeping native controls for longer videos.

VideoPlayer enhances the native video element without replacing its full control bar. By default, clips up to 30 seconds loop silently while visible; longer videos stay manual and use the browser’s playback controls.

Short Clip

A bee visiting flowers (MDN's CC0 sample).
mdx
import { VideoPlayer } from '@prosefly/astro-components';
<VideoPlayer
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
width={960}
height={540}
caption="A bee visiting flowers."
/>

Short clips have white circular Lucide play/pause and sound buttons when the browser can determine whether an audio track exists. The buttons appear on hover, keyboard focus, or pause, and stay visible on touch devices. Confirmed silent clips omit the sound button. Unknown audio falls back to native controls; zero decoded audio bytes alone never counts as proof of silence.

Looping pauses when a clip leaves the viewport or the tab becomes hidden, and resumes silently when it becomes visible again. A manual pause stays paused. With reduced motion enabled, playback requires an explicit user action. If autoplay is blocked, the play control remains available.

Native Controls

Set shortVideoSeconds={0} to disable automatic looping, even for short clips:

The same clip with native controls and manual playback.
mdx
<VideoPlayer src="/videos/tutorial.mp4" shortVideoSeconds={0} />

Videos longer than the threshold, streams, and videos with unknown duration also use native controls. They do not autoplay. Videos containing track children retain native controls so captions can still be selected. Without JavaScript, the original video and native controls remain usable.

Automatic Markdown And MDX Figures

Figure conversion is enabled by default. Register the shared integration once in a standalone Astro project:

astro.config.ts
import mdx from '@astrojs/mdx';
import { defineConfig } from 'astro/config';
import proseflyComponents from '@prosefly/astro-components/integration';
export default defineConfig({
integrations: [
proseflyComponents(),
mdx(),
],
});

Authors can then write ordinary HTML in .md and .mdx files without importing a component. The mdx() integration is only needed for .mdx files:

A short clip, authored as an HTML figure.
mdx
<figure id="demo" class="wide">
<video poster="/videos/poster.webp" width="1280" height="720" data-duration="15">
<source src="/videos/demo.webm" type="video/webm" />
<source src="/videos/demo.mp4" type="video/mp4" />
<track kind="captions" src="/videos/demo.en.vtt" srcLang="en" />
</video>
<figcaption>A <strong>short</strong> demo with a <a href="/guide/">guide</a>.</figcaption>
</figure>

rehypeVideoPlayer generates HTML directly, preserving figure and video attributes (including MDX expressions and spreads), multiple sources, tracks, and the original rich figcaption. The integration loads the shared styles and browser runtime. Image figures, audio figures, figures with additional content, multiple videos, or no source are left alone. Standalone video elements are not transformed.

data-duration is an optional hint in seconds; actual browser metadata takes precedence. No videos are fetched during compilation, and no URL-specific audio lookup is used. At runtime the player manages controls, loop, autoplay, and mute state according to its playback policy.

To disable figure conversion, use proseflyComponents({ markdown: { videoPlayer: false } }). For custom thresholds or translations, pass an options object:

typescript
proseflyComponents({
markdown: {
videoPlayer: {
shortVideoSeconds: 15,
labels: { play: '再生', pause: '一時停止', mute: '消音', unmute: '音声をオン' },
},
},
});

Independent Plugin And Themes

Lotus and Dahlia already register the shared integration and enable figure conversion; no additional plugin is needed. For custom thresholds, translations, or disabling conversion, register your configured integration before the theme. The theme will reuse it instead of registering another instance:

astro.config.ts
import { defineConfig } from 'astro/config';
import lotus from '@prosefly/astro-theme-lotus';
import proseflyComponents from '@prosefly/astro-components/integration';
export default defineConfig({
integrations: [
proseflyComponents({
markdown: { videoPlayer: { shortVideoSeconds: 15 } },
}),
lotus(),
],
});

The /markdown entry exports rehypeVideoPlayer and VideoPlayerOptions for processor composition. Use proseflyComponents() to load the player styles and browser runtime.

Sources And Captions

For direct component use, put source children inside VideoPlayer and use the caption prop for plain text:

mdx
<VideoPlayer caption="Tutorial">
<source src="/videos/tutorial.webm" type="video/webm" />
<source src="/videos/tutorial.mp4" type="video/mp4" />
</VideoPlayer>

For tracks, custom fallback content, figure attributes, or rich captions, write an ordinary <figure> with a <video> and <figcaption> as shown above. The plugin preserves the complete authored markup; there is no need to wrap that figure in VideoPlayer yourself.

Props

PropTypeNotes
src, poster, width, height, preload, etc.Native video attributesApplied to the generated video; preload defaults to metadata.
captionstringPlain-text caption and default video label.
durationnumber | stringPositive duration hint in seconds; browser metadata takes precedence.
shortVideoSecondsnumberDefaults to 30; 0 disables looping.
labelsPartial<VideoPlayerLabels>Override play, pause, mute, unmute, and download labels.
classstringAdditional class on the outer figure.

Set a localized caption or aria-label as well as localized action labels.

Styling

Media borders, radius, caption color, typography, and focus rings use shared theme tokens. Overlay buttons intentionally stay white with dark icons in both themes. Override these component tokens in your layout:

TokenDefault
--pf-video-player-radiusvar(--pf-radius-md)
--pf-video-player-border-colorvar(--pf-border-subtle)
--pf-video-player-caption-colorvar(--pf-text-muted)
--pf-video-player-button-size2.5rem
--pf-video-player-button-background#fff
--pf-video-player-button-color#182230
--pf-video-player-button-hover-background#f4f6f8

Last updated Oct 19, 2026