@flowqen-forms/react
React hooks and components for Flowqen forms. Build type-safe, accessible forms with built-in validation, submission handling, and error display — no boilerplate required.
Installation
Install the package from npm.
npm install @flowqen-forms/react
Or with other package managers:
yarn add @flowqen-forms/reactpnpm add @flowqen-forms/react
Quick start
A complete contact form in under 30 lines. The useForm hook handles submission, loading state, and server-side validation errors.
import { useForm, ValidationError } from "@flowqen-forms/react";function ContactForm() {const { state, handleSubmit, getFieldProps } = useForm("YOUR_FORM_ID");if (state.succeeded) {return <p>Thanks for your message! We'll be in touch.</p>;}return (<form onSubmit={handleSubmit}><label htmlFor="email">Email</label><inputid="email"{...getFieldProps("email", { required: true, type: "email" })}/><ValidationError field="email" errors={state.errors} /><label htmlFor="message">Message</label><textarea id="message" {...getFieldProps("message")} /><ValidationError field="message" errors={state.errors} /><button type="submit" disabled={state.submitting}>{state.submitting ? "Sending..." : "Send"}</button></form>);}
API Reference
useForm(formId, options?)
The main hook. Pass your Flowqen form ID and an optional config object. Returns everything you need to render and submit a form.
Parameters
| Param | Type | Description |
|---|---|---|
| formId | string | Required. Your Flowqen form ID (from the dashboard). |
| options.endpoint | string | Override the API endpoint. Defaults to https://flowqen.com/api/f/{formId}. |
| options.onSuccess | (data) => void | Callback fired after a successful submission. Receives the API response. |
| options.onError | (errors) => void | Callback fired when the submission fails or returns validation errors. |
| options.data | Record<string, string> | Extra hidden fields to include with every submission (e.g. page URL, user ID). |
Return value
| Property | Type | Description |
|---|---|---|
| state.submitting | boolean | true while the form is being submitted. |
| state.succeeded | boolean | true after a successful submission. Use this to show a thank-you message. |
| state.errors | FieldError[] | Array of validation errors returned by the server. Pass to ValidationError. |
| handleSubmit | (e: FormEvent) => void | Pass to your <form onSubmit>. Prevents default, serializes fields, and posts to Flowqen. |
| getFieldProps | (name, opts?) => props | Returns name, id, type, required, and aria-describedby props for an input. |
| reset | () => void | Resets the form state (clears errors, succeeded, and submitting flags). |
| submitData | (data: object) => Promise | Submit data programmatically without a form element. Useful for custom UIs or multi-step flows. |
<ValidationError>
Renders inline validation errors for a specific field. Automatically matches errors from state.errors by field name. Renders nothing when there are no errors for the field.
| Prop | Type | Description |
|---|---|---|
| field | string | Required. The name of the field to show errors for. |
| errors | FieldError[] | Required. Pass state.errors from useForm. |
| className | string | Optional CSS class for custom styling. |
| as | string | Component | Wrapper element. Defaults to span. |
<input {...getFieldProps("email", { required: true, type: "email" })} /><ValidationErrorfield="email"errors={state.errors}className="text-red-500 text-sm"/>
<FlowqenProvider> Optional
Wrap your app in FlowqenProvider to set a default form ID or endpoint for all nested useFormcalls. This is optional — you can always pass the form ID directly to the hook.
import { FlowqenProvider } from "@flowqen-forms/react";function App() {return (<FlowqenProvider formId="YOUR_FORM_ID"><ContactForm /></FlowqenProvider>);}
Next.js example
Since useForm uses React hooks, you need the "use client" directive in Next.js App Router.
"use client";import { useForm, ValidationError } from "@flowqen-forms/react";export default function ContactPage() {const { state, handleSubmit, getFieldProps } = useForm("YOUR_FORM_ID");if (state.succeeded) {return (<div className="text-center py-12"><h2>Message sent!</h2><p>We'll get back to you soon.</p></div>);}return (<form onSubmit={handleSubmit} className="max-w-md mx-auto space-y-4"><div><label htmlFor="email">Email</label><inputid="email"{...getFieldProps("email", { required: true, type: "email" })}className="w-full border rounded px-3 py-2"/><ValidationError field="email" errors={state.errors} /></div><div><label htmlFor="message">Message</label><textareaid="message"{...getFieldProps("message")}className="w-full border rounded px-3 py-2"rows={4}/><ValidationError field="message" errors={state.errors} /></div><buttontype="submit"disabled={state.submitting}className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 disabled:opacity-50">{state.submitting ? "Sending..." : "Send Message"}</button></form>);}
Programmatic submission
Use submitData to submit form data without a traditional <form> element. This is useful for custom UIs, multi-step wizards, or chatbot flows.
import { useForm } from "@flowqen-forms/react";function FeedbackWidget() {const { state, submitData } = useForm("YOUR_FORM_ID");const sendFeedback = (rating: number) => {submitData({rating: String(rating),page: window.location.href,});};if (state.succeeded) return <p>Thanks for your feedback!</p>;return (<div><p>How would you rate this page?</p>{[1, 2, 3, 4, 5].map((n) => (<buttonkey={n}onClick={() => sendFeedback(n)}disabled={state.submitting}>{n}</button>))}</div>);}
Custom callbacks
Use onSuccess and onErrorto run custom logic after submission — analytics events, redirects, toast notifications, etc.
import { useForm, ValidationError } from "@flowqen-forms/react";function ContactForm() {const { state, handleSubmit, getFieldProps } = useForm("YOUR_FORM_ID", {onSuccess: (data) => {// Track conversionanalytics.track("form_submitted", { formId: data.id });// Redirect after a short delaysetTimeout(() => window.location.href = "/thank-you", 1500);},onError: (errors) => {console.error("Submission failed:", errors);toast.error("Something went wrong. Please try again.");},data: {// Hidden fields included with every submission_source: "website",_page: typeof window !== "undefined" ? window.location.href : "",},});if (state.succeeded) return <p>Thanks! Redirecting...</p>;return (<form onSubmit={handleSubmit}><input {...getFieldProps("email", { required: true, type: "email" })} /><ValidationError field="email" errors={state.errors} /><textarea {...getFieldProps("message")} /><button type="submit" disabled={state.submitting}>{state.submitting ? "Sending..." : "Send"}</button></form>);}
TypeScript types
The package ships with full TypeScript declarations. Here are the main types you can import:
import type {UseFormOptions, // Options for useForm(formId, options)FormState, // { submitting, succeeded, errors }FieldError, // { field: string; message: string; code?: string }UseFormReturn, // Return type of useForm()} from "@flowqen-forms/react";
Tips
getFieldPropssetsaria-describedbyautomatically for accessibility- The honeypot spam field is included automatically — no extra setup needed
- Works with React 18+ and Next.js 13+ (App Router and Pages Router)
- The package is tree-shakeable — only import what you use
- For the popup widget approach (no form building), see the Widget docs