Skip to main content

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 with rx.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 subclasses NoSSRComponent (rendered via dynamic(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 underlying HTMLMediaElement.
  • Imperative API: react-player v3 exposes its instance API through the forwarded ref (ref.currentTime, ref.play() ...); helpers drive it via rx.call_script against refs['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):

  1. Bump version in pyproject.toml and __version__ in custom_components/reflex_react_player/__init__.py; add a CHANGELOG.md entry.

  2. Open a PR to main; CI (tests on Python 3.11–3.14, build, demo compile on Node 24) must pass. Merge it.

  3. Tag the merge commit on main and 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)

Source distribution for reflex-react-player 0.2.0
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)

Table of built distributions (wheels) for reflex-react-player 0.2.0
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page