reflex-react-player
A Reflex custom component that wraps react-player v3 — a versatile media player that plays files, audio, HLS, DASH, YouTube, Vimeo, Wistia, Mux, Spotify, Twitch and TikTok by inspecting the URL.
It gives you a Pythonic, typed API for props and events, plus imperative control (play, pause, seek, fullscreen) from your Python event handlers — and ships with a full demo app reproducing every example from the official react-player demo.
Built with Spec-Driven Design. The full specs (PRD, architecture, component contract, plan, tasks) live in
docs/sdd/.
Features
- 🎬 One component for many providers — file/audio, HLS (
.m3u8), DASH (.mpd), YouTube, Vimeo, Wistia, Mux, Spotify, Twitch, TikTok. - 🐍 Pythonic props (
snake_case→camelCase), typed withrx.Var. - 🔔 Events that deliver serializable media-state dicts (played, loaded, duration, ...) — not raw browser events.
- 🎛️ Imperative control helpers:
play,pause,seek_to,seek_relative,seek_fraction,set_volume,set_playback_rate,request_fullscreen,get_current_time,get_duration. - 🌗 Light (thumbnail-first) mode, picture-in-picture, per-player
config. - 🧪 Smoke tests + a complete demo app.
Installation
pip install reflex-react-player
Or install from source (editable) for development:
git clone https://github.com/ecrespo/reflex-react-player.git
cd reflex-react-player
pip install -e .
Requires Reflex ≥ 0.10.0 and Python ≥ 3.11 (tested on 3.11–3.14). The npm dependency
(react-player@3.4.0) is added to your Reflex project automatically on build.
Quickstart
import reflex as rx
from reflex_react_player import react_player
class State(rx.State):
playing: bool = False
@rx.event
def toggle(self):
self.playing = not self.playing
def index() -> rx.Component:
return rx.vstack(
react_player(
id="player",
src="https://www.youtube.com/watch?v=oUFJJNQGwhk",
playing=State.playing,
controls=True,
width="640px",
height="360px",
style={"aspectRatio": "16 / 9"},
),
rx.button("Play / Pause", on_click=State.toggle),
)
app = rx.App()
app.add_page(index)
Tracking progress (events)
Most events deliver a media-state dict with played (0..1), loaded (0..1),
played_seconds, duration, etc.
class State(rx.State):
played: float = 0.0
loaded: float = 0.0
duration: float = 0.0
@rx.event
def on_time_update(self, s: dict):
self.played = s["played"]
@rx.event
def on_progress(self, s: dict):
self.loaded = s["loaded"]
@rx.event
def on_duration(self, seconds: float):
self.duration = seconds
react_player(
id="player",
src="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8", # HLS
controls=True,
on_time_update=State.on_time_update,
on_progress=State.on_progress,
on_duration_change=State.on_duration,
)
Imperative control
Give the player an id, then drive it from Python:
from reflex_react_player import controls
rx.button("Play", on_click=controls.play("player"))
rx.button("Pause", on_click=controls.pause("player"))
rx.button("-10s", on_click=controls.seek_relative("player", -10))
rx.button("+10s", on_click=controls.seek_relative("player", 10))
rx.button("Fullscreen", on_click=controls.request_fullscreen("player"))
API reference
See the full contract in
docs/sdd/03-component-spec.md. Summary:
Props: src, playing, loop, controls, light, volume, muted,
playback_rate, pip, plays_inline, width, height, style, auto_play,
preload, cross_origin, poster, disable_remote_playback,
disable_picture_in_picture, preview_tab_index, preview_aria_label,
o_embed_url, config.
Events: on_ready, on_start, on_play, on_pause, on_ended,
on_error, on_duration_change, on_progress, on_time_update, on_seeking,
on_seeked, on_waiting, on_playing, on_rate_change, on_volume_change,
on_enter_picture_in_picture, on_leave_picture_in_picture, on_click_preview.
Control helpers: controls.play/pause/seek_to/seek_relative/seek_fraction/ set_volume/set_playback_rate/request_fullscreen/get_current_time/get_duration.
Per-player config
react_player(
src="https://www.youtube.com/watch?v=oUFJJNQGwhk",
config={"youtube": {"color": "white"}, "vimeo": {"color": "ffffff"}},
)
Keys: html, hls, dash, mux, youtube, vimeo, wistia, spotify,
twitch, tiktok.
Run the demo
cd reflex_react_player_demo
pip install -e .. # install the component
pip install -r requirements.txt
reflex run # needs Node.js (CI uses Node 24)
The demo's Radix theme is configured in rxconfig.py through
rx.plugins.RadixThemesPlugin (Reflex ≥ 0.9 deprecates rx.App(theme=...)).
Open http://localhost:3000 — the demo includes a provider switcher, transport controls, speed buttons, a seek slider, a volume slider, controls/muted/loop/ light toggles, the Played + Loaded progress bars, and a live time readout.
How it works
- Client-only: react-player touches
window, so the component subclassesNoSSRComponent(rendered viadynamic(import(...), {ssr:false})). - Default export:
is_default = True(omitting it is the classic "Invalid Element Type" failure). - Events: native
SyntheticEvents are reduced, on the client, to a small serializable dict read from the underlyingHTMLMediaElement. - Imperative API: react-player v3 exposes its instance API through the
forwarded ref (
ref.currentTime,ref.play()...); helpers drive it viarx.call_scriptagainstrefs['ref_<id>'].current.
Details and decisions: docs/sdd/02-architecture.md.
Limitations
react-player v3 has not yet ported every v2 provider. DailyMotion, SoundCloud, Streamable, Facebook, Mixcloud and Kaltura are not supported here; stay on react-player v2 if you need them.
Development
pip install -e ".[dev]"
pytest -q
python -m build # build sdist + wheel (or: uv build)
Type stubs
react_player.pyi is committed and shipped in the wheel. Reflex 0.10 removed the
reflex component CLI (reflex component build no longer exists), so regenerate
the stubs after changing props or events with Reflex's stub generator:
pip install ruff
python -m reflex_base.utils.pyi_generator custom_components/reflex_react_player
Releasing
Releases are published to PyPI by .github/workflows/publish.yml using
Trusted Publishing (no API token):
-
Bump
versioninpyproject.tomland__version__incustom_components/reflex_react_player/__init__.py; add aCHANGELOG.mdentry. -
Open a PR to
main; CI (tests on Python 3.11–3.14, build, demo compile on Node 24) must pass. Merge it. -
Tag the merge commit on
mainand push the tag:git checkout main && git pull git tag v0.2.0 git push origin v0.2.0
The workflow checks that the tag matches the package version, runs the tests,
builds, and uploads to PyPI from the pypi environment.
License
Apache-2.0 © 2026 Ernesto Crespo.
react-player is a separate project by Pete Cook / Mux, under its own license.
Metadata
Release files for reflex-react-player 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| reflex_react_player-0.2.0.tar.gz | 18.5 kB | With provenance | Details for reflex_react_player-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| reflex_react_player-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for reflex_react_player-0.2.0-py3-none-any.whl |
Total release size: 32.6 kB
Release files / reflex_react_player-0.2.0.tar.gz
| Download URL | reflex_react_player-0.2.0.tar.gz |
|---|---|
| Size | 18.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7c1108b34eedd8318c4818320dec89ba6ffcb3fc8f7707d00deaf83865fae8fb
|
|
BLAKE2b-256 checksum How to use checksums |
085f92bbf5e5f85c531fa44883839dffc99ffc14815bd5280dab578a268f1d95
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logRelease files / reflex_react_player-0.2.0-py3-none-any.whl
| Download URL | reflex_react_player-0.2.0-py3-none-any.whl |
|---|---|
| Size | 14.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bdafb54eaa461b946d6184b1bb86b0eb688c1c977d4b22a0ed7c2fa30b67d7f1
|
|
BLAKE2b-256 checksum How to use checksums |
9b75bbab97f47ed71ec319b2584f5955b225e89bc3f3984a366124116c608d00
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logSecurity for reflex-react-player 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for reflex_react_player-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.