Skip to content

React ​

Das Paket @pollyou/react bindet PollYou-Umfragen als React-Komponente ein. Es kapselt das Web Component <pollyou-survey> aus @pollyou/embed-core und funktioniert mit React 18 und 19, auch mit Server-Side Rendering (Next.js, Remix).

Installation ​

bash
npm install @pollyou/react
# oder
pnpm add @pollyou/react

react und react-dom (Version 18 oder 19) sind Peer-Abhängigkeiten. @pollyou/embed-core wird automatisch mitinstalliert und erst im Browser nachgeladen.

Einbinden ​

tsx
import { PollYouSurvey } from '@pollyou/react'

export function Feedback() {
  return (
    <PollYouSurvey
      surveyId="AbC123xyz0"
      apiBase="https://app.pollyou.org"
      locale="de"
      onComplete={(detail) => console.log('Antwort gespeichert', detail.responseId)}
    />
  )
}

Den passenden Code mit allen gewählten Optionen erzeugt der Einbetten-Dialog im Dashboard (Reiter „React“).

Props ​

PropTypBedeutung
surveyIdstring (Pflicht)Öffentliche ID der Umfrage.
embedIdstringEmbed-Konfiguration (erlaubte Domains, Vorgaben).
apiBasestringOrigin der Collector-API, z. B. https://app.pollyou.org.
mode'inline' | 'popup' | 'slider' | 'fullpage'Darstellung, Standard inline; Overlays sichtbar solange open. Auslöser (Verzögerung, Scrolltiefe …) nur über die Embed-Konfiguration, siehe Overlays.
openbooleanSichtbarkeit des Popups (kontrolliert). Weglassen, wenn usePollYou() das Popup steuert.
localestringSprache, Standard: Sprache der Seite bzw. des Browsers.
theme'auto' | 'light' | 'dark'Farbschema, Standard auto.
showResultsbooleanfalse blendet Ergebnisse aus, auch wenn die Umfrage sie zeigt.
liveResultsbooleanAngezeigte Ergebnisse live aktualisieren.
identityTokenstringSigniertes Identitäts-Token des angemeldeten Nutzers.
metaRecord<string, string | number | boolean>Zusatzdaten zur Antwort (max. 20 Schlüssel).
hiddenRecord<string, string | number | boolean>Versteckte Felder, z. B. { 'user-id': 42 } → hidden-user-id. Schlüssel werden kleingeschrieben.
channelstringKanal in der Auswertung, Standard react.
classNamestringCSS-Klasse des Elements.
styleCSSPropertiesInline-Styles des Elements.
refRef<PollYouSurveyElement>Wird an das Element <pollyou-survey> weitergereicht.

Gestalten lässt sich die Umfrage über das Theme im Dashboard und über ::part()-Selektoren (siehe Einbettung per Script).

Ereignisse ​

Alle Callbacks erhalten (detail, event); event ist das ursprüngliche CustomEvent (pollyou:*).

PropEreignisdetail
onReadypollyou:ready{ surveyId, type, status }
onStartpollyou:start{ surveyId } (erste Antwort)
onAnswerpollyou:answer{ surveyId, questionKey, answer }
onCompletepollyou:complete{ surveyId, responseId, results }
onClosepollyou:close{ surveyId, mode }
onErrorpollyou:error{ surveyId, phase: 'load' | 'submit', status }

Die Callbacks dürfen bei jedem Rendern neue Funktionen sein; die Komponente registriert ihre Listener nur einmal und ruft immer die aktuelle Fassung auf.

Kontrolliert über open und onClose:

tsx
import { useState } from 'react'
import { PollYouSurvey } from '@pollyou/react'

export function FeedbackButton() {
  const [open, setOpen] = useState(false)
  return (
    <>
      <button type="button" onClick={() => setOpen(true)}>
        Feedback geben
      </button>
      <PollYouSurvey
        surveyId="AbC123xyz0"
        mode="popup"
        open={open}
        onClose={() => setOpen(false)}
      />
    </>
  )
}

Oder imperativ mit dem Hook usePollYou():

tsx
import { PollYouSurvey, usePollYou } from '@pollyou/react'

export function FeedbackButton() {
  const survey = usePollYou()
  return (
    <>
      <button type="button" onClick={survey.open} disabled={survey.completed}>
        Feedback geben
      </button>
      <PollYouSurvey ref={survey.ref} surveyId="AbC123xyz0" mode="popup" />
      {survey.lastEvent?.type === 'complete' && <p>Danke!</p>}
    </>
  )
}

usePollYou() liefert ref, element, open(), close(), isOpen, lastEvent ({ type, detail } des letzten pollyou:*-Ereignisses) und completed.

Identitäts-Token ​

Für „eine Antwort pro Kunde“ oder Umfragen nur für angemeldete Nutzer erzeugt Ihr Server ein signiertes Identitäts-Token und reicht es an die Komponente weiter – das Embed-Secret gehört nie in den Browser:

tsx
// Next.js App Router: Server Component
import { SignJWT } from 'jose'
import { Feedback } from './feedback' // Client Component mit <PollYouSurvey>

export default async function Page() {
  const user = await currentUser()
  const token = await new SignJWT({})
    .setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
    .setSubject(String(user.id))
    .setExpirationTime('10m')
    .sign(new TextEncoder().encode(process.env.POLLYOU_EMBED_SECRET))
  return <Feedback identityToken={token} />
}

SSR, Next.js und Remix ​

  • Das Paket ist als Client-Modul markiert ('use client'). In Next.js (App Router) kann <PollYouSurvey> deshalb direkt in Server Components verwendet werden, solange nur serialisierbare Props übergeben werden; Callbacks wie onComplete brauchen eine eigene Client Component.
  • Beim Rendern greift die Komponente auf keine Browser-API zu. Auf dem Server entsteht nur das leere Tag <pollyou-survey> (mit class/style); Attribute, Listener und das Nachladen von @pollyou/embed-core passieren erst im Browser in einem Effect. Eine Hydration-Warnung entsteht dadurch nicht.
  • Für einen festen Platz ohne Layoutsprung empfiehlt sich eine Mindesthöhe, z. B. per className oder style mit minHeight: 320.

React 19 kann Custom Elements selbst mit Properties versorgen; @pollyou/react nutzt das bewusst nicht, sondern setzt Attribute und Listener explizit über eine Ref. Dadurch verhalten sich React 18 und 19 identisch.

Beispiel-App ​

Lauffähige Beispiele liegen im Playground: frontend/apps/playground/react-vite (React 19 + Vite) und frontend/apps/playground/nextjs (Next.js App Router mit 'use client'-Seite). Beide lesen die Umfrage-ID aus der Adresse und protokollieren alle Callbacks.

Fehlersuche ​

  • Die Umfrage bleibt leer oder zeigt „nicht gefunden“: Nur veröffentlichte Umfragen sind öffentlich; ein Entwurf oder eine archivierte Umfrage liefert 404. Das Ereignis onError meldet { phase: 'load', status }.
  • Fehler „blocked by CORS policy“ oder 403 origin_not_allowed: Die Embed-Konfiguration erlaubt nur bestimmte Origins. Tragen Sie die Origin Ihrer Seite ein – mit Schema und Port, also auch den lokalen Dev-Server (z. B. http://localhost:5173). apiBase ist die Origin der API ohne Pfad (https://app.pollyou.org, nicht …/api).
  • Content-Security-Policy: Erlauben Sie die API in connect-src (z. B. connect-src 'self' https://app.pollyou.org, für Live-Ergebnisse zusätzlich den Hub). script-src braucht keinen Eintrag: @pollyou/embed-core ist Teil Ihres Bundles und wird als eigener Chunk von Ihrer Origin nachgeladen. Die Styles setzt das Web-Component per Constructable Stylesheets und CSSOM, 'unsafe-inline' in style-src ist nicht nötig – nur eigenes Theme-CSS wird als <style> eingefügt und fehlt ohne 'unsafe-inline' (die Umfrage funktioniert trotzdem). Das Captcha kommt ohne Worker und eval aus.
  • Lokal in Chrome keine Verbindung zur API: Chrome blockiert mit Local Network Access Anfragen von öffentlichen Seiten an localhost. Die Seite muss selbst lokal laufen (localhost/127.0.0.1), wie die Apps im Playground.
  • Next.js: „Functions cannot be passed directly to Client Components“: Callbacks wie onComplete gehen nicht aus einer Server Component heraus. Legen Sie eine eigene Client Component ('use client') mit <PollYouSurvey> an.
  • Next.js: „useSearchParams() should be wrapped in a suspense boundary“: Wer die Umfrage-ID aus der Query liest, umschließt die Komponente mit <Suspense> (siehe Playground) – sonst bricht next build beim Vorrendern ab.
  • Hydration-Warnungen: Die Komponente rendert auf Server und Client dasselbe leere Element. Warnungen entstehen, wenn eigene Props auf dem Server anders berechnet werden als im Browser (z. B. aus window.location oder Date.now()); solche Werte erst in einem Effect setzen.
  • Monorepo mit verlinkten Quellen: Nur wenn @pollyou/embed-core als TypeScript-Quelle eingebunden ist (wie im PollYou-Repository), braucht Next.js transpilePackages: ['@pollyou/embed-core']. Das veröffentlichte npm-Paket enthält fertiges ESM.