Use Cases (Mis à jour: 02/06/2026)

Lecteur vidéo avec Claude Code

Construire un lecteur vidéo React avec Claude Code : accessibilité, modes de diffusion et tunnel de conversion inclus.

Lecteur vidéo avec Claude Code

Édition 2026 : qu’est-ce qu’un lecteur vidéo ?

Un lecteur vidéo est une interface qui charge un fichier ou un flux vidéo et permet au lecteur de contrôler la lecture, la pause, la recherche, le volume, les sous-titres et la vitesse. Sur le web, on place le plus souvent un élément natif <video> comme socle, puis on lit et on met à jour depuis JavaScript les propriétés de HTMLMediaElement : currentTime, duration, paused, volume, muted et playbackRate.

Je pose cette définition en premier parce qu’un lecteur vidéo n’est pas une simple décoration. Dans un produit de formation, le taux de complétion dépend directement de la possibilité de reprendre en cours de route, de comprendre grâce aux sous-titres et de réviser à vitesse réduite. Sur un site média, ce qui compte est de ne pas gêner l’affichage initial de l’article, de ne pas faire attendre sur réseau mobile et d’amener naturellement vers l’article suivant ou l’inscription une fois la vidéo terminée. Pour une démo produit SaaS, l’expérience de lecture elle-même construit la confiance.

Pendant l’implémentation, consultez la référence de l’élément <video> et l’API HTMLMediaElement sur MDN. Claude Code est efficace pour mettre en place d’un seul tenant le composant, les tests, la revue d’accessibilité et les événements de mesure. En revanche, décider « quelle expérience protéger » reste une décision côté produit. Commencez par le lecteur minimal dont vos lecteurs ont réellement besoin.

Pour des implémentations voisines, regardez aussi le lecteur audio avec Claude Code si vous ne traitez que le son, l’accessibilité avec Claude Code pour le clavier et les sous-titres, et l’optimisation des performances avec Claude Code pour la vitesse de chargement : la conception dérivera moins. Si vous voulez transformer un tunnel pédagogique ou média en chiffre d’affaires, placez la page formation et conseil comme CTA, puis passez à la revue d’implémentation et à la conception opérationnelle.

Différence entre <video> natif, contrôles personnalisés et streaming

La première décision n’est pas l’apparence des boutons mais le mode de lecture à adopter. Le <video controls> natif se publie le plus vite et le navigateur prend en charge le clavier et l’affichage de base des sous-titres. À l’inverse, si vous avez besoin de gestion d’apprentissage, de sauvegarde de progression, de CTA réservés aux membres, de chapitrage ou d’une mesure sur mesure, des contrôles personnalisés qui pilotent HTMLMediaElement conviennent mieux. Quand les vidéos sont longues, l’audience nombreuse ou les écarts de pays et de débit importants, envisagez aussi HLS, DASH ou le streaming via un service de diffusion vidéo.

ModeCas adaptésPoints à surveiller en production
<video controls> natifCourt intégré dans un article, documentation interne, landing page simpleImplémentation rapide. L’expression de marque et la mesure détaillée sont limitées, mais la fiabilité des contrôles de base est élevée.
Contrôles personnalisés sur HTMLMediaElementProduit de formation, site média, démo SaaS, vidéo réservée aux membresVous maîtrisez l’UI, la position de reprise, l’affichage des CTA et l’analytique. En contrepartie, vous portez aussi la responsabilité de l’accessibilité et de la gestion d’erreurs.
HLS/DASH ou plateforme de diffusionCours longs, gros catalogue, direct, vidéo à protégerIl faut concevoir la transcodage, le manifeste, le CDN, le débit, l’autorisation et la bibliothèque de lecture.

Pour une première implémentation avec Claude Code, le réaliste est de partir d’un court MP4, de preload="metadata", d’une image poster et de fichiers de sous-titres. Personnalisez quand apparaissent les liens de chapitre, la recherche dans les sous-titres, le taux de complétion, l’affichage réservé aux acheteurs Gumroad ou l’intégration SSO interne, puis passez au streaming une fois qu’un seul MP4 montre ses limites.

Tableau d’architecture

CoucheRôleCe qu’il faut faire vérifier à Claude Code
Gestion des assetsPréparer MP4/WebM, image poster, sous-titres et manifeste de streamingVérifier URL, type MIME, CORS, URL à durée limitée et texte alternatif.
Élément média natifConfier au navigateur la lecture, le chargement, le temps, les sous-titres et les erreursVérifier preload, playsInline, track, le message de repli et l’abonnement aux événements.
Gestion d’étatRefléter dans React currentTime, duration, paused, muted, volume et la vitesseS’assurer que l’état est synchronisé depuis les événements média et non changé par supposition.
UI de contrôleFournir bouton de lecture, barre de recherche, volume, vitesse, sous-titres et plein écranUtiliser button et input, conserver le clavier et les libellés.
État persistantSauvegarder la position de reprise, l’état de complétion, la vitesse et le réglage muetMinimiser le périmètre sauvegardé et prévoir une mention de confidentialité sur un site public.
MesureSuivre le démarrage, 25/50/75 %, la complétion, les erreurs et les clics CTANe pas envoyer un événement par seconde et ne garder que les indicateurs utiles à l’amélioration.
PerformanceRégler poster, CDN, chargement paresseux et débitVérifier le CLS, le transfert initial, le réseau mobile et les en-têtes de cache.

Avec ce découpage, les demandes à Claude Code deviennent concrètes. « Ne revois que l’accessibilité de la barre de recherche », « corrige seulement la taille du poster et sa stratégie de chargement », « teste que l’événement de complétion n’est pas envoyé deux fois » : comme on peut découper en petites tâches, on évite qu’une correction d’UI vidéo n’entraîne par accident toute la plateforme de diffusion ou la conception analytique.

Cas d’usage réels

Le cas 1 est la leçon d’un cours payant. L’apprenant s’absente en cours, revient sur un autre appareil et révise à 1,25x. Ce dont il a besoin, ce n’est pas une jolie animation mais des sous-titres, une position de reprise, une condition de complétion et un lien vers la leçon suivante. L’événement de complétion s’envoie non pas à l’affichage de la page, mais au moment où une certaine proportion a été visionnée.

Le cas 2 est la vidéo intégrée dans un article média. Le lecteur décide de regarder la vidéo tout en lisant le texte. Placez l’image poster et la transcription à proximité, et ne chargez pas lourdement la vidéo elle-même tant qu’elle n’est pas nécessaire. Au lecteur qui a fini la vidéo, proposez un lien vers les articles liés, la newsletter, l’inscription ou un rapport payant.

Le cas 3 est la démo produit SaaS. En adaptant les chapitres par fonctionnalité, le lien vers la page tarifs, le lien vers la documentation API et le bouton de contact à la position de visionnage, une simple vidéo devient une interface d’explication d’avant-vente. Plus que la complétion, ce qui compte est de savoir quel chapitre a été vu et quel CTA a été cliqué.

Le cas 4 est la formation interne et le support. Pour mettre en vidéo des exemples de discours commercial, d’onboarding, de conformité et de traitement des demandes, ce qui importe est la lecture stable sous SSO, les sous-titres, le journal d’audit et le message en cas d’erreur. Plus que l’apparence, la valeur réside dans la capacité à vérifier, au strict minimum, qui a vu jusqu’où.

Code React/TypeScript prêt à copier-coller

Le composant suivant est une configuration minimale que vous pouvez poser telle quelle dans Vite, Next.js ou une island React d’Astro. Il ne dépend d’aucune bibliothèque de lecteur externe et utilise l’élément natif <video> et les événements de HTMLMediaElement.

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>
  );
}

Pièges d’accessibilité et de performance

Le plus grand piège est d’avoir masqué les contrôles natifs sans avoir rétabli une maniabilité équivalente. Fabriquer le bouton de lecture avec un div, une barre de recherche sans libellé, une vitesse non modifiable au clavier, une absence de sous-titres : ces implémentations ont beau être jolies, elles ne conviennent pas à un produit de formation. La base est un vrai button pour le bouton, un input type="range" pour la recherche, et un role="alert" pour signaler les erreurs.

Côté performance, charger simultanément plusieurs vidéos dans une liste d’articles ou une landing page est dangereux. Donnez une largeur et une hauteur à l’image poster, mettez les vidéos à visionnage facultatif en preload="metadata" et diffusez la vidéo elle-même depuis un CDN. Distribuer un cours long via un seul énorme MP4 vous désavantage sur tous les fronts : réseau mobile, visionnage à l’étranger et abandon en cours de route.

Les échecs de mesure sont aussi fréquents. Plutôt que d’envoyer un événement par seconde, il est plus exploitable de se limiter au démarrage, 25 %, 50 %, 75 %, complétion, erreur et clic CTA. Pour un service de formation, il faut regarder « quelles leçons ont un faible taux de complétion » ; pour un site média, « le visiteur a-t-il poursuivi vers l’inscription après la vidéo ».

Checklist de déploiement

  • J’ai vérifié les libellés pour le clavier, le tactile, la souris et le lecteur d’écran.
  • J’ai prévu des sous-titres ou une transcription et n’ai pas enfermé une information clé dans la seule vidéo.
  • Sur mobile, playsInline fonctionne et aucun passage en plein écran involontaire ne se produit.
  • J’ai fixé le ratio et la taille de l’image poster et n’ai pas provoqué de CLS.
  • Je n’ai pas mis preload="auto" sur une vidéo inutile à l’affichage initial.
  • J’ai testé l’expiration d’URL, l’absence de sous-titres, le réseau lent et le blocage de l’autoplay.
  • J’ai fixé les noms de mesure pour le démarrage, la progression, la complétion, l’erreur et le clic CTA.
  • J’ai prévu une procédure de repli vers les controls natifs si les contrôles personnalisés cassent.

Tunnel de conversion et note de vérification

Un lecteur vidéo peut aussi devenir le tunnel qui relie un article gratuit à un support payant ou à une consultation. Le flux naturel consiste à poser un aperçu gratuit dans l’article, puis à orienter le lecteur dont la compréhension a progressé vers une fiche de travail, un produit Gumroad, un cours ou la formation et le conseil. Ce n’est pas de la vente forcée : c’est se positionner comme « voici pour qui veut concevoir cette UI vidéo pour ses propres supports, sa formation interne ou son site média ».

Note de vérification : après avoir réellement essayé cette configuration, synchroniser l’état isPlaying côté React depuis les événements play et pause s’est révélé plus stable que de le changer par supposition au moment du clic sur le bouton. Comme video.play() peut échouer à cause de la restriction d’autoplay du navigateur, il faut un try/catch pour inviter le lecteur à réessayer. Si vous faites relire par Claude Code, montrez-lui d’abord ces trois points — « l’état est-il synchronisé depuis les événements média », « toutes les commandes sont-elles accessibles au clavier », « la vidéo elle-même est-elle évitée au chargement initial » — et les corrections colleront directement à la réalité du terrain.

#Claude Code #lecteur vidéo #React #accessibilité #UI média
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.