Use this pattern when React components own your narrative controls. Use the compatible dependencies from the quickstart.
import { useState } from 'react'
import { createFiveProvider, FiveAutoCanvas } from '@realsee/five/react'
import type { Work } from '@realsee/five'
import { VreoProvider, useVreoAction, useVreoPausedState } from '@realsee/vreo/react'
import type { VreoUnit } from '@realsee/vreo/typings'
import '@realsee/vreo/stylesheets/default.css'
const FiveProvider = createFiveProvider()
function Controls({ unit }: { unit: VreoUnit }) {
const { load, play, pause } = useVreoAction()
const paused = useVreoPausedState()
const [status, setStatus] = useState('Ready to load narrative')
async function start() {
setStatus('Loading narrative')
try {
if (!await load(unit, 0) || !play(0)) throw new Error('Narrative unavailable')
setStatus('Playing')
} catch { setStatus('Narrative unavailable. Check the script and media access.') }
}
return <div><button onClick={() => void start()}>Load and replay</button><button onClick={pause} disabled={paused}>Pause</button><p role="status">{status}</p></div>
}
// Keep FiveAutoCanvas outside VreoProvider: that provider mounts children after Player creation.
export function NarrativeScene({ work, unit }: { work: Work; unit: VreoUnit }) {
return <FiveProvider initialWork={work}>
<div style={{ width: '100%', height: 480 }}><FiveAutoCanvas /></div>
<VreoProvider><Controls unit={unit} /></VreoProvider>
</FiveProvider>
}VreoProvider must be inside FiveProvider, since it obtains that Five instance. Keep FiveAutoCanvas outside VreoProvider: Vreo creates its player in an effect and renders children only after creation. Putting the scene canvas inside it can leave player creation ahead of the canvas mount.
Pass parsed Work and a validated VreoUnit from your application. Wait until the scene is ready before offering narrative controls in the complete application. The snippet demonstrates provider and hook integration; it does not implement your Work-loading UI.
Replay is explicit
useVreoAction().play() in Vreo 2.6.4 delegates to player.play(currentTime || 0). Calling it without a time therefore starts from zero. These controls say Load and replay, and call play(0) deliberately. Do not label that hook call Resume. If you need accurate resume using the current timeline, use the imperative Player controls, which expose getCurrentTime().
useVreoPausedState() follows the player state. The provider disposes its player on unmount; avoid creating another Player for the same scene in parallel.
Verify
Load after scene readiness, pause, replay, and unmount the route. Test in your actual client-rendering environment and browser audio policy.
