@cloudflare/stream-react

Official React component for embedding and controlling Cloudflare Stream video players.

SDK
npm
v1.9.3
198stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
47/100Fair
Development Activity12
Maintenance32
Community64
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture68
Code Quality58
Innovation70
Learning Curve82

@cloudflare/stream-react is Cloudflare’s official React wrapper around the Cloudflare Stream player, giving you a single <Stream> component that accepts a video UID or signed token and renders a fully controllable, responsive video embed. It mirrors the familiar HTML5 <video> prop surface (autoplay, controls, muted, poster, loop, preload, volume, playbackRate) while adding Cloudflare Stream-specific extensions like VAST ad URLs, letterbox/primary color theming, default text tracks, and customer-code subdomains.

Under the hood the component lazily injects Cloudflare’s Stream SDK script, binds every player event (play, pause, resize, ad start/end, etc.) to React callback props, and exposes the underlying player API via an optional ref for imperative control — so teams building on Cloudflare Stream get idiomatic React ergonomics without hand-rolling iframe messaging or script loading themselves.

What You Get

  • A <Stream> component that renders a responsive, aspect-ratio-aware Cloudflare Stream player from a video UID or signed token
  • Full TypeScript types for every prop, event callback, and the underlying StreamPlayerApi
  • Support for every native <video>-style prop (autoplay, controls, muted, poster, loop, preload, currentTime, volume, playbackRate) plus Stream-specific extensions (adUrl, primaryColor, letterboxColor, defaultTextTrack, customerCode)
  • Imperative access to the player instance via a streamRef for advanced control beyond props
  • Automatic responsive sizing based on the video’s actual aspect ratio, with an escape hatch to opt out

Common Use Cases

  • Embedding uploaded or live Cloudflare Stream videos inside a React or Next.js marketing site or product UI
  • Building a custom video player experience with VAST pre-roll ads via the adUrl prop
  • Serving signed/tokenized private videos to authenticated users without exposing raw stream URLs
  • Theming the player’s controls and letterboxing to match brand colors via primaryColor/letterboxColor

Under The Hood

Architecture: The library exposes a single Stream React functional component that wraps Cloudflare’s third-party embed SDK. Stream (in src/Stream.tsx) first calls useStreamSDK() (src/useStreamSDK.ts), which lazily injects a <script> tag pointing at embed.cloudflarestream.com/embed/sdk.latest.js and exposes the resulting window.Stream factory via a state-backed hook so renders are gated until the SDK loads; until then, Stream renders null. Once available, StreamEmbed computes the iframe src via useIframeSrc — either passing through a caller-supplied signed/absolute URL (validated by validSrcUrl.ts against videodelivery.net/cloudflarestream.com hostnames) or building a query-string URL from playback options, memoized once on mount by design to avoid re-navigating the iframe on prop changes. On mount, Stream(iframeRef.current) instantiates the real player API, stored on ref.current (internal or caller-supplied streamRef). Two internal hooks, useProperty and useEvent, imperatively bridge React prop changes onto that player-API object: useProperty syncs primitives (muted, controls, currentTime, volume, etc.) via effects, and useEvent subscribes/unsubscribes DOM-style listeners for roughly 25 optional callback props, including Stream-specific stream-adstart/stream-adend events. A resize listener also updates local videoDimensions state, feeding a Container wrapper that computes CSS padding-based responsive aspect ratio.

Tech Stack: TypeScript (97% of bytes) built with tsdx, producing CJS (dist/index.js) and ESM (dist/stream-react.esm.js) bundles plus generated .d.ts typings. React is a peer dependency (>=16) — the library ships no React version itself, though devDependencies pin react@^16.13.1 for local dev/Storybook, dated relative to current React but non-restrictive for consumers since it’s peer-only. There are no runtime dependencies beyond React; the actual Cloudflare Stream player is never bundled — it’s fetched at runtime from Cloudflare’s CDN via a dynamically injected script tag, making the npm package itself a thin ~6-file wrapper. Storybook 5 plus a separate CRA-style example/ app are used for manual/visual verification. Package manager is Yarn, with Husky running clean + prettier pre-commit.

Code Quality: There are no automated tests in the repository — the test script is tsdx test --passWithNoTests and no *.test.*/*.spec.* files exist in src/; correctness relies solely on manual Storybook stories and the example app. The ~6 source files are otherwise clean: consistent TypeScript typing throughout (StreamProps, StreamPlayerApi, VideoDimensions), every public prop carries a JSDoc comment explaining exact browser/DOM semantics, and naming is idiomatic React (useProperty, useEvent, useIframeSrc). Error handling is minimal but appropriate: validSrcUrl.ts wraps new URL() in try/catch to fail closed, and safelyAccessStreamSDK guards against SSR before touching window.Stream. The intentionally incomplete dependency array in useIframeSrc’s useMemo is documented inline as a deliberate choice, not an oversight — but zero test coverage remains a real gap for a stateful component.

API Design: The public surface is a single <Stream src=... /> component plus a useStreamSDK hook and TypeScript types, re-exported from index.tsx — about as minimal as possible. Getting started needs one import and one JSX tag with a video UID or signed token; every prop a developer already knows from HTML5 <video> (controls, autoplay, muted, poster, loop, preload, currentTime, volume, playbackRate) is supported with matching names, sharply lowering the learning curve. Imperative access via streamRef covers consumers needing direct control beyond props, and Cloudflare-specific extensions (adUrl, letterboxColor, primaryColor, defaultTextTrack, customerCode) are clearly documented per-prop in the README. The rough edge: the player SDK is fetched from a Cloudflare CDN script at runtime, an implicit dependency not obvious from imports alone, and Stream silently renders null if that script fails to load (e.g. blocked by an ad blocker or strict CSP) — a behavior the README doesn’t call out.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search