npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@moviie/player-expo

v0.29.0

Published

Moviie video player for Expo (expo-video, telemetry, native features).

Readme

@moviie/player-expo

React Native / Expo player for the Moviie platform. Wraps expo-video with the Moviie playback API, custom chrome, telemetry, PiP, background audio, and optional Chromecast.

Full documentation: docs.moviie.ai/player-expo

Platforms

One package, the same useMoviiePlayer + <MoviieVideo /> API on every Expo target:

| Platform | Rendition | |----------|-----------| | iOS / Android | expo-video player with the Moviie chrome (PiP, background audio, native fullscreen, telemetry) | | Web (expo start --web / expo export --platform web) | The official Moviie watch embed in an iframe — full web player (HLS, AI menu, watermark), controlled via the Player JS API |

On web the iframe mounts immediately from embedId and authorizes itself, so the publishable key is optional there (it still enables the playback metadata in the hook result). Details and per-platform differences: docs.moviie.ai/player-expo/expo-web.

Prerequisites

You need a Moviie account and a publishable API key (mvi_pub_*).

  1. Create an account at app.moviie.ai/signin.
  2. Go to Organization Settings → API Keys and create a Publishable key.
  3. Copy the key: it starts with mvi_pub_.

Set it as an environment variable in your project:

# .env.local
EXPO_PUBLIC_MOVIIE_PUBLISHABLE_KEY=mvi_pub_your_key_here

Never use a secret key (mvi_priv_*) in a client app. The playback API rejects it.

Install

pnpm add @moviie/player-expo \
  expo expo-video expo-application \
  react-native-svg react-native-reanimated react-native-gesture-handler \
  react-native-safe-area-context

Optional extras:

| Package | Unlocks | |---------|---------| | expo-secure-store | Persistent viewer token + resume position across sessions | | react-native-google-cast | Chromecast button: install only if you need it |

Setup

1. Add the config plugin to app.json:

{
  "expo": {
    "plugins": [
      ["@moviie/player-expo", { "backgroundPlayback": true, "pictureInPicture": true }],
      ["expo-video", { "supportsBackgroundPlayback": true, "supportsPictureInPicture": true }]
    ]
  }
}

2. Rebuild native projects after adding or changing plugins:

pnpm expo prebuild
pnpm expo run:ios     # or run:android

3. Wrap your app with MoviieProvider:

import { MoviieProvider, MoviieVideo, useMoviiePlayer } from "@moviie/player-expo"

function Player({ embedId }: { embedId: string }) {
  const moviie = useMoviiePlayer({ embedId })
  return <MoviieVideo {...moviie} aspectRatio={16 / 9} />
}

export default function App() {
  return (
    <MoviieProvider publishableKey={process.env.EXPO_PUBLIC_MOVIIE_PUBLISHABLE_KEY}>
      <Player embedId="YOUR-EMBED-UUID" />
    </MoviieProvider>
  )
}

Set EXPO_PUBLIC_MOVIIE_PUBLISHABLE_KEY to a mvi_pub_* key from your organization settings.


API reference

<MoviieProvider>

Provides the Moviie client to the component tree. Place it at the root of your app.

| Prop | Type | Required | Description | |------|------|:--------:|-------------| | publishableKey | string | ✓ | Publishable key (mvi_pub_*) from your Moviie organization settings. | | children | ReactNode | ✓ | Your app tree. |


useMoviiePlayer(options)

Fetches playback metadata and wires native player state. Spread the return value onto <MoviieVideo>.

Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | embedId | string | — | Required. UUID of the embed from the Moviie dashboard. | | autoplay | boolean | dashboard | Override the embed's autoplay setting. | | pictureInPicture | boolean | dashboard | Override the embed's PiP setting. | | pictureInPictureAutoStart | boolean | false | Automatically enter PiP when the app moves to the background. | | backgroundPlayback | boolean | false | Keep audio playing when the app is backgrounded. | | lockScreenNowPlaying | boolean | false | Show a Now Playing notification on the lock screen. | | rememberPosition | boolean | dashboard | Override the embed's "remember position" setting. |

Returns:

| Field | Type | Description | |-------|------|-------------| | player | VideoPlayer \| null | expo-video player instance. null on web. | | playback | MoviiePlaybackData \| null | Full playback metadata from the API. null while loading. | | error | Error \| null | Typed error (see Error codes). null when healthy. | | isLoading | boolean | true while the first playback fetch is in-flight. | | videoPresentation | MoviieVideoPresentationProps | PiP and linear playback flags, pre-computed from playback + options. Spread onto <MoviieVideo>. | | retry | () => void | Re-triggers the playback fetch. |


<MoviieVideo>

Main playback component. Accepts the full return value of useMoviiePlayer spread as props, plus presentation and behaviour overrides.

| Prop | Type | Default | Description | |------|------|---------|-------------| | player | VideoPlayer \| null | — | Player instance from useMoviiePlayer. | | playback | MoviiePlaybackData \| null | — | Playback metadata from useMoviiePlayer. | | aspectRatio | number | — | Width ÷ height ratio (e.g. 16 / 9). | | native | boolean | false | Use the platform's native VideoView chrome instead of the Moviie custom skin. | | nativeControls | boolean | auto | Show or hide native VideoView controls (only relevant when native={true}). | | pictureInPicture | boolean | hook / dashboard | Override PiP availability for this component. | | pictureInPictureAutoStart | boolean | false | Auto-enter PiP when the app moves to the background. | | controlsAutoHideMs | number | 3000 | Milliseconds of inactivity before the Moviie chrome hides. | | controlsVisibleByDefault | boolean | false | Show the chrome immediately on mount without a tap. | | castAdapter | MoviieCastAdapter \| null | — | Chromecast adapter. Create with createGoogleCastAdapter() from @moviie/player-expo/cast. | | cast | boolean | dashboard | Override the embed's Chromecast setting. false hides the button and silences the missing-adapter warning. | | isLoading | boolean | — | From useMoviiePlayer. Shows a loading state in the chrome. | | error | Error \| null | — | From useMoviiePlayer. Triggers the built-in error shell. | | retry | () => void | — | From useMoviiePlayer. Called by the built-in retry button. | | onRetry | () => void | — | Replace the default retry behaviour. You must call moviie.retry() yourself if needed. |

Also accepts all VideoViewProps from expo-video (except player, which is managed internally).


<MoviieErrorBoundary>

React error boundary that catches crashes inside <MoviieVideo> and prevents them from bubbling up to your app.

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | ReactNode | — | Component tree to protect — wrap <MoviieVideo> here. | | fallback | ({ error: Error, reset: () => void }) => ReactNode | built-in shell | Custom fallback UI rendered when a crash occurs. reset() unmounts and remounts the player. | | onError | (error: Error, info: React.ErrorInfo) => void | — | Called on each caught error. Pass Sentry.captureException directly. |

import { MoviieErrorBoundary, MoviieVideo, useMoviiePlayer } from "@moviie/player-expo"
import * as Sentry from "@sentry/react-native"

const moviie = useMoviiePlayer({ embedId })

<MoviieErrorBoundary
  onError={(err, info) => Sentry.captureException(err, { contexts: { react: info } })}
  fallback={({ error, reset }) => <MyFallback message={error.message} onRetry={reset} />}
>
  <MoviieVideo {...moviie} aspectRatio={16 / 9} />
</MoviieErrorBoundary>

Error codes

Each error from useMoviiePlayer has a .code field and maps to a typed class from @moviie/player-sdk.

| Code | Class | Cause | Default CTA | |------|-------|-------|-------------| | auth | MoviieAuthError | Invalid or missing publishable key. | Open dashboard | | not_found | MoviieNotFoundError | Embed ID does not exist or was deleted. | Open dashboard | | bundle_blocked | MoviieBundleBlockedError | App bundle ID not in the embed's allowlist. Most common on mobile. | Open dashboard | | referrer_blocked | MoviieReferrerBlockedError | Request origin present but not in the embed's referrer allowlist. | Open dashboard | | direct_access_blocked | MoviieDirectAccessBlockedError | Video opened directly (no embedding origin) while direct-URL access is blocked. | Open dashboard | | subscription_inactive | MoviieSubscriptionInactiveError | Organization subscription is paused or expired. | Open dashboard | | network | MoviieNetworkError | Network request failed (timeout, unreachable). High frequency on mobile. | Retry | | rate_limit | MoviieRateLimitError | Too many requests in a short period. | Retry | | unknown | Error | Unrecognized error. | Retry |

Detect by instance or code string:

import { MoviieBundleBlockedError } from "@moviie/player-sdk"

if (error instanceof MoviieBundleBlockedError) { /* fix allowlist */ }
if (error?.code === "network") { /* retry */ }

Chromecast (optional)

Cast support lives in the @moviie/player-expo/cast subpath. Install only if you need it:

pnpm add react-native-google-cast

Add the plugin to app.json, then rebuild:

["react-native-google-cast", { "iosReceiverAppId": "CC1AD845", "androidReceiverAppId": "CC1AD845" }]

Wire the adapter:

import { useMemo } from "react"
import { createGoogleCastAdapter } from "@moviie/player-expo/cast"

const castAdapter = useMemo(() => createGoogleCastAdapter(), [])
<MoviieVideo {...moviie} castAdapter={castAdapter} aspectRatio={16 / 9} />

Without a castAdapter, the cast button is hidden and the player works normally.


End of playback

import { useMoviiePlaybackEnded } from "@moviie/player-expo"

useMoviiePlaybackEnded(player, () => {
  // video reached the end
})

Or use PLAYER_API_EVENTS.ENDED with useMoviieEvent for a generic handler.


Requirements

  • Expo SDK 53+
  • expo-video 3+
  • Dev client required: expo-video does not work in Expo Go

License

MIT: see LICENSE. Copyright (c) 2026 Moviie.