VideohatiDocs
Guides

Guide: Embed a player in a Next.js 15 app

Install @videohati/react, create a playback session on the server, and render the player in a client component — with RTL support.

This guide embeds the Videohati player in a Next.js 15 App Router project. The session is created on the server with the Node SDK; the browser only ever sees a short-lived sessionToken.

Before you start

You need:

  • A Next.js 15 app using the App Router.
  • A project API key in test mode (vh_test_...) on the server.
  • A video that has finished encoding. See Upload and play your first video.

1. Install the SDKs

The React SDK renders the player; the Node SDK creates the session on the server.

npm install @videohati/react @videohati/node

2. Create the playback session on the server

In a Server Component (or a route handler / server action), mint a session with the Node SDK. Keep the API key server-side.

// app/watch/[videoId]/page.tsx
import { Videohati } from "@videohati/node";
import { Watch } from "./watch";

export default async function WatchPage({
  params,
}: {
  params: Promise<{ videoId: string }>;
}) {
  const { videoId } = await params;
  const videohati = new Videohati({ apiKey: process.env.VIDEOHATI_API_KEY });

  const session = await videohati.playback.createSession({
    videoId,
    viewerDisplayText: "[email protected]",
  });

  return (
    <Watch
      videoId={videoId}
      projectId={process.env.VIDEOHATI_PROJECT_ID!}
      sessionToken={session.sessionToken}
    />
  );
}

playback.createSession() must run on the server. Passing a vh_live_ or vh_test_ API key into a client component would expose it in the browser bundle.

3. Render the player in a client component

<VideohatiPlayer /> is a client component. Pass the videoId, projectId, and the server-created sessionToken.

// app/watch/[videoId]/watch.tsx
"use client";

import { VideohatiPlayer } from "@videohati/react";

export function Watch({
  videoId,
  projectId,
  sessionToken,
}: {
  videoId: string;
  projectId: string;
  sessionToken: string;
}) {
  return (
    <VideohatiPlayer
      videoId={videoId}
      projectId={projectId}
      sessionToken={sessionToken}
      lang="en"
      autoplay="muted"
      onStateChange={(state) => console.log("player state:", state)}
      onError={(error) => console.error(error.code, error.message)}
      style={{ width: "100%", maxWidth: 960 }}
    />
  );
}

The player handles HLS playback, the session heartbeat, and the watermark on its own — you do not call the playback endpoints again from the browser.

4. RTL and Arabic

Set lang="ar" to render the player chrome in Arabic with a right-to-left layout. The player mirrors its own controls; you only need to make sure the surrounding page direction is correct:

<VideohatiPlayer
  videoId={videoId}
  projectId={projectId}
  sessionToken={sessionToken}
  lang="ar"
/>

In a localized Next.js layout, set dir="rtl" on <html> (or the player's container) for the Arabic locale so the page around the player mirrors too.

5. Optional: configure a shared base URL

If you point at a staging API, wrap the part of your tree that uses the React hooks with VideohatiProvider:

"use client";

import { VideohatiProvider } from "@videohati/react";

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <VideohatiProvider baseUrl="https://api.staging.videohati.com">
      {children}
    </VideohatiProvider>
  );
}

For client-side uploads from the dashboard session, the useVideohatiUpload hook drives the same chunked flow inside the browser.