Docs
Player ReactRecipesCustom event tracking

Custom event tracking

Subscribe to contract events with useMoviieEvent and onEvent to drive your own analytics.

The player emits the same contract events as the window.Moviie Player API. Subscribe to them to drive analytics, sync your UI, or trigger downstream behaviour. The full event list and payloads are in the Events reference.

One event with useMoviieEvent

useMoviieEvent(handle, event, listener) subscribes to a single event. Get the handle from <MoviieVideo>'s onReady (or a ref), keep it in state, and pass it to the hook:

"use client"
import { useState } from "react"
import { MoviieVideo, useMoviiePlayer, useMoviieEvent } from "@moviie/player-react"
import type { MoviiePlayerHandle } from "@moviie/player-react"

function Player({ embedId }: { embedId: string }) {
  const moviie = useMoviiePlayer({ embedId })
  const [handle, setHandle] = useState<MoviiePlayerHandle | null>(null)

  useMoviieEvent(handle, "play", () => analytics.track("video_play", { embedId }))
  useMoviieEvent(handle, "ended", () => analytics.track("video_complete", { embedId }))

  return <MoviieVideo {...moviie} onReady={setHandle} aspectRatio={16 / 9} />
}

The subscription re-binds when the handle changes and cleans up on unmount.

Many events with onEvent

To handle every event in one place, use the onEvent prop. It fires for each contract event with the event name and its payload:

<MoviieVideo
  {...moviie}
  aspectRatio={16 / 9}
  onEvent={(event, detail) => {
    if (event === "timeupdate") return // skip the high-frequency one
    analytics.track(`moviie_${event}`, { embedId, ...((detail as object) ?? {}) })
  }}
/>

Reading payloads

Some events carry a payload. For example timeupdate reports the current time:

useMoviieEvent(handle, "timeupdate", (detail) => {
  const { currentTime } = detail as { currentTime: number }
  progressBar.value = currentTime
})

Throttle high-frequency handlers

timeupdate fires frequently. Coalesce expensive work (analytics flushes, layout reads) before reacting, or skip it in onEvent as shown above.

Combine with getState()

Events tell you when something happens; getState() tells you what the player looks like at that moment. Read a coherent snapshot from the handle:

useMoviieEvent(handle, "pause", () => {
  const state = handle?.getState()
  analytics.track("video_pause", { at: state?.currentTime })
})

See getState and the Properties reference.

On this page