Intégration React

Composant React réutilisable pour le widget Ekoo

Le widget charge automatiquement sa configuration depuis le backoffice. Les attributs d'apparence sont des surcharges optionnelles — inutile de les définir si le backoffice est configuré.
Attributs principaux
data-ekooReq
string
UUID du site Ekoo. Visible dans le backoffice → Paramètres du site.
data-ekoo-product-idReq
string
Référence produit (doit correspondre exactement au catalogue Ekoo).
data-ekoo-locale
string·default: auto
Code langue : fr, en, es, it, de, ar, cn, tw, hk, jp, kr, nl, tr, pl, pt, lu, be, ru. "auto" = détection navigator.language.
data-ekoo-variant
string
Référence stable d’une configuration widget. Le produit, ses audios et ses reviewers restent pilotés par data-ekoo-product-id ; seul l’habillage change.
data-ekoo-review-id
string
ID d'un avis audio spécifique. Si omis, le premier avis publié est utilisé.
data-ekoo-on-event
string (fn name)
Nom d'une fonction globale window appelée à chaque événement widget (printed, played-0, played-25…).
Apparence — surcharges backoffice
data-ekoo-direction
normal | reverse·default: normal
Sens d'expansion. normal = gauche→droite, reverse = droite→gauche.
data-ekoo-scale
number·default: 1
Facteur de zoom (ex: "1.2" pour 20% plus grand).
data-ekoo-animation
string·default: pulse
Type d'animation de l'icône au repos.
data-ekoo-animation-duration
string·default: continuous
Durée de l'animation.
data-ekoo-always-open
boolean·default: false
Si "true", le widget reste toujours déplié.
data-ekoo-show-image
boolean·default: true
Afficher ou masquer l'image produit dans le widget.
data-ekoo-not-fully-clickable
boolean·default: false
Si "true", seul le bouton lecture est cliquable.
data-ekoo-autoplay
boolean·default: false
Lecture audio automatique au chargement.
data-ekoo-show-transcript
boolean·default: false
Afficher un bouton pour lire la transcription.
data-ekoo-show-speed-button
boolean·default: false
Afficher un contrôle de vitesse de lecture.
data-ekoo-closed-state-main-text
string
Texte CTA principal affiché quand le widget est replié.
data-ekoo-closed-state-secondary-text
string
Texte secondaire sous le CTA quand le widget est replié.
SPA & Shadow DOM
data-ekoo-mode
spa | static·default: auto
Force le mode rendu. Auto-détecté (Next.js, Nuxt, React, Vue, Angular, Sapper). À utiliser seulement si la détection auto échoue.
data-shadow-mode
open | closed·default: open
"open" (par défaut) permet l'inspection, l'accès CSS externe et le tracking analytics. Définir à "closed" pour isoler complètement le widget.
Config JS globale
window.EKOO_FORCE_SPA = true
Force le mode SPA globalement (alternative à data-ekoo-mode="spa" sur chaque widget).
window.ekooShadowMode = "open"
Shadow DOM mode global (alternative à data-shadow-mode sur chaque widget).

1. Déclarations TypeScript

Ajoutez les types pour les fonctions globales Ekoo afin d'éviter les erreurs TypeScript :

types/ekoo.d.ts
typescript
1declare global {
2 interface Window {
3 ekooLoad?: () => void
4 ekooUnload?: () => void
5 ekooReload?: () => void
6 }
7}
8
9export {}

2. Composant EkooWidget

components/EkooWidget.tsx
tsx
1'use client' // Next.js App Router only
2
3import { useEffect, useRef } from 'react'
4
5const EKOO_SCRIPT_URL = 'https://app.ekoo.co/widgets/widget-4.0.0-standalone.js'
6
7interface EkooWidgetProps {
8 websiteId: string
9 productId: string
10 locale?: string
11}
12
13export function EkooWidget({
14 websiteId,
15 productId,
16 locale = 'fr',
17}: EkooWidgetProps) {
18 const containerRef = useRef<HTMLDivElement>(null)
19
20 useEffect(() => {
21 // Load the script if not already present
22 let script = document.querySelector<HTMLScriptElement>(
23 `script[src="${EKOO_SCRIPT_URL}"]`
24 )
25
26 if (!script) {
27 script = document.createElement('script')
28 script.src = EKOO_SCRIPT_URL
29 script.defer = true
30 document.head.appendChild(script)
31 }
32
33 // Initialize the widget once the script is loaded
34 const init = () => window.ekooLoad?.()
35
36 if (script.dataset.loaded === 'true') {
37 init()
38 } else {
39 script.addEventListener('load', () => {
40 script!.dataset.loaded = 'true'
41 init()
42 })
43 }
44
45 // Cleanup on unmount
46 return () => {
47 window.ekooUnload?.()
48 }
49 }, [websiteId, productId, locale])
50
51 return (
52 <div
53 ref={containerRef}
54 data-ekoo={websiteId}
55 data-ekoo-product-id={productId}
56 data-ekoo-locale={locale}
57 />
58 )
59}

3. Utilisation

app/product/[id]/page.tsx
tsx
1import { EkooWidget } from '@/components/EkooWidget'
2
3export default function ProductPage({ params }: { params: { id: string } }) {
4 return (
5 <main>
6 <h1>My product</h1>
7 <EkooWidget
8 websiteId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
9 productId={params.id}
10 locale="fr"
11 />
12 </main>
13 )
14}

4. Navigation SPA (React Router)

Dans une SPA, le composant se monte et se démonte au changement de route. Le useEffect du composant ci-dessus gère déjà ce cas :

  • Au montage : ekooLoad() initialise le widget.
  • Au démontage : ekooUnload() nettoie les listeners.
⚠️

Ré-initialisation au changement de route

Si le productId change sans démontage du composant (ex. navigation entre deux produits sur la même route), appelez window.ekooReload() pour rafraîchir le widget. Le composant ci-dessus le gère via la dépendance productId dans le useEffect.

5. Notes pour Next.js

  • App Router : ajoutez 'use client' en haut du fichier composant (déjà fait dans l'exemple).
  • Pages Router : utilisez dynamic(() => import(...), { ssr: false }) pour désactiver le SSR si vous rencontrez des erreurs window is not defined.
  • Le script Ekoo manipule le DOM — il doit s'exécuter côté client uniquement.
Import dynamique (Pages Router)
tsx
1import dynamic from 'next/dynamic'
2
3const EkooWidget = dynamic(
4 () => import('@/components/EkooWidget').then(m => m.EkooWidget),
5 { ssr: false }
6)
7
8export default function ProductPage() {
9 return <EkooWidget websiteId="..." productId="..." />
10}

Aller plus loin

React — Documentation — Ekoo