Video
Examples
(3)Properties
(47)Playground
Basic Playback
Video with a local source, default transport controls, and custom buttons wired to the imperative ref for play, pause, and seeking.Loading demo…
import
{
useMemo
,
useRef
,
useState
}
from
'react'
;
import
{
Asset
}
from
'expo-asset'
;
import
{
Block
,
Button
,
Card
,
Row
,
Text
,
Video
}
from
'@platform-blocks/react-ui-library'
;
import
type
{
VideoRef
}
from
'@platform-blocks/react-ui-library'
;
// `source.url` takes a URL, so resolve the bundled clip to one. `Image.resolveAssetSource`
// is native-only, whereas expo-asset works on web too.
const
SOURCE
=
{
url
:
Asset
.
fromModule
(
require
(
'../../../../assets/video/demo-clip.mp4'
)
)
.
uri
,
}
as
const
;
export
function
Demo
(
)
{
const
videoRef
=
useRef
<VideoRef
>
(
null
)
;
const
[
status
,
setStatus
]
=
useState
(
'Ready'
)
;
const
[
currentTime
,
setCurrentTime
]
=
useState
(
0
)
;
const
[
duration
,
setDuration
]
=
useState
(
0
)
;
const
actions
=
useMemo
(
(
)
=>
[
{
label
:
'Play'
,
onPress
:
(
)
=>
videoRef
.
current
?
.
play
(
)
}
,
{
label
:
'Pause'
,
onPress
:
(
)
=>
videoRef
.
current
?
.
pause
(
)
}
,
{
label
:
'Skip to 4s'
,
onPress
:
(
)
=>
videoRef
.
current
?
.
seek
(
4
)
}
,
{
label
:
'Volume 50%'
,
onPress
:
(
)
=>
videoRef
.
current
?
.
setVolume
(
0.5
)
}
,
{
label
:
'Mute'
,
onPress
:
(
)
=>
videoRef
.
current
?
.
setVolume
(
0
)
}
,
{
label
:
'Unmute'
,
onPress
:
(
)
=>
videoRef
.
current
?
.
setVolume
(
1
)
}
,
{
label
:
'1.5× speed'
,
onPress
:
(
)
=>
videoRef
.
current
?
.
setPlaybackRate
(
1.5
)
}
,
{
label
:
'Reset status'
,
onPress
:
(
)
=>
setStatus
(
'Ready'
)
}
,
]
,
[
]
)
;
const
formatTime
=
(
seconds
:
number
)
=>
{
const
mins
=
Math
.
floor
(
seconds
/
60
)
.
toString
(
)
.
padStart
(
2
,
'0'
)
;
const
secs
=
Math
.
floor
(
seconds
%
60
)
.
toString
(
)
.
padStart
(
2
,
'0'
)
;
return
`${mins}:${secs}`
;
}
;
return
(
<Card
p
=
"md"
>
<Block
>
<Text
size
=
"sm"
color
=
"secondary"
>
Combine
default
transport controls with imperative helpers to script playback
,
adjust volume
,
and toggle captions
from
surrounding
UI
.
</Text
>
<Video
ref
=
{
videoRef
}
source
=
{
SOURCE
}
w
=
"100%"
h
=
{
300
}
controls
=
{
{
play
:
true
,
pause
:
true
,
progress
:
true
,
time
:
true
,
volume
:
true
,
fullscreen
:
true
,
playbackRate
:
true
,
quality
:
true
,
autoHide
:
true
,
autoHideTimeout
:
3000
,
}
}
onPlay
=
{
(
)
=>
setStatus
(
'Playing'
)
}
onPause
=
{
(
)
=>
setStatus
(
'Paused'
)
}
onLoad
=
{
(
)
=>
setStatus
(
'Loaded'
)
}
onBuffer
=
{
(
buffering
)
=>
setStatus
(
buffering
?
'Buffering…'
:
'Playing'
)
}
onTimeUpdate
=
{
(
state
)
=>
setCurrentTime
(
state
.
currentTime
)
}
onDurationChange
=
{
(
value
)
=>
setDuration
(
value
)
}
onError
=
{
(
error
)
=>
setStatus
(
`Error: ${error}`
)
}
/>
<Block
>
<Text
size
=
"xs"
color
=
"secondary"
>
Status
:
{
status
}
</Text
>
{
duration
>
0
&&
(
<Text
size
=
"xs"
color
=
"secondary"
>
Progress
:
{
formatTime
(
currentTime
)
}
/
{
formatTime
(
duration
)
}
</Text
>
)
}
</Block
>
<Row
gap
=
"sm"
wrap
=
"wrap"
>
{
actions
.
map
(
(
action
)
=>
(
<Button
key
=
{
action
.
label
}
size
=
"xs"
variant
=
"outline"
onPress
=
{
action
.
onPress
}
>
{
action
.
label
}
</Button
>
)
)
}
</Row
>
</Block
>
</Card
>
)
;
}
Timeline Events
timeline markers to fire callbacks at specific timestamps and keep a log of playback milestones without polling the player.Loading demo…
import
{
useMemo
,
useState
}
from
'react'
;
import
{
Asset
}
from
'expo-asset'
;
import
{
Block
,
Button
,
Card
,
Text
,
Video
}
from
'@platform-blocks/react-ui-library'
;
import
type
{
VideoTimelineEvent
,
VideoState
}
from
'@platform-blocks/react-ui-library'
;
// `source.url` takes a URL, so resolve the bundled clip to one. `Image.resolveAssetSource`
// is native-only, whereas expo-asset works on web too.
const
SOURCE
=
{
url
:
Asset
.
fromModule
(
require
(
'../../../../assets/video/demo-clip.mp4'
)
)
.
uri
,
}
as
const
;
export
function
Demo
(
)
{
const
[
log
,
setLog
]
=
useState
<
string
[
]
>
(
[
]
)
;
const
timelineEvents
=
useMemo
<VideoTimelineEvent
[
]
>
(
(
)
=>
[
{
id
:
'intro'
,
time
:
2
,
type
:
'chapter'
,
data
:
{
label
:
'Introduction'
,
description
:
'Video introduction starts'
}
,
callback
:
(
_
,
state
)
=>
{
console
.
log
(
'Introduction reached!'
,
state
.
currentTime
)
;
setLog
(
(
entries
)
=>
[
.
.
.
entries
,
'Reached introduction at 2s'
]
)
;
}
,
}
,
{
id
:
'main-content'
,
time
:
5
,
type
:
'chapter'
,
data
:
{
label
:
'Main content'
,
description
:
'Main content begins'
}
,
callback
:
(
_
,
state
)
=>
{
console
.
log
(
'Main content reached!'
,
state
.
currentTime
)
;
setLog
(
(
entries
)
=>
[
.
.
.
entries
,
'Reached main content at 5s'
]
)
;
}
,
}
,
]
,
[
]
)
;
const
handleTimelineEvent
=
(
event
:
VideoTimelineEvent
,
state
:
VideoState
)
=>
{
console
.
log
(
'Timeline event triggered:'
,
event
.
id
,
'at time:'
,
state
.
currentTime
)
;
}
;
return
(
<Card
p
=
"md"
>
<Block
>
<Text
size
=
"sm"
color
=
"secondary"
>
Attach
`timeline`
markers to run callbacks as playback crosses those timestamps
.
Use
the
`onTimelineEvent`
hook
for
analytics or syncing
UI
.
</Text
>
<Video
source
=
{
SOURCE
}
w
=
"100%"
h
=
{
300
}
controls
timeline
=
{
timelineEvents
}
onTimelineEvent
=
{
handleTimelineEvent
}
/>
<Block
>
<Text
size
=
"xs"
color
=
"secondary"
>
Timeline
log
</Text
>
{
log
.
length
===
0
?
(
<Text
size
=
"xs"
color
=
"secondary"
>
Press
play to fire registered markers
.
</Text
>
)
:
(
<Block
>
{
log
.
map
(
(
entry
,
index
)
=>
(
<Text
key
=
{
`${entry}-${index}`
}
size
=
"xs"
>
{
entry
}
</Text
>
)
)
}
</Block
>
)
}
{
log
.
length
>
0
&&
(
<Button
size
=
"xs"
variant
=
"outline"
onPress
=
{
(
)
=>
setLog
(
[
]
)
}
>
Clear
log
</Button
>
)
}
</Block
>
</Block
>
</Card
>
)
;
}
YouTube Source
Video at a YouTube URL to stream hosted media while keeping the same control surface and status callbacks as native sources.Loading demo…
import
{
useState
}
from
'react'
;
import
{
Block
,
Button
,
Card
,
Row
,
Text
,
Video
}
from
'@platform-blocks/react-ui-library'
;
const
YOUTUBE_SOURCE
=
{
youtube
:
'dQw4w9WgXcQ'
}
as
const
;
export
function
Demo
(
)
{
const
[
status
,
setStatus
]
=
useState
(
'Paused'
)
;
const
[
currentTime
,
setCurrentTime
]
=
useState
(
0
)
;
const
[
duration
,
setDuration
]
=
useState
(
0
)
;
return
(
<Card
p
=
"md"
>
<Block
>
<Text
size
=
"sm"
color
=
"secondary"
>
Stream
YouTube
content by pointing
`source.youtube`
to an
ID
.
Playback
state stays in sync so you can react to buffering or progress updates
.
</Text
>
<Video
source
=
{
YOUTUBE_SOURCE
}
w
=
{
420
}
h
=
{
280
}
controls
=
{
{
play
:
true
,
pause
:
true
,
progress
:
true
,
time
:
true
,
volume
:
true
,
fullscreen
:
true
,
playbackRate
:
true
,
quality
:
true
,
}
}
youtubeOptions
=
{
{
modestbranding
:
true
,
rel
:
false
,
}
}
onPlay
=
{
(
)
=>
setStatus
(
'Playing'
)
}
onPause
=
{
(
)
=>
setStatus
(
'Paused'
)
}
onBuffer
=
{
(
buffering
)
=>
setStatus
(
buffering
?
'Buffering…'
:
'Playing'
)
}
onError
=
{
(
)
=>
setStatus
(
'Error'
)
}
onTimeUpdate
=
{
(
state
)
=>
{
setCurrentTime
(
state
.
currentTime
)
;
setDuration
(
state
.
duration
)
;
}
}
/>
<Row
gap
=
"sm"
justify
=
"space-between"
align
=
"center"
>
<Text
size
=
"xs"
color
=
"secondary"
>
Status
:
{
status
}
</Text
>
<Text
size
=
"xs"
color
=
"secondary"
>
{
Math
.
floor
(
currentTime
)
}
s
/
{
Math
.
floor
(
duration
)
}
s
</Text
>
</Row
>
<Row
gap
=
"sm"
wrap
=
"wrap"
>
<Button
size
=
"xs"
variant
=
"outline"
onPress
=
{
(
)
=>
setStatus
(
'Paused'
)
}
>
Reset
status
</Button
>
</Row
>
</Block
>
</Card
>
)
;
}
</>
react-ui-library
100+ accessible, themeable components that work seamlessly across iOS, Android, and Web.
A Platform Blocks product.
Quick Links
Resources