How to POST Form Data using the JavaScript Fetch API

avatar
Borislav Hadzhiev

Last updated: Jun 4, 2023
6 min

banner

# Table of Contents

  1. How to POST Form Data using the JavaScript Fetch API
  2. POST Form Data using the JavaScript Fetch API using URLSearchParams
  3. You might also see examples online that manually build the FormData

# How to POST Form Data using the JavaScript Fetch API

To POST form data using the JavaScript fetch API:

  1. Add a submit event handler to the form element.
  2. Use the FormData() constructor to create a FormData object.
  3. Use the fetch() method to send the FormData object over the networking, setting the HTTP method to POST.

Here is the HTML for the example.

index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <h2>bobbyhadz.com</h2> <form id="form"> <input type="text" id="username" name="username" /> <br /> <br /> <input type="password" id="password" name="password" autocomplete="on" /> <br /> <br /> <button type="submit" id="submit-btn">Log in</button> </form> <script src="index.js"></script> </body> </html>

And here is the related JavaScript code.

index.js
const form = document.getElementById('form'); form.addEventListener('submit', async event => { event.preventDefault(); const data = new FormData(form); console.log(Array.from(data)); try { const res = await fetch( 'https://jsonplaceholder.typicode.com/users', { method: 'POST', body: data, }, ); const resData = await res.json(); console.log(resData); } catch (err) { console.log(err.message); } });

post form data using javascript fetch api

We used the document.getElementById method to select the form element and added a submit event handler to it.

Notice that we marked the event handler function as async to be able to use the await keyword.

index.js
form.addEventListener('submit', async event => { // ... })

We called the event.preventDefault() method to prevent the browser from refreshing the page.

index.js
event.preventDefault();

The next step is to use the FormData() constructor to create a FormData object.

The FormData object stores a set of key-value pairs representing form fields and their values.

The set of key-value pairs can be sent over the network using fetch, axios, XMLHttpRequest.send(), etc.

index.js
const data = new FormData(form); // [ // ["username", "ab"], // ["password", "cd"] // ] console.log(Array.from(data)); try { const res = await fetch( 'https://jsonplaceholder.typicode.com/users', { method: 'POST', body: data, }, ); const resData = await res.json(); console.log(resData); } catch (err) { console.log(err.message); }

Notice that we aren't using JSON.stringify when sending over the request body.

The FormData object uses the same format a form would use if its encoding type were set to multipart/form-data.

Notice that when the FormData object is converted to an array, it stores a two-dimensional array.

index.js
const data = new FormData(form); // [ // ["username", "ab"], // ["password", "cd"] // ] console.log(Array.from(data));

The first element in each subarray is the key and the second is the value.

Make sure to set the method property in the options object to POST and the body property to the FormData object.

index.js
const res = await fetch( 'https://jsonplaceholder.typicode.com/users', { method: 'POST', body: data, }, );

To view the request payload:

  1. Open your developer tools by pressing F12.
  2. Click on the Network tab.
  3. Select the request in the left sidebar under Name.
  4. Click on the Payload tab.

view request payload in dev tools

As shown in the screenshot the username field stores a value of ab and the password field stores a value of cd at the time of making the request.

If you click on the Headers tab and scroll down to the Request Headers section, you will see that fetch automatically sets the Content-Type header.

fetch automatically sets content type header

The header is automatically set to multipart/form-data; boundary=---....

If you've explicitly set the Content-Type header when calling fetch() make sure to remove it, so it gets set automatically.

# POST Form Data using the JavaScript Fetch API using URLSearchParams

When sending text form data over the network, it is more efficient to send the data as application/x-www-form-urlencoded by using the URLSearchParams() constructor.

The HTML for the example remains the same.

index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <h2>bobbyhadz.com</h2> <form id="form"> <input type="text" id="username" name="username" /> <br /> <br /> <input type="password" id="password" name="password" autocomplete="on" /> <br /> <br /> <button type="submit" id="submit-btn">Log in</button> </form> <script src="index.js"></script> </body> </html>

And here is the related JavaScript code.

index.js
const form = document.getElementById('form'); form.addEventListener('submit', async event => { event.preventDefault(); const formData = new FormData(form); // ๐Ÿ‘‡๏ธ pass form data to URLSearchParams() constructor const searchParams = new URLSearchParams(formData); // ๐Ÿ‘‡๏ธ username=ab&password=cd console.log(searchParams.toString()); try { const res = await fetch( 'https://jsonplaceholder.typicode.com/users', { method: 'POST', body: searchParams, }, ); const resData = await res.json(); console.log(resData); } catch (err) { console.log(err.message); } });

post form data using javascript fetch api with urlsearchparams

We used the FormData() constructor to create a FormData object and passed the FormData object to the URLSearchParams() constructor.

index.js
const formData = new FormData(form); // ๐Ÿ‘‡๏ธ pass form data to URLSearchParams() constructor const searchParams = new URLSearchParams(formData); // ๐Ÿ‘‡๏ธ username=ab&password=cd console.log(searchParams.toString());

The URLSearchParams() constructor creates a URLSearchParams object.

URLSearchParams objects define utility methods that enable you to work with the query string of a URL.

Notice that calling the toString() method on the object returns a query string (without the leading question mark ?).

If you click on the Payload tab for the specific request in the Network tab in your developer tools, you will see that the correct Form Data is sent over the network.

correct form data sent over network

If you click on the Headers tab and scroll down to the Request Headers section, you will see that the Content-Type is set to application/x-www-form-urlencoded.

content type is application x www form urlencoded

The Content-Type is set automatically by fetch, so we didn't have to explicit set it.

index.js
const res = await fetch( 'https://jsonplaceholder.typicode.com/users', { method: 'POST', body: searchParams, }, );

The application/x-www-form-urlencoded content type is used to describe form data that is sent in a single block in the HTTP body.

If the data is sent in the query parameters part of the URL, its size is restricted.

However, when you send the data in the request body as we did in the example, its size is unrestricted.

# You might also see examples online that manually build the FormData

You might also see examples online that manually build the FormData object.

The HTML for the example remains the same.

index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <h2>bobbyhadz.com</h2> <form id="form"> <input type="text" id="username" name="username" /> <br /> <br /> <input type="password" id="password" name="password" autocomplete="on" /> <br /> <br /> <button type="submit" id="submit-btn">Log in</button> </form> <script src="index.js"></script> </body> </html>

And here is the updated JavaScript code.

index.js
const form = document.getElementById('form'); const username = document.getElementById('username'); const password = document.getElementById('password'); form.addEventListener('submit', async event => { event.preventDefault(); const formData = new FormData(); formData.append('username', username.value); formData.append('password', password.value); console.log(Array.from(formData)); try { const res = await fetch( 'https://jsonplaceholder.typicode.com/users', { method: 'POST', body: formData, }, ); const resData = await res.json(); console.log(resData); } catch (err) { console.log(err.message); } });

manually building the form data object

Notice that this time, we created an empty FormData object.

index.js
const formData = new FormData(); formData.append('username', username.value); formData.append('password', password.value); // [ // ["username", "ab"], // ["password", "cd"] // ] console.log(Array.from(formData));

The FormData.append method can be used to append a new key-value pair to the FormData object.

The first parameter the append() method takes is the name of the key and the second is the name of the value.

If the key already exists in the FormData object, its value gets updated, otherwise, the new key-value pair gets added to the FormData object.

However, most of the time manually constructing the FormData object is not necessary as it can be automatically done by passing the form element to the FormData() constructor.

index.js
const data = new FormData(form); // [ // ["username", "ab"], // ["password", "cd"] // ] console.log(Array.from(data));

You should only build the FormData object manually if you need to overwrite the values of specific keys.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2023 Borislav Hadzhiev