Appearance
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/reactreact 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
| Prop | Typ | Bedeutung |
|---|---|---|
surveyId | string (Pflicht) | Öffentliche ID der Umfrage. |
embedId | string | Embed-Konfiguration (erlaubte Domains, Vorgaben). |
apiBase | string | Origin 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. |
open | boolean | Sichtbarkeit des Popups (kontrolliert). Weglassen, wenn usePollYou() das Popup steuert. |
locale | string | Sprache, Standard: Sprache der Seite bzw. des Browsers. |
theme | 'auto' | 'light' | 'dark' | Farbschema, Standard auto. |
showResults | boolean | false blendet Ergebnisse aus, auch wenn die Umfrage sie zeigt. |
liveResults | boolean | Angezeigte Ergebnisse live aktualisieren. |
identityToken | string | Signiertes Identitäts-Token des angemeldeten Nutzers. |
meta | Record<string, string | number | boolean> | Zusatzdaten zur Antwort (max. 20 Schlüssel). |
hidden | Record<string, string | number | boolean> | Versteckte Felder, z. B. { 'user-id': 42 } → hidden-user-id. Schlüssel werden kleingeschrieben. |
channel | string | Kanal in der Auswertung, Standard react. |
className | string | CSS-Klasse des Elements. |
style | CSSProperties | Inline-Styles des Elements. |
ref | Ref<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:*).
| Prop | Ereignis | detail |
|---|---|---|
onReady | pollyou:ready | { surveyId, type, status } |
onStart | pollyou:start | { surveyId } (erste Antwort) |
onAnswer | pollyou:answer | { surveyId, questionKey, answer } |
onComplete | pollyou:complete | { surveyId, responseId, results } |
onClose | pollyou:close | { surveyId, mode } |
onError | pollyou: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.
Popup
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 wieonCompletebrauchen eine eigene Client Component. - Beim Rendern greift die Komponente auf keine Browser-API zu. Auf dem Server entsteht nur das leere Tag
<pollyou-survey>(mitclass/style); Attribute, Listener und das Nachladen von@pollyou/embed-corepassieren 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
classNameoderstylemitminHeight: 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
onErrormeldet{ 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).apiBaseist 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-srcbraucht keinen Eintrag:@pollyou/embed-coreist 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'instyle-srcist 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 undevalaus. - 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
onCompletegehen 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 brichtnext buildbeim 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.locationoderDate.now()); solche Werte erst in einem Effect setzen. - Monorepo mit verlinkten Quellen: Nur wenn
@pollyou/embed-coreals TypeScript-Quelle eingebunden ist (wie im PollYou-Repository), braucht Next.jstranspilePackages: ['@pollyou/embed-core']. Das veröffentlichte npm-Paket enthält fertiges ESM.