Menu
Examples and frameworks

React

Install a LittleReply form with React.

Integration approach

Use React to render or enhance the public form while LittleReply handles the published schema, durable intake, private inbox, and configured destinations.

Before copying code

  1. Create and publish the form with the exact field names used by the example.
  2. Copy its public key and configure the deployed origin.
  3. Keep recipients and private credentials out of browser code.

Example to adapt

Replace the example key and endpoint, then adapt labels and styles without changing transport names accidentally.

React / JSX
// Browser-side React component
import { useState, useRef } from 'react';
export default function Contact() {
  const [etat, setEtat] = useState('');
  const [busy, setBusy] = useState(false);
  const tentative = useRef({ contenu: '', id: '' });
  async function envoyer(event) {
    event.preventDefault();
    const form = event.currentTarget;
    if (!form.reportValidity()) return;
    const data = new FormData(form);
    const fields = { name: data.get('name'), email: data.get('email'), message: data.get('message') };
    const contenu = JSON.stringify(fields);
    if (contenu !== tentative.current.contenu) tentative.current = { contenu, id: crypto.randomUUID() };
    setBusy(true); setEtat("Sending…");
    try {
      const response = await fetch("https://littlereply.com/api/submit", {
        method: 'POST', headers: { 'Content-Type': 'application/json', 'Idempotency-Key': tentative.current.id },
        body: JSON.stringify({ form_key: "YOUR_ACCESS_KEY", fields })
      });
      const result = await response.json();
      if (!response.ok) throw new Error(response.status === 429 ? "Limit reached. Try again later." : "Could not submit. Check the fields and try again.");
      setEtat("Your message was saved. Email delivery is tracked separately.");
    } catch (error) { setEtat(error instanceof Error ? error.message : "Could not submit."); }
    finally { setBusy(false); }
  }
  return <form onSubmit={envoyer}>
    <label>Your name<input name='name' autoComplete='name' required /></label>
    <label>Your email<input name='email' type='email' autoComplete='email' required /></label>
    <label>Your message<textarea name='message' required maxLength={5000} /></label>
    <button disabled={busy}>Send message</button>
    <p role='status' aria-live='polite'>{etat}</p>
  </form>;
}

Verify the result

Check the browser request and response, find the stored submission, then inspect delivery history. Repeat the test on the actual deployed hostname.

Documentation reviewed on October 20, 2026Suggest an improvement →