Retour au catalogue

Blog Audio Player

Blog format podcast avec barres de forme d'onde audio, lecture/pause et liste d'episodes.

blogmedium Both Responsive a11y
boldplayfuluniversaleducationsaasstacked
Theme

Créer un lecteur podcast pour blog avec barres de waveform animées en React

Un lecteur podcast React pour blog affiche des barres de waveform animées via Framer Motion, un bouton lecture/pause qui bascule les barres d'hauteurs statiques vers des keyframes en boucle, et une liste d'épisodes scrollable, le tout thémé par des propriétés CSS custom pour s'adapter à n'importe quelle palette sans toucher au composant.

  • Stack : React 18 + Framer Motion 11 + Lucide React, ~140 lignes, zéro dépendance à l'API Audio.
  • 40 barres de waveform avec hauteurs aléatoires générées une seule fois via useMemo ; chaque barre s'anime indépendamment lors de la lecture.
  • Accessible : le bouton lecture/pause porte un aria-label qui se met à jour avec l'état.
  • Entièrement thémé : toutes les couleurs viennent de propriétés CSS custom (--color-accent, --color-border, etc.), compatible avec les 7 presets.
  • La waveform est purement visuelle, branche un vrai élément <audio> ou un hook Web Audio API pour ajouter la lecture réelle.

Blog Audio Player est une section React qui donne vie à un flux podcast avec une waveform animée, une carte d'épisode en vedette et une liste compacte d'épisodes en dessous. Les barres de waveform passent de hauteurs figées à des animations en boucle asynchrones à l'instant où l'utilisateur appuie sur lecture, offrant un retour visuel immédiat sans une seule ligne de keyframe CSS écrite à la main.

Anatomie

Le composant comporte trois zones visuelles. En haut, un header centré avec un badge pill (icône Headphones + libellé "Podcast"), un titre de podcast et une courte description. En dessous, une carte d'épisode en vedette avec les métadonnées invité/date, un titre et une description, puis une rangée horizontale avec le bouton lecture/pause, le sous-composant WaveformBars et la durée de l'épisode. En bas, une liste verticale scrollable d'épisodes plus petits, chacun avec une icône Play circulaire, un titre d'épisode, une durée et une date.

Comment ça marche

Le sous-composant WaveformBars reçoit un seul booléen `playing`. Au montage, useMemo génère un tableau de 40 hauteurs aléatoires (entre 20 et 100% de 48px) qui ne changent jamais entre les rendus, assurant un aspect cohérent. Quand `playing` est false, chaque barre se rend à sa hauteur fixe via une transition de 0,3s. Quand `playing` passe à true, la prop `animate` de chaque barre reçoit un tableau de trois keyframes de hauteur et une durée aléatoire entre 0,4s et 0,8s avec `repeat: Infinity, repeatType: "reverse"`, créant l'impulsion waveform décalée et organique. Les barres dans les 40 premiers pourcents du tableau utilisent `--color-accent` pour suggérer la progression "déjà écouté", les autres `--color-border`.

Comment le coder en React

  1. Construire le sous-composant WaveformBars

    Crée un composant qui accepte `playing` et un `barCount` optionnel (défaut 40). Utilise useMemo pour générer le tableau de hauteurs aléatoires une seule fois. Itère dessus et rends une motion.div par barre, en donnant à chacune un `minWidth: 2` et `maxWidth: 6` fixes pour que les barres se serrent dans l'espace disponible.

    const heights = useMemo(
      () => Array.from({ length: barCount }, () => 20 + Math.random() * 80),
      [barCount]
    );
  2. Basculer entre l'état statique et l'état animé

    Passe des props `animate` différentes selon le flag `playing`. Quand false, anime vers la hauteur pixel fixe. Quand true, passe un tableau de trois keyframes pour que Framer Motion le boucle automatiquement. Chaque barre reçoit une durée aléatoire pour rompre la synchronie visuelle.

    animate={playing
      ? { height: [h * 0.3, h * 0.01 * 48, h * 0.5 * 0.01 * 48] }
      : { height: h * 0.01 * 48 }
    }
    transition={playing
      ? { duration: 0.4 + Math.random() * 0.4, repeat: Infinity, repeatType: "reverse" }
      : { duration: 0.3 }
    }
  3. Assembler la carte d'épisode en vedette

    Rends le nom de l'invité, la date, le titre et la description de l'épisode au-dessus de la rangée du lecteur. La rangée du lecteur est un conteneur flex : le bouton lecture/pause circulaire en accent à gauche, WaveformBars avec `flex: 1` au centre, et la durée à droite. Gère l'état de lecture avec un seul useState booléen que tu bascules au clic.

    const [playing, setPlaying] = useState(false);
    // ...
    <button onClick={() => setPlaying(!playing)} aria-label={playing ? "Pause" : "Lecture"}>
      {playing ? <Pause /> : <Play />}
    </button>
    <WaveformBars playing={playing} />
    <span>{feat.duration}</span>
  4. Ajouter la liste d'épisodes avec animations d'entrée décalées

    Itère sur le tableau d'épisodes et rends chacun sous forme de motion.button (pour l'accessibilité clavier). Définis `initial={{ opacity: 0, y: 10 }}` et `whileInView={{ opacity: 1, y: 0 }}` avec un délai de `0.12 + index * 0.05` secondes pour que les rangées s'enchaînent à mesure que l'utilisateur fait défiler vers la section.

Quand l'utiliser

À utiliser sur des blogs ou sites éditoriaux qui publient du contenu audio : landing pages de podcast, newsletters axées interviews, ou marques médias qui veulent mettre en avant des épisodes sans renvoyer les lecteurs vers Spotify. Il s'insère bien entre un article phare et un CTA newsletter. Passe ton chemin si ton site n'a aucun contenu audio, et rappelle-toi que la waveform est décorative, branche une vraie source audio avant de mettre en production.

Utilisé par

  • Spotify, Des scrubbers style waveform et des barres animées signalent l'état de lecture sur son lecteur web et ses pages podcast.
  • Transistor, Lecteurs podcast intégrables avec aperçus waveform animés utilisés par des milliers de podcasteurs indépendants.
  • Substack, Lecteurs audio intégrés aux articles avec une UI waveform minimaliste permettent aux auteurs de publier des éditions parlées de leurs newsletters.
  • Overcast, Des cartes d'épisode compactes avec métadonnées (durée, date, invité) reproduisent le schéma de mise en page de la liste d'épisodes du composant.

FAQ

Ce composant lit-il vraiment de l'audio ?

Non, la waveform et l'état lecture/pause sont purement visuels. Pour ajouter une lecture réelle, attache un élément HTML <audio> (ou l'API Web Audio) et synchronise ses appels `play()`/`pause()` avec le même état booléen qui pilote l'animation de la waveform.

Pourquoi utiliser useMemo pour les hauteurs des barres ?

Sans useMemo, les hauteurs aléatoires se régénéreraient à chaque rendu, faisant sauter les barres vers de nouvelles positions à chaque re-rendu du parent. Les mémoriser une seule fois garantit une forme de waveform stable et cohérente pendant toute la durée de vie du composant.

Comment changer la couleur de la waveform pour coller à ma marque ?

Les barres lisent `--color-accent` (portion jouée) et `--color-border` (portion non jouée) depuis des propriétés CSS custom. Redéfinis ces deux variables sur ton élément wrapper ou bascule vers un autre preset de thème et la waveform se met à jour automatiquement.

Peut-on réduire le nombre de barres de waveform pour une UI plus légère ?

Oui. Passe `barCount={20}` (ou n'importe quel nombre) à WaveformBars. Moins de barres donnent un aspect plus épuré et minimal ; plus de barres produisent une waveform plus dense. La mise en page flex du composant adapte automatiquement la largeur des barres pour remplir l'espace disponible.

"use client";

import { useState, useMemo } from "react";
import { motion } from "framer-motion";
import { Play, Pause, Headphones, Clock, Mic } from "lucide-react";

interface FeaturedEpisode {
  title: string;
  description: string;
  duration: string;
  date: string;
  guest: string;
}

interface Episode {
  title: string;
  duration: string;
  date: string;
}

interface BlogAudioPlayerProps {
  podcastName?: string;

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Avis

Lecteur podcast React avec waveform animée, Tutoriel