Use Cases (Aktualisiert: 2.6.2026)

Einen eigenen Video-Player mit Claude Code bauen

Einen produktionsreifen Video-Player für React mit Claude Code: Barrierefreiheit, Auslieferung und Monetarisierung.

Einen eigenen Video-Player mit Claude Code bauen

Upgrade 2026: Was ein Video-Player überhaupt ist

Ein Video-Player ist eine UI, die eine Videodatei oder einen Stream lädt und dem Leser Bedienung für Wiedergabe, Pause, Suchen, Lautstärke, Untertitel und Geschwindigkeit gibt. Im Web liegt meist das native <video>-Element als Fundament darunter, und aus JavaScript liest und aktualisiert man currentTime, duration, paused, volume, muted und playbackRate des HTMLMediaElement.

Diese Definition steht bewusst am Anfang, denn ein Video-Player ist keine bloße Dekoration. In Lernprodukten entscheidet er direkt über die Abschlussquote: ob Teilnehmer mitten im Video wieder einsteigen können, ob Untertitel beim Verstehen helfen und ob man die Geschwindigkeit zum Wiederholen senken kann. Auf Medienseiten zählt, ob er die erste Darstellung des Artikels nicht stört, ob er auch über das Mobilfunknetz nicht zu lange warten lässt und ob nach dem Ansehen ein natürlicher Übergang zum nächsten Artikel oder zur Registrierung entsteht. Bei einer SaaS-Produktdemo wird das Wiedergabeerlebnis selbst zum Vertrauenssignal.

Sieh dir bei der Umsetzung die Referenz zum <video>-Element und die HTMLMediaElement-API bei MDN an. Claude Code eignet sich gut dafür, Komponente, Tests, Barrierefreiheits-Review und Messereignisse gemeinsam aufzuräumen. Welche Erfahrung du schützen willst, entscheidet aber die Produktseite. Beginne mit dem kleinstmöglichen Player, den deine Leser brauchen.

Als verwandte Umsetzungen lohnt der Blick auf den Audio-Player mit Claude Code für reinen Ton, auf Barrierefreiheit mit Claude Code für Tastaturbedienung und Untertitel und auf die Performance-Optimierung mit Claude Code für die Ladezeit. Wenn aus Lern- oder Medien-Pfaden Umsatz werden soll, setze die Trainings- und Beratungsseite als CTA und gehe von dort zu Implementierungs-Review und Betriebsdesign über.

Natives <video>, eigene Steuerung und Streaming im Vergleich

Zuerst zu entscheiden ist nicht das Aussehen der Buttons, sondern welche Wiedergabeform du wählst. Natives <video controls> ist am schnellsten veröffentlicht, und der Browser kümmert sich um Tastaturbedienung und grundlegende Untertitel. Brauchst du dagegen Lernverwaltung, gespeicherten Fortschritt, CTAs für Mitglieder, Kapitel oder eigene Messung, ist eine eigene Steuerung über das HTMLMediaElement passender. Ist das Video lang, gibt es viele Zuschauer oder große Unterschiede bei Land und Netz, kommen HLS, DASH oder Streaming über einen Video-Dienst hinzu.

FormGeeignet fürWorauf in Produktion achten
Natives <video controls>Kurze Einbettung im Artikel, interne Doku, einfache LandingpageSchnell umgesetzt. Marke und detaillierte Messung sind begrenzt, aber die Grundbedienung ist zuverlässig.
Eigene Steuerung auf HTMLMediaElementLernprodukt, Medienseite, SaaS-Demo, Video für MitgliederUI, Wiedereinstieg, CTA-Anzeige und Analytics steuerbar. Dafür trägst du Verantwortung für Barrierefreiheit und Fehlerbehandlung.
HLS/DASH oder Video-PlattformLange Kurse, große Kataloge, Live, geschützte VideosTranscoding, Manifest, CDN, Bitrate, Autorisierung und Player-Bibliothek müssen entworfen werden.

Für eine erste Umsetzung mit Claude Code ist es realistisch, mit einem kurzen MP4, preload="metadata", einem Poster-Bild und einer Untertiteldatei zu starten. Sobald Kapitellinks, Untertitelsuche, Abschlussquote, Anzeige für Gumroad-Käufer oder eine SSO-Anbindung nötig werden, machst du den Player individuell, und sobald eine einzelne MP4-Datei nicht mehr reicht, wechselst du zu Streaming.

Architektur-Tabelle

SchichtAufgabeWas Claude Code prüfen soll
Asset-VerwaltungMP4/WebM, Poster-Bild, Untertitel und Streaming-Manifest bereitstellenURL, MIME-Type, CORS, befristete URLs und Alternativtext prüfen.
Natives Medien-ElementWiedergabe, Laden, Zeit, Untertitel und Fehler dem Browser überlassenpreload, playsInline, track, Fallback-Text und Event-Abos prüfen.
ZustandsverwaltungcurrentTime, duration, paused, muted, volume und Tempo in React spiegelnZustand nicht raten, sondern aus Media-Events synchronisieren.
Bedien-UIWiedergabe, Suchleiste, Lautstärke, Tempo, Untertitel und Vollbild anbietenbutton und input nutzen, Tastaturbedienung und Labels erhalten.
Fortbestehender ZustandWiedereinstiegsposition, Abschluss, Tempo und Stummschaltung speichernSpeicherumfang minimieren, auf öffentlichen Seiten Datenschutz erklären.
MessungStart, 25/50/75 %, Abschluss, Fehler und CTA-Klick erfassenKeine Events pro Sekunde senden, nur nützliche Kennzahlen behalten.
PerformancePoster, CDN, Lazy Loading und Bitrate justierenCLS, initiale Übertragung, Mobilnetz und Cache-Header prüfen.

Mit dieser Aufteilung werden auch die Aufträge an Claude Code konkret. „Prüfe nur die Barrierefreiheit der Suchleiste“, „Korrigiere nur Größe und Ladestrategie des Posters“ oder „Teste, dass das Abschluss-Event nicht doppelt gesendet wird“ – so klein schneidbar verhinderst du, dass eine UI-Korrektur die Auslieferung oder das Analytics-Design mitreißt.

Reale Anwendungsfälle

Anwendungsfall 1 ist die Lektion eines kostenpflichtigen Kurses. Teilnehmer verlassen den Platz, kommen auf einem anderen Gerät zurück und wiederholen mit 1,25-facher Geschwindigkeit. Wichtiger als hübsche Animationen sind Untertitel, Wiedereinstiegsposition, Abschlussbedingung und der Weg zur nächsten Lektion. Das Abschluss-Event wird nicht beim Seitenaufruf gesendet, sondern wenn ein bestimmter Anteil angesehen wurde.

Anwendungsfall 2 ist ein eingebettetes Video im Medienartikel. Leser entscheiden beim Lesen, ob sie das Video ansehen. Stelle Poster-Bild und Transkript daneben und lade das Video selbst erst, wenn es gebraucht wird. Wer das Video zu Ende sieht, bekommt einen Pfad zu verwandten Artikeln, Newsletter, Registrierung oder einem kostenpflichtigen Report.

Anwendungsfall 3 ist die SaaS-Produktdemo. Funktionsbezogene Kapitel, ein Pfad zur Preisseite, ein Link zur API-Doku und ein Kontakt-Button, je nach Wiedergabeposition angepasst – so wird aus einem bloßen Video eine Erklär-UI vor dem Verkaufsgespräch. Wichtiger als der vollständige Abschluss ist, welches Kapitel gesehen und welcher CTA geklickt wurde.

Anwendungsfall 4 sind internes Training und Support. Wenn du Verkaufsgespräche, Onboarding, Compliance und Support-Beispiele als Video aufbereitest, zählen stabile Wiedergabe unter SSO, Untertitel, Audit-Logs und eine Anleitung im Fehlerfall. Statt eines auffälligen Designs wird es wertvoll, mit dem nötigen Minimum zu sehen, wer wie weit gekommen ist.

Kopierbarer React-/TypeScript-Code

Die folgende Komponente ist eine Minimalvariante, die du direkt in Vite, Next.js oder eine React-Island in Astro setzen kannst. Sie hängt von keiner externen Player-Bibliothek ab und nutzt das native <video> sowie HTMLMediaElement-Events.

import { useEffect, useRef, useState, type ChangeEvent } from "react";

type CaptionTrack = {
  src: string;
  srcLang: string;
  label: string;
  default?: boolean;
};

type ProductionVideoPlayerProps = {
  src: string;
  title: string;
  poster?: string;
  captions?: CaptionTrack[];
};

function formatTime(value: number) {
  if (!Number.isFinite(value)) return "0:00";
  const minutes = Math.floor(value / 60);
  const seconds = Math.floor(value % 60).toString().padStart(2, "0");
  return `${minutes}:${seconds}`;
}

export function ProductionVideoPlayer({
  src,
  title,
  poster,
  captions = [],
}: ProductionVideoPlayerProps) {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [volume, setVolume] = useState(0.8);
  const [rate, setRate] = useState(1);
  const [error, setError] = useState("");

  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;

    const syncTime = () => setCurrentTime(video.currentTime);
    const syncDuration = () => {
      setDuration(Number.isFinite(video.duration) ? video.duration : 0);
    };
    const syncPlayState = () => setIsPlaying(!video.paused);
    const syncVolume = () => setVolume(video.muted ? 0 : video.volume);

    video.addEventListener("timeupdate", syncTime);
    video.addEventListener("loadedmetadata", syncDuration);
    video.addEventListener("durationchange", syncDuration);
    video.addEventListener("play", syncPlayState);
    video.addEventListener("pause", syncPlayState);
    video.addEventListener("volumechange", syncVolume);

    return () => {
      video.removeEventListener("timeupdate", syncTime);
      video.removeEventListener("loadedmetadata", syncDuration);
      video.removeEventListener("durationchange", syncDuration);
      video.removeEventListener("play", syncPlayState);
      video.removeEventListener("pause", syncPlayState);
      video.removeEventListener("volumechange", syncVolume);
    };
  }, []);

  async function togglePlay() {
    const video = videoRef.current;
    if (!video) return;

    if (video.paused) {
      try {
        await video.play();
        setError("");
      } catch {
        setError("Playback was blocked. Tap play again or check browser settings.");
      }
    } else {
      video.pause();
    }
  }

  function seek(event: ChangeEvent<HTMLInputElement>) {
    const video = videoRef.current;
    if (!video) return;
    const nextTime = Number(event.target.value);
    video.currentTime = nextTime;
    setCurrentTime(nextTime);
  }

  function changeVolume(event: ChangeEvent<HTMLInputElement>) {
    const video = videoRef.current;
    if (!video) return;
    const nextVolume = Number(event.target.value);
    video.volume = nextVolume;
    video.muted = nextVolume === 0;
    setVolume(nextVolume);
  }

  function changeRate(event: ChangeEvent<HTMLSelectElement>) {
    const video = videoRef.current;
    if (!video) return;
    const nextRate = Number(event.target.value);
    video.playbackRate = nextRate;
    setRate(nextRate);
  }

  function toggleMute() {
    const video = videoRef.current;
    if (!video) return;
    video.muted = !video.muted;
  }

  return (
    <section className="production-video-player" aria-label={`${title} video player`}>
      <video ref={videoRef} poster={poster} preload="metadata" playsInline>
        <source src={src} type={src.endsWith(".webm") ? "video/webm" : "video/mp4"} />
        {captions.map((track) => (
          <track
            key={track.src}
            kind="captions"
            src={track.src}
            srcLang={track.srcLang}
            label={track.label}
            default={track.default}
          />
        ))}
        Your browser does not support the video element.
      </video>

      <div role="group" aria-label="Video controls">
        <button type="button" onClick={togglePlay} aria-label={isPlaying ? "Pause video" : "Play video"}>
          {isPlaying ? "Pause" : "Play"}
        </button>

        <label>
          <span>Seek</span>
          <input
            type="range"
            min="0"
            max={duration || 0}
            step="0.1"
            value={duration ? currentTime : 0}
            onChange={seek}
            aria-valuetext={`${formatTime(currentTime)} of ${formatTime(duration)}`}
          />
        </label>

        <output>
          {formatTime(currentTime)} / {formatTime(duration)}
        </output>

        <button type="button" onClick={toggleMute} aria-label={volume === 0 ? "Unmute video" : "Mute video"}>
          {volume === 0 ? "Unmute" : "Mute"}
        </button>

        <label>
          <span>Volume</span>
          <input type="range" min="0" max="1" step="0.05" value={volume} onChange={changeVolume} />
        </label>

        <label>
          <span>Speed</span>
          <select value={rate} onChange={changeRate}>
            {[0.75, 1, 1.25, 1.5, 2].map((speed) => (
              <option key={speed} value={speed}>
                {speed}x
              </option>
            ))}
          </select>
        </label>
      </div>

      {error ? <p role="alert">{error}</p> : null}
    </section>
  );
}

Fallstricke bei Barrierefreiheit und Performance

Der größte Fallstrick ist ein Zustand, in dem die native Steuerung versteckt, die gleichwertige Bedienbarkeit aber nicht zurückgegeben wurde. Eine Wiedergabe-Taste aus einem div, eine Suchleiste ohne Label, keine Tempoänderung per Tastatur, fehlende Untertitel – solche Umsetzungen sehen vielleicht gut aus, taugen aber nicht für Lernprodukte. Grundregel: Buttons als echtes button, das Suchen als input type="range" und Fehler über role="alert" mitteilen.

Bei der Performance ist ein Design gefährlich, das in einer Artikelliste oder auf einer Landingpage mehrere Videos gleichzeitig lädt. Gib dem Poster-Bild feste Breite und Höhe, setze optional ansehbare Videos auf preload="metadata" und liefere das Video selbst über ein CDN aus. Verteilst du einen langen Kurs nur als eine riesige MP4, bist du bei Mobilnetz, Auslandsnutzung und Abbruch zwischendurch überall im Nachteil.

Auch Fehler bei der Messung sind häufig. Statt Events im Sekundentakt zu senden, sind Start, 25 %, 50 %, 75 %, Abschluss, Fehler und CTA-Klick für Verbesserungen nützlicher. Bei einem Lerndienst solltest du sehen, „welche Lektion eine niedrige Abschlussquote hat“, auf einer Medienseite, „ob nach dem Ansehen eine Registrierung folgte“.

Rollout-Checkliste

  • Labels für Tastatur, Touch, Maus und Screenreader geprüft.
  • Untertitel oder Transkript bereitgestellt und wichtige Informationen nicht nur ins Video gesperrt.
  • Auf Mobil wirkt playsInline und es gibt keinen ungewollten Vollbildwechsel.
  • Seitenverhältnis und Größe des Poster-Bilds fixiert, kein CLS ausgelöst.
  • Kein preload="auto" an Videos, die für die erste Darstellung nicht nötig sind.
  • Abgelaufene URLs, fehlende Untertitel, langsames Netz und Autoplay-Block getestet.
  • Messnamen für Start, Fortschritt, Abschluss, Fehler und CTA-Klick festgelegt.
  • Vorgehen vorbereitet, um bei kaputter eigener Steuerung auf native controls zurückzufallen.

Verwendungsbeispiel

<VideoPlayer
  src="/videos/demo.mp4"
  poster="/images/poster.jpg"
  subtitles={[
    { src: '/subtitles/de.vtt', label: 'Deutsch', lang: 'de' },
    { src: '/subtitles/en.vtt', label: 'English', lang: 'en' },
  ]}
/>

Monetarisierungspfad und Prüfnotiz

Ein Video-Player kann auch ein Pfad sein, der von kostenlosen Artikeln zu kostenpflichtigem Lernmaterial oder Beratung führt. Natürlich ist ein Ablauf, bei dem du eine kostenlose Vorschau in den Artikel stellst und dem Leser, der mehr verstanden hat, ein Arbeitsblatt, ein Gumroad-Produkt, einen Kurs oder Training und Beratung anbietest. Kein aufdringlicher Verkauf, sondern die Positionierung: „Wer diese Video-UI auf eigenes Lernmaterial, internes Training oder eine Medienseite zuschneiden möchte, ist hier richtig.“

Prüfnotiz: Beim praktischen Test mit diesem Aufbau war es stabiler, isPlaying auf der React-Seite aus den play- und pause-Events zu synchronisieren, statt es beim Buttonklick zu raten. Weil video.play() an der Autoplay-Beschränkung des Browsers scheitern kann, muss man den Leser per try/catch zu einer erneuten Bedienung auffordern. Wenn du Claude Code prüfen lässt, zeig zuerst die drei Punkte „Wird der Zustand aus Media-Events synchronisiert?“, „Lässt sich alles per Tastatur bedienen?“ und „Wird beim ersten Laden nicht das Video selbst heruntergeladen?“ – dann führen die Korrekturen direkt zur Praxis.

Zur Formaten und Encoding von Web-Videos liefern die MDN Web Docs ausführliche Informationen.

#Claude Code #Video-Player #HTML5 #React #Medien-UI
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.