Installation
This library assumes that React is already installed in your environment as a peer dependency. Our helpers rely on React Hooks, so you must be on version 16.8.0 or higher.
npm install @formspree/react
Source on GitHub | npm package
You can use this library with or without the Formspree CLI. This article assumes you're creating forms in the dashboard. For help with CLI projects, see our article on using the Formspree CLI.
Usage
The useForm
React hook is the easiest way to setup a React form with Formspree. Just import @formspree/react
and then call useForm
with the form's hashid obtained by creating a form in the Formspree dashboard.
import { useForm } from '@formspree/react';
function MyForm() {
const [state, handleSubmit, reset] = useForm('{your-form-id}');
if (state.succeeded) {
return <div>Thank you for signing up!</div>;
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />
<button type="submit" disabled={state.submitting}>Sign up</button>
</form>
)
}
State object
The first value in the array returned by this hook is a state object:
const [state, handleSubmit, reset] = useForm('{your-form-id}');
The state object contains the following properties:
Key | Description |
---|---|
submitting |
A Boolean indicating whether the form is currently submitting (defaults to false ) |
succeeded |
A Boolean indicating whether the form successfully submitted (defaults to false ) |
errors |
An instance of the SubmissionError class containing of server-side validation errors (defaults to null ) |
result |
An object with one property next which is the redirect URL when the submission is successful (defaults to null ) |
State changes over time in the following ways:
- When
handleSubmit
is called,submitting
becomestrue
- If the submission fails server-side validations, a new instance of
errors
is created with the specific errors - If the submission succeeds,
succeeded
becomestrue
, andresult
is notnull
- After the submission request finishes,
submitting
always becomesfalse
The errors
object has following methods:
Key | Description |
---|---|
getFormErrors() |
Returns an array of form-level errors |
getFieldErrors(field) |
Returns an array of errors for the field |
getAllFieldErrors() |
Returns an array of key-value pair of field-level errors of all fields ( |
The error items in the errors
object have the following properties:
Key | Description |
---|---|
message |
A human-readable error message fragment (e.g. "is required") |
code |
A machine-friendly error code (See error codes below) |
details |
An object containing various additional properties about the error. For example, when used with Stripe, will contain a stripeCode field that contains the exact error from Stripe |
Reset
The third value in the array returned by the hook is a reset function.
const [state, handleSubmit, reset] = useForm('{your-form-id}');
This function is used to reset the state object after a form submission request has been completed. It clears out the result
and errors
properties, and sets the submitting
andsucceeded
boolean properties to their default state (i.e. false). You can use this method on the "thank you" page you show after the form submission request has been completed to allow the user to reset the form and submit it once again.
Here's an example showing how to use the reset()
function in a form:
function TestForm() {
const [state, submit, reset] = useForm('{your-form-id}');
if (state.submitting) {
return <p>Submitting…</p>;
}
if (state.succeeded) {
return (
<div>
<p>Thanks!</p>;<button onClick={reset}>Reset</button>
</div>
);
}
return (
<div>
<h1>Form</h1>
<form onSubmit={submit}>
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
name="email"
defaultValue="test@example.com"
/>
<button type="submit">Sign up</button>
</form>
</div>
);
}
Validation errors
Here's an example form using the ValidationError
component to display field errors:
import { ValidationError, useForm } from '@formspree/react';
function MyForm() {
const [state, handleSubmit] = useForm('{your-form-id}');
if (state.succeeded) {
return <div>Thank you for signing up!</div>;
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" required />
<ValidationError field="email" prefix="Email" errors={state.errors} />
<button type="submit" disabled={state.submitting}>Sign up</button>
</form>
)
}
The ValidationError
component accepts the following special properties:
field
— the name of the field for which to display errors. If nofield
attribute is provided then general (non field) errors will be reported. (See Error codes below)errors
— the object containing validation errors (required)prefix
— the human-friendly name of the field (optional, defaults to "This field")
All other props (such as className
) are passed through to the <div>
wrapper. If the given field is invalid, this component renders a <div>
containing the error message:
<div {...props}>
{prefix} {message}
</div>
Additional options
This hook accepts two arguments: the form key and an object containing options.
const [state, handleSubmit, reset] = useForm('{your-form-key}', options);
Here are the acceptable options:
Key | Type | Description |
---|---|---|
data |
object | An Object containing strings or functions to merge with your form data |
client |
Formspree | An instance of the Formspree client |
Usage Example
const [state, handleSubmit, reset] = useForm('{your-form-id}', {
data: {
subject: 'Someone joined the newsletter',
pageTitle: function() {
// This function will be evaluated at submission time
return document.title;
}
}
});
Usage with Stripe
The Formspree React library includes support for payments via Stripe. The library handles most of the front end state management required to create payments and perform Strong Customer Authentication (SCA). The Formspree backend then takes care of submitting payments to Stripe. Together, Formspree dramatically reduces the development effort required to create a payment form.
Stripe functionality is lazy loaded. That means only a minimal stripe wrapper (just 6k compressed) is bundled with @formspree/react
. The bulk of the Stripe library is loaded only if, and when, Stripe elements are first rendered.
To use Stripe, first wrap your app with the FormspreeProvider
component and include the stripePK
prop with your Stripe publishable key.
import { FormspreeProvider } from '@formspree/react';
function MyApp() {
return (
<FormspreeProvider
stripePK={'{your-stripe-publishable-key}'}
>
<PaymentForm />
</FormspreeProvider>
)
}
Then when building your form, use the CardElement
component exported by @formspree/react
.
import { useForm, CardElement, ValidationError } from '@formspree/react';
function PaymentForm() {
const [state, handleSubmit] = useForm('{your-form-id}');
if (state.succeeded) {
return <div>Payment has been handled successfully!</div>;
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />
</div>
<div>
<label>Card details</label>
<CardElement />
<ValidationError
field="paymentMethod"
errors={state.errors}
/>
</div>
<button type="submit" disabled={state.submitting}>
{state.submitting ? 'Handling payment...' : 'Pay'}
</button>
</form>
)
}
The CardElement
component is passed-through directly from @stripe/react-stripe-js
. It is exported by Formspree React only to ensure version consistency. The documentation for the CardElement
component can be found here.
Note the field name "paymentMethod" on ValidationError
. This is the field name used to send data from Stripe to Formspree, and must be passed to ValidationError
to catch relevant Stripe errors.
Usage with an external form library
The useSubmit
React hook is an easy way to integrate another form library with Formspree. Just import @formspree/react
and then call useSubmit
with the form's hashid obtained by creating a form in the Formspree dashboard.
import { useSubmit } from '@formspree/react';
import { useForm } from 'react-hook-form';
type Inputs = {
email: string;
message: string;
name: string;
};
export function WithReactHookForm() {
const {
formState: { errors, isSubmitSuccessful, isSubmitting },
handleSubmit,
register,
setError,
} = useForm<Inputs>();
const submit = useSubmit<Inputs>(
process.env.REACT_APP_REACT_HOOK_FORM_ID as string,
{
onError(errs) {
const formErrs = errs.getFormErrors();
for (const { code, message } of formErrs) {
setError(`root.${code}`, {
type: code,
message,
});
}
const fieldErrs = errs.getAllFieldErrors();
for (const [field, errs] of fieldErrs) {
setError(field, {
message: errs.map((e) => e.message).join(', '),
});
}
},
}
);
return (
<div>
{isSubmitSuccessful ? (
<h2>Your message has been sent successfully!</h2>
) : (
<form onSubmit={handleSubmit(submit)}>
<div className="block">
<label htmlFor="email">Email</label>
<input {...register('email')} id="email" type="email" />
{errors.email && <p className="error">{errors.email.message}</p>}
</div>
<div className="block">
<label htmlFor="name">Name</label>
<input {...register('name')} id="name" />
{errors.name && <p className="error">{errors.name.message}</p>}
</div>
<div className="block">
<label htmlFor="message">Message</label>
<textarea {...register('message')} id="message" rows={10} />
</div>
{errors.root && (
<div className="block">
<ul className="error">
{Object.values(errors.root).map((err) => {
if (typeof err !== 'object') {
return <li key={err}>{err}</li>;
}
const { type, message } = err;
return <li key={type}>{message}</li>;
})}
</ul>
</div>
)}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
)}
</div>
);
}
Error codes
The following error codes may appear in the state object's errors array:
Code | Field Error | Description |
---|---|---|
INACTIVE
|
The form has been disabled | |
BLOCKED
|
The form has been blocked | |
EMPTY
|
No data was submitted | |
PROJECT_NOT_FOUND
|
An invalid project key was used to submit the form | |
FORM_NOT_FOUND
|
An invalid form hashid was used to submit the form | |
NO_FILE_UPLOADS
|
File uploads are not supported for this form | |
TOO_MANY_FILES
|
The form was submitted with too many file attachments | |
FILES_TOO_BIG
|
One or more files uploaded exceeded the max file size | |
REQUIRED_FIELD_MISSING
|
✓ | A field is required, but no value was provided |
REQUIRED_FIELD_EMPTY
|
✓ | A field is required, but a blank or empty string was provided |
TYPE_EMAIL
|
✓ | A field should contain an email |
TYPE_NUMERIC
|
✓ | A field should contain a number |
TYPE_TEXT
|
✓ | A field should contain text |
STRIPE_CLIENT_ERROR
|
✓ | Stripe key missing |
STRIPE_SCA_ERROR
|
✓ | Stripe SCA error. See details |