AI Workshop: learn to build apps with AI →
Forms: Form Actions

Join the AI Workshop and learn to build real-world apps with AI. A hands-on, practical program to level up your skills.


Within a component you might need to respond to an event (for example a click) with an event handler that must trigger something on the server.

Very common pattern:

import { useState } from 'react'

export const Demo = () => {
  const [fullName, setFullName] = useState('')

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()

    await fetch('https://api.example.com/submit', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ fullName }),
    })
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          type='text'
          value={fullName}
          onChange={(e) => setFullName(e.target.value)}
        />
        <button type='submit'>Submit</button>
      </form>
    </div>
  )
}

Same example, using FormData, which removes the need to track each individual input field value using a state management tool (useState in the above example):

import { useRef } from 'react'

export const Demo = () => {
  const formRef = useRef<HTMLFormElement>(null)

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()

    if (formRef.current) {
      const formData = new FormData(formRef.current)

      await fetch('https://api.example.com/submit', {
        method: 'POST',
        body: formData,
      })
    }
  }

  return (
    <div>
      <form ref={formRef} onSubmit={handleSubmit}>
        <input
          type='text'
          name='fullName'
        />
        <button type='submit'>Submit</button>
      </form>
    </div>
  )
}

The same thing now can be performed using an action:

export const Demo = () => {
  async function myFormAction(formData) => {
    await fetch('https://api.example.com/submit', {
      method: 'POST',
      body: formData,
    })
  }

  return (
    <div>
      <form action={myFormAction}>
        <input
          type='text'
          name='fullName'
        />
        <button type='submit'>Submit</button>
      </form>
    </div>
  )
}

Look how much simpler the code is, because now we don’t track the individual input fields state, but also we don’t respond to a browser event directly, and we don’t have to pass the form ref around to keep track of how to access it, because the action is directly passed the FormData object.

Lessons in this unit:

0: Introduction
1: Managing forms in React
2: ▶︎ Form Actions
3: React, how to make a checked checkbox editable
4: How I fixed an issue with a React login form state and Browser autofill
5: How to get the value of an input element in React