Skip to content
Seamlift

Other frameworks

Astro, Svelte, Vue, plain JavaScript and Expo DOM components, powered by one framework-free player.

One player across frameworks

Import seamplayer/element to register <seam-player>. Simple options are attributes; arrays, objects and handlers are properties. React is optional for this entry. CSS injects automatically after registration.

TypeScript
import "seamplayer/element";
const player = document.querySelector("seam-player")!;
player.options = { src: "/film.mp4", title: "Film", chapters: [{ start: 0, title: "Intro" }] };
player.play();

Astro

No React integration or client hydration directive is required. Import the element in a browser script. For a poster before JavaScript loads, render the core poster HTML and import the stylesheet on the server.

HTML / component
---
import { renderPoster } from "seamplayer/core";
import "seamplayer/styles.css";
const options = { src: "/film.mp4", poster: "/poster.jpg", title: "Film" };
---
<seam-player class="sp" src={options.src} poster={options.poster} title={options.title}>
  <Fragment set:html={renderPoster(options)} />
</seam-player>
<script>
  import "seamplayer/element";
</script>

Svelte and Vue

The Svelte 5 example registers only in the browser and assigns chapters as a property. Vue can use the same element; configure isCustomElement for seam-player in the Vue compiler.

HTML / component
<script lang="ts">
  import { onMount } from "svelte";
  import type { SeamPlayerElement } from "seamplayer/element";
  import type { SeamChapter } from "seamplayer/core";
  import "seamplayer/styles.css";

  let { src, poster, title = "Film", chapters = [] }: {
    src: string; poster?: string; title?: string; chapters?: SeamChapter[];
  } = $props();
  let player = $state<SeamPlayerElement>();
  onMount(() => { void import("seamplayer/element"); });
  $effect(() => { if (player) player.chapters = chapters; });
</script>

<seam-player class="sp" bind:this={player} {src} {poster} {title} />

Plain JavaScript

The core entry exposes createSeamPlayer, renderPoster and the shared option types. Call destroy when removing the host to release media, timers and listeners.

TypeScript
import { createSeamPlayer } from "seamplayer/core";
const player = createSeamPlayer(document.querySelector<HTMLElement>("#player")!, { src: "/film.mp4" });
player.update({ src: "/next.mp4" });
player.destroy();

Expo and React Native

Copy this file into your app so Expo processes the use dom directive. It runs the web player in a webview on native. Props must be serializable; native actions must be top-level async functions. Expo SDK 55 and earlier require react-native-webview; SDK 56+ includes the default DOM webview. Fullscreen, picture-in-picture, clipboard and downloads depend on the platform. Native lock-screen media controls are not implemented.

React
"use dom";

import type { DOMProps } from "expo/dom";
import { SeamPlayer } from "seamplayer/react";

// Keep props serializable. Native actions must be top-level async functions.
export default function SeamPlayerDOM({ src, poster, title, onTimeUpdate }: {
  src: string;
  poster?: string;
  title?: string;
  dom?: DOMProps;
  onTimeUpdate?: (time: number) => Promise<void>;
}) {
  return <SeamPlayer src={src} poster={poster} title={title}
    onTimeUpdate={(time) => { void onTimeUpdate?.(time); }} />;
}

Source and complete examples on GitHub