Back to blog
guides

How to create a React form

Build a React contact form that collects submissions with no backend. Copy-paste examples for a plain HTML form and a JavaScript submission with success and error states.

J
Jesper Christiansen

FormBackend is perfect for static sites and single-page apps. This guide shows you how to add a React contact form that collects submissions and emails you on every new one — without writing or hosting any backend code.

We’ll start with a plain HTML form (which already works on its own) and then enhance it to submit with JavaScript so users stay on the page and see an inline confirmation.

Create a new React app

We’ll start with the basics and assume you don’t have an existing React app. If you do, you can proceed to the next section.

The recommended way to start a new React project is with Vite. Run the following in your terminal:

npm create vite@latest formbackend-react -- --template react

formbackend-react is the directory of the app and can of course be whatever you’d like.

Let’s go to the directory and install dependencies:

cd formbackend-react
npm install

Start the development server:

npm run dev

Your browser should open with http://localhost:5173 loaded, which is the development server.

Create a new form in FormBackend

Log in to your FormBackend account and visit the forms index page. Go ahead and create a new form and give it a name you can remember it by.

After your form has been created, you’ll see the “Submissions” page which is where new submissions will appear. If you navigate to the “Set up” page you can see the unique URL for your form. We’ll copy that!

Create your react form

Now that we have our endpoint in FormBackend we can go ahead and hook it up to a form in React.

Open the file src/App.jsx and replace it with

import './App.css';

function App() {
  return (
    <form action="https://www.formbackend.com/f/your-form-id" method="POST">
      <div className="fieldset">
        <label htmlFor="name">Name</label>
        <input type="text" id="name" name="name" required />
      </div>

      <div className="fieldset">
        <label htmlFor="email">Email</label>
        <input type="email" id="email" name="email" required />
      </div>

      <button type="submit">Submit</button>
    </form>
  );
}

export default App;

Replace your-form-id with the unique URL from your form’s “Set up” tab. Note the JSX-specific attributes: className instead of class, and htmlFor instead of forclass and for are reserved words in JavaScript, so React uses these alternatives.

If you go back to the browser it should look like this

The unstyled HTML form

Let’s add some simple styling by replacing the content of src/App.css with this

body { font-family: Arial, Helvetica, sans-serif; }

.fieldset + .fieldset,
form + form {
  margin-top: 8px;
}

label {
  color: #334155;
  display: block;
  font-size: 87.5%;
  font-weight: bold;
  text-transform: uppercase;
}

input,
textarea,
select {
  border: 1px solid #ddd;
  color: #475569;
  font-size: 100%;
  padding: 5px;
  border-radius: 4px;
}

button[type="submit"] {
  background: purple;
  background: #14b8a6;
  border: none;
  box-shadow: none;
  color: white;
  border-radius: 2px;
  font-size: .8rem;
  text-transform: uppercase;
  font-weight: 500;
  padding: 8px 12px;
  margin-top: 16px;
}

You should now have a form that looks a litlte nicer

The styled React contact form

After filling it out and hitting the submit button, you’ll be taken to FormBackend’s submission success page and if you navigate to the Submissions page for the form you created in FormBackend you should see the submission you just added.

Submit without a page refresh

The form above works on its own, but it redirects the user to FormBackend’s thank-you page. For a smoother experience, submit it with JavaScript so the user stays on the page and sees an inline confirmation. Update src/App.jsx:

import { useState } from 'react';
import './App.css';

function App() {
  const [status, setStatus] = useState('idle');

  async function handleSubmit(event) {
    event.preventDefault();
    setStatus('submitting');

    const form = event.currentTarget;
    const response = await fetch(form.action, {
      method: 'POST',
      body: new FormData(form),
      headers: { accept: 'application/json' },
    });

    if (response.ok) {
      form.reset();
      setStatus('success');
    } else {
      setStatus('error');
    }
  }

  if (status === 'success') {
    return <p>Thanks! Your message has been sent.</p>;
  }

  return (
    <form
      action="https://www.formbackend.com/f/your-form-id"
      method="POST"
      onSubmit={handleSubmit}
    >
      <div className="fieldset">
        <label htmlFor="name">Name</label>
        <input type="text" id="name" name="name" required />
      </div>

      <div className="fieldset">
        <label htmlFor="email">Email</label>
        <input type="email" id="email" name="email" required />
      </div>

      <button type="submit" disabled={status === 'submitting'}>
        {status === 'submitting' ? 'Sending…' : 'Submit'}
      </button>
      {status === 'error' && <p>Something went wrong — please try again.</p>}
    </form>
  );
}

export default App;

A few things worth noting:

  • Calling event.preventDefault() stops the browser’s default full-page submission.
  • The browser’s built-in FormData reads every field straight from the form, so you don’t need a piece of useState per input — the inputs stay uncontrolled.
  • The accept: application/json header tells FormBackend to return JSON instead of an HTML page, so you can react to the result in code.
  • Because the <form> still has a valid action and method, it keeps working even if JavaScript fails to load — progressive enhancement for free.

What to set up next

Now that your React form is collecting submissions, here are a few things worth configuring in FormBackend:

Looking for other frameworks? See our guides for Next.js, Vue.js, Gatsby, Svelte, and more.

Add a form backend to your site in minutes

Connect any HTML form to FormBackend and start collecting submissions — no backend code required.

Start free