Player em React/Next.js

Integre o player da Spalla em projetos React e Next.js (App Router).

O player da Spalla não é um pacote npm: ele é um script carregado por <script> e exposto como window.SpallaPlayer. Em React, isso significa carregar o script antes de qualquer componente que crie um player — e ler a global depois de montado.

Coloque os arquivos do player em public/vendor/.

Baixe o pacote da release do player e copie para dentro de public/vendor/ do seu projeto:

  • spalla-player.min.js (pode renomear para spalla-player.js)
  • spalla-player.transmuxer-worker.js — na mesma pasta do player; ele é localizado automaticamente ao lado do script
  • wasm_exec.js (e spalla.wasm, se o pacote o incluir) — apenas se a sua política de CSP proíbe worker-src blob:
spalla-player.js
spalla-player.transmuxer-worker.js
wasm_exec.js

Arquivos em public/ são servidos como estáticos pelo Next, então /vendor/spalla-player.js resolve sem configuração adicional.

Alternativa via CDN: em vez de hospedar os arquivos, aponte o <Script src> do próximo passo direto para https://beyond.spalla.io/player/spalla-player.min.js, preservando a estratégia beforeInteractive. A URL serve sempre a última versão publicada, sem fixar versão; o player localiza os arquivos auxiliares ao lado do script no CDN, sem copiá-los para o projeto. Para decidir quando atualizar, prefira o bundle local de uma release específica.

Carregue o script no layout raiz com a estratégia beforeInteractive:

// app/layout.jsx
import Script from "next/script";

export default function RootLayout({ children }) {
  return (
    <html lang="pt-BR">
      <body>
        <Script src="/vendor/spalla-player.js" strategy="beforeInteractive" />
        {children}
      </body>
    </html>
  );
}

Essa estratégia é essencial: garante que window.SpallaPlayer já exista antes de qualquer componente montar. Qualquer outra (afterInteractive, lazyOnload) arrisca uma corrida em que o componente tenta new SpallaPlayer(...) antes do script terminar de carregar.

Crie o player num componente cliente. O componente recebe o ID do conteúdo, monta o player e — importante — o destrói na limpeza:

"use client";

import { useEffect, useRef } from "react";

export function PlayerStage({ contentId }) {
  const containerRef = useRef(null);

  useEffect(() => {
    const instancia = new window.SpallaPlayer(containerRef.current);
    instancia.load(contentId).catch(console.error);

    return () => instancia.destroy();
  }, [contentId]);

  return <div ref={containerRef} />;
}

Sem o destroy() na limpeza, trocar de tela ou desmontar o componente deixa o player anterior tocando em segundo plano.

Use o componente onde quiser:

<PlayerStage contentId="SEU_ID_AQUI" />

A partir daí, vale tudo dos guias de ajustes e eventos: as opções vão no segundo argumento do construtor e os eventos em instancia.on(...).

Skin registrada em runtime: nunca importe no topo do módulo

Uma skin personalizada estende SpallaPlayer.Skin, que só existe depois do script carregar. Importar esse arquivo no topo de um módulo quebra em produção: o Next também renderiza componentes 'use client' no servidor (para gerar o HTML inicial), e lá window não existe. Importe dinamicamente, dentro de um useEffect, depois de confirmar que está no navegador:

useEffect(() => {
  import("../skins/minhaSkin.js").then(() => {
    const instancia = new window.SpallaPlayer(containerRef.current, {
      skin: "minha",
    });
  });
}, []);

Como registrar a skin em si está no guia de skins personalizadas.

Onde aprofundar

O repositório do player traz dois exemplos Next.js: o completo, com quatro telas (controles com painel próprio, overlay personalizado, várias instâncias na mesma página e skin registrada em runtime), e a variante que carrega o player do CDN, sem bundle em public/vendor/. Para criar skins e plugins próprios, veja skins personalizadas e plugins.

Nesta página