Skip to content
Working With React

Handling errors in React

Handle Qubeforms API errors in React forms with validation feedback and preserved input. Manage rate limits, network failures, duplicate submissions, and retries.

1. Separate local validation from API errors

Start with A React form component. Browser required and type="email" validation run before its submit handler for normal form submission. Add your own local checks for file count and size if needed, but keep the server response authoritative. Track pending and feedback state separately so a failed request leaves the visitor’s fields available.

2. Parse responses defensively

Keep existing field values after an error and clear them only after response.ok and result.ok === true. Use error.message for server feedback and render it as text, not HTML. The API does not promise a per-field error map, so do not assume error.details always contains field names. A proxy may return HTML or an empty body; guard JSON parsing and retain a fallback message. A network exception does not prove that the submission failed to arrive.

const result = await response.json().catch(() => null);
if (!response.ok || result?.ok !== true) {
  setFeedback(result?.error?.message || `Could not confirm receipt (HTTP ${response.status}).`);
  return;
}
// Reset fields only in the confirmed-success branch.

3. Handle common status codes

Use the HTTP status and message together. Do not relabel every failure as a CORS problem or every 422 as an email-field error. A redirect-validation error can occur after storage, so inspect the inbox before repeating a submission with a corrected redirect.

402  The plan does not allow this feature, such as file uploads.
403  Origin or access restriction.
404  Form or requested resource unavailable.
413  Request or payload too large.
422  Validation, CAPTCHA, attachment, or redirect error; inspect error.message.
429  Rate, storage, or usage limit; follow Retry-After when present.
500  Server error; receipt may be uncertain.
503  Service or storage unavailable.

4. Respect throttling

The standard submission limit is 20 attempts per form/IP per 10 minutes; advanced-plan controls can change it. Other limits can also return 429. Read Retry-After when present; Qubeforms returns a delay in seconds. Show that delay and prevent an immediate retry in your UI if you implement a cooldown. The basic React component displays it but does not implement a timer. Do not start a tight automatic retry loop.

5. Avoid duplicate submissions

The component’s ref and disabled fieldset prevent double clicks during a request. For deliberate retry support, send an Idempotency-Key generated once for the same logical submission and reuse it only when retrying the unchanged payload. Qubeforms returns the existing record for a reused key; changed payloads are not compared and may therefore be ignored. Clear the key for a new submission or after edits. Every attempt still consumes rate-limit capacity. Do not generate a new key on each automatic retry or submit on every render.

6. Handle uncertain outcomes and unmounts

If a timeout, cancellation, or network loss occurs, the server may already have stored the submission. Do not tell the visitor that cancelling removed it. Preserve the entered data while offering a clear next step and check for receipt before repeating the request. If you add AbortController on unmount, treat abort as an uncertain outcome, not an undo operation. Keep server messages in {feedback} or another text node rather than dangerouslySetInnerHTML.

7. Test the failure states

Test offline mode, a rejected origin, invalid CAPTCHA, an oversized file, a non-JSON error response, and rate limiting. Confirm that the button unlocks, entered values remain, and a successful response resets only once. A success can still be classified as Spam; do not infer delivery from the HTTP status alone.

Keep private credentials private

Public form keys can appear in browser code. Read keys, management credentials, and webhook signing secrets must stay on the server.