Aller au contenu

Package React

Intégrez Opini directement dans votre app React. Quatre niveaux de style, du prêt à l'emploi au totalement sans interface. Des champs HTML simples, sans composants de champ : votre mise en page reste la vôtre.

Installer

bash
# pnpm
pnpm add @opini-dev/react

# npm
npm install @opini-dev/react

React 18 ou 19 en peer dependency. ESM uniquement. Compatible avec le rendu serveur : les portails du mode flottant ne se montent qu'après le premier effet côté client.

Niveau 1 : prêt à l'emploi

Un seul composant. Les styles par défaut sont actifs, il ressemble donc tout de suite à un widget de feedback.

tsx
import { OpiniWidget } from "@opini-dev/react"

export default function App() {
  return <OpiniWidget projectKey="pk_live_xxx" />
}

Passez unstyled pour partir d'une page blanche.

Niveau 2 : classes par partie

Appliquez des classes Tailwind, CSS Modules ou CSS classique aux parties structurelles. Le style des champs reste dans le HTML de votre formulaire (voir le niveau 3).

tsx
<OpiniWidget
  projectKey="pk_live_xxx"
  unstyled
  className="fixed bottom-6 right-6"
  classNames={{
    trigger: "rounded-full bg-black px-5 py-2.5 text-sm text-white",
    popover: "w-96 rounded-2xl border bg-white p-5 shadow-xl",
    submit: "rounded-md bg-violet-600 px-3 py-2 text-white hover:bg-violet-700",
  }}
/>

Pour des transitions en CSS seul, chaque partie expose data-state="open|closed" et data-loading="true|false". Avec Tailwind, branchez l'entrée et la sortie avec data-[state=open]:scale-100.

Niveau 3 : composants combinables

Utilisez votre propre bouton, vos textes et votre mise en page. Des <input> et <textarea> simples avec name="message", "email" ou "name" forment le contrat des champs.

tsx
import {
  OpiniWidgetRoot,
  OpiniTrigger,
  OpiniPopover,
  OpiniForm,
  OpiniSubmit,
  OpiniSuccess,
} from "@opini-dev/react"

<OpiniWidgetRoot projectKey="pk_live_xxx">
  <OpiniTrigger asChild>
    <MyBrandedButton>Send feedback →</MyBrandedButton>
  </OpiniTrigger>
  <OpiniPopover className="my-popover">
    <h2>Pitch a chapter.</h2>
    <OpiniForm className="space-y-3">
      <textarea
        name="message"
        placeholder="Albums that auto-curate themselves on a trip…"
      />
      <input name="name" placeholder="name (optional)" />
      <input name="email" type="email" placeholder="email" />
      <OpiniSubmit asChild>
        {({ status }) => (
          <MyBrandedButton variant="primary" disabled={status === "submitting"}>
            {status === "submitting" ? "Sending…" : "SEND IT →"}
          </MyBrandedButton>
        )}
      </OpiniSubmit>
    </OpiniForm>
    <OpiniSuccess>You'll hear back. Promise.</OpiniSuccess>
  </OpiniPopover>
</OpiniWidgetRoot>

Niveau 4 : useOpiniSubmit (sans interface)

Quand vous voulez le client d'API et la machine à états sans aucune interface.

tsx
import { useOpiniSubmit } from "@opini-dev/react"

function MyOwnForm() {
  const { submit, status, error } = useOpiniSubmit({ projectKey: "pk_live_xxx" })
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        const fd = new FormData(e.currentTarget)
        void submit({
          text: String(fd.get("message") ?? ""),
          email: fd.get("email") ? String(fd.get("email")) : undefined,
        })
      }}
    >
      <textarea name="message" />
      <input name="email" />
      <button disabled={status === "submitting"}>Send</button>
      {error ? <p>{error.message}</p> : null}
    </form>
  )
}

Props

Toutes les props sont typées : votre éditeur les liste pendant la saisie. Il n'y a pas de prop customCss : ce champ du tableau de bord ne concerne que le widget en balise script. En React, vous stylez avec vos outils habituels.

Noms des parties

La liste complète : trigger, popover, arrow, close, form, actions, submit, success, msg. Chaque partie porte aussi un attribut data-opini-part="…" pour le CSS-in-JS.

Contrat des champs

<OpiniForm> lit le formulaire via FormData à l'envoi. Exactement trois champs sont acceptés :

  • name="message": obligatoire, le texte du retour.
  • name="email": facultatif, validé s'il est présent.
  • name="name": facultatif, texte libre.

Tout le reste est ignoré avant le POST : vous pouvez ajouter vos propres champs d'état sans polluer l'envoi.

SSR

Le contenu des portails du mode flottant ne s'affiche qu'après le premier effet côté client ; le HTML initial ne contient aucun portail. L'hydratation ne fait rien. Le mode inline s'affiche directement dans votre arbre, côté serveur comme côté client.