API

nsh/video

nsh/video exports Video: a video player built on the platform’s player (AVPlayer on iOS and Mac Catalyst, MediaPlayer on Android). Playback is controlled with props, so pausing is a signal away.

import { Video, type VideoProps } from "nsh/video";

See Web view and video for the guide and <Video> for its props.

#Video

const Video: (props: VideoProps) => YNode;

interface VideoProps extends Omit<ViewProps, "children">, VideoHostProps {
  source: string | { uri: string };
}
import { mount, signal, Text, View, type YNode } from "nsh";
import { Video } from "nsh/video";

function Player() {
  const paused = signal(true);
  const time = signal("");
  let video!: YNode;
  return (
    <View style={{ flex: 1, padding: 16, gap: 12 }}>
      <Video
        ref={video}
        source="https://example.com/clip.mp4"
        paused={paused()}
        muted
        repeat
        resizeMode="cover"
        style={{ height: 220, borderRadius: 8, overflow: "hidden", backgroundColor: "#000" }}
        onProgress={(e) => time.set(`${e.currentTime.toFixed(1)} / ${e.duration.toFixed(1)} s`)}
      />
      <View style={{ flexDirection: "row", gap: 16 }}>
        <Text onPress={() => paused.update((p) => !p)}>{paused() ? "Play" : "Pause"}</Text>
        <Text onPress={() => video.seek(0)}>Restart</Text>
        <Text>{time()}</Text>
      </View>
    </View>
  );
}

mount(Player);

#Props

Video takes the view props (style, classes, layout, accessibility, ref; no children), plus:

PropTypeDefaultDescription
sourcestring | { uri: string }(required)The video’s URL.
pausedbooleanfalsePlayback is paused.
mutedbooleanfalseNo sound.
volumenumber10..1.
ratenumber1The playback speed.
repeatbooleanfalsePlays again from the start at the end.
controlsbooleanfalseShows the platform’s playback controls.
resizeMode"cover" | "contain" | "stretch" | "center"How the video fits its box.
progressUpdateIntervalnumber250Milliseconds between onProgress events.

#Events

EventPayloadWhen
onLoadStartThe video starts loading.
onLoad{ duration: number; width: number; height: number }It is ready to play: its length in seconds and its size.
onProgress{ currentTime: number; duration: number }While playing, every progressUpdateInterval ms. Seconds.
onBuffer{ isBuffering: boolean }Playback waits for data, or resumes.
onEndPlayback reached the end.
onError{ message: string }The video could not be loaded or played.

#Ref methods

MethodDescription
seek(seconds)Jumps to seconds.
const aspect = signal(16 / 9);

<Video source={url} onLoad={(e) => aspect.set(e.width / e.height)} style={{ width: "100%", aspectRatio: aspect() }} />;

#Capabilities

When your app declares capabilities in nsh.json, remote video sources must be allowed under network, like fetch; a refused source shows nothing. See Sandbox and capabilities.