How do I post form data with fetch api?

You can set body to an instance of URLSearchParams with query string passed as argument

fetch("/path/to/server", {
, body:new URLSearchParams("[email protected]&password=pw")

document.forms[0].onsubmit = async(e) => {
  const params = new URLSearchParams([ FormData(]);
  // fetch("/path/to/server", {method:"POST", body:params})
  const response = await new Response(params).text();
  <input name="email" value="[email protected]">
  <input name="password" value="pw">
  <input type="submit">

Use FormData and fetch to grab and send data

fetch(form.action, {method:'post', body: new FormData(form)});

function send(e,form) {
  fetch(form.action, {method:'post', body: new FormData(form)});

  console.log('We send post asynchronously (AJAX)');
<form method="POST" action="myapi/send" onsubmit="send(event,this)">
    <input hidden name="csrfToken" value="a1e24s1">
    <input name="email" value="[email protected]">
    <input name="phone" value="123-456-789">
    <input type="submit">    

Look on chrome console>network before/after 'submit'


Do not set the content-type header.

// Build formData object.
let formData = new FormData();
formData.append('name', 'John');
formData.append('password', 'John123');

        body: formData,
        method: "post"


Use the FromForm attribute to specify that binding source is form data.

public class SampleDataController : Controller
    public IActionResult Create([FromForm]UserDto dto)
        return Ok();

public class UserDto
    public string Name { get; set; }
    public string Password { get; set; }

To quote MDN on FormData (emphasis mine):

The FormData interface provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".

So when using FormData you are locking yourself into multipart/form-data. There is no way to send a FormData object as the body and not sending data in the multipart/form-data format.

If you want to send the data as application/x-www-form-urlencoded you will either have to specify the body as an URL-encoded string, or pass a URLSearchParams object. The latter unfortunately cannot be directly initialized from a form element. If you don’t want to iterate through your form elements yourself (which you could do using HTMLFormElement.elements), you could also create a URLSearchParams object from a FormData object:

const data = new URLSearchParams();
for (const pair of new FormData(formElement)) {
    data.append(pair[0], pair[1]);

fetch(url, {
    method: 'post',
    body: data,

Note that you do not need to specify a Content-Type header yourself.

As noted by monk-time in the comments, you can also create URLSearchParams and pass the FormData object directly, instead of appending the values in a loop:

const data = new URLSearchParams(new FormData(formElement));

This still has some experimental support in browsers though, so make sure to test this properly before you use it.