Get super confident at JavaScript. Javascript Icon

I promise to change your JavaScript skills!

How to Transform FormData into a Query String

“How do I turn FormData into a query string?” you ask. This typically involves what we would call “serializing” your data.

It’s likely that you want to end up with something like this:

fullname=Todd+Motto&pizza=pepperoni&size=large&quantity=2

Alongside some HTTP Headers of Content-Type set to application/x-www-form-urlencoded - this will pass along nicely to your backend application.

In this article, we’re going to look at the HTML for a basic form that will then have a submit event bound to it - and once it fires we’ll be using the FormData API and converting it to a query string.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

For this, we’ll be using the new URLSearchParams API to our advantage.

Let’s look at the HTML we have for our Pizza form:

<form name="order">
  <label>
    Your name
    <input type="text" name="fullname">
  </label>
  <label>
    What pizza would you like?
    <select name="pizza">
      <option value="pepperoni">Pepperoni</option>
      <option value="meaty">Meaty</option>
      <option value="cheesey">Cheesey</option>
    </select>
  </label>
  <div>
    What size?
    <label>
      Small
      <input type="radio" name="size" value="small">
    </label>
    <label>
      Medium
      <input type="radio" name="size" value="medium">
    </label>
    <label>
      Large
      <input type="radio" name="size" value="large">
    </label>
  </div>
  <label>
    How many?
    <input type="number" name="quantity" value="1">
  </label>
  <button type="submit">
    Submit
  </button>
</form>

So initially, we have a few name properties on the form fields:

It makes sense that we’ll expect to capture these values with the FormData API.

To do that, let’s setup an event listener and create a new instance of FormData:

const form = document.forms.order;

function handleSubmit(event) {
  event.preventDefault();
  const formData = new FormData(event.target);
}

form.addEventListener('submit', handleSubmit);

What does const formData now contain? As FormData is a constructor, it will return us a new instance of FormData - which means it has some useful methods on it.

If we try to console.log(formData) directly, we’ll see this:

FormData {}
  __proto__: FormData

This isn’t very helpful - I can’t see any properties or any of my data. Turns out the methods that do exist are on the FormData.prototype, which has a few methods that allow us to have a look inside at our data, to ensure we’re constructing it properly.

Using FormData.entries

Let’s begin with .entries(), which returns us an Iterable object. Because it returns an Iterable, we can either iterate over it (via a loop) or make use of a newer JavaScript feature - the Spread syntax:

function handleSubmit(event) {
  event.preventDefault();
  const formData = new FormData(event.target);
  const data = [...formData.entries()];
  console.log(data);
}

Which then outputs a multi-dimensional array in the console upon successful completion of the form:

[
  ["fullname",  "Todd Motto"],
  ["pizza",  "pepperoni"],
  ["size",  "large"],
  ["quantity",  "2"]
]

A multi-dimensional array can easily be converted across to simple key-value pair syntax.

Using encodeURIComponent

Evolving our function with the Array map operator, this is typically what we’d need to do to assemble a query string to be sent off to the server:

function handleSubmit(event) {
  event.preventDefault();
  const formData = new FormData(event.target);
  const data = [...formData.entries()];
  const asString = data
      .map(x => `${encodeURIComponent(x[0])}=${encodeURIComponent(x[1])}`)
      .join('&');
  console.log(asString);
}

This now gives us:

fullname=Todd%20Motto&pizza=pepperoni&size=large&quantity=2

And we could stop there, or perhaps you’re already there.

Note that this method uses %20 to space my name (fullname=Todd%20Motto).

Introducing a new addition to the JavaScript language, URLSearchParams. Browser support is Microsoft Edge and everyone else.

Using URLSearchParams

We can now introduce URLSearchParams , which allows us to work with a query string of a URL.

This is great because if we pass our multi-dimensional FormData array into it, we’ll get a perfectly formatted query string ready to send - and for minimal effort. That’s what I love about this new API.

Refactoring our function, we can advocate this new approach and create a much more readable line of code :

function handleSubmit(event) {
  event.preventDefault();
  const formData = new FormData(event.target);
  const asString = new URLSearchParams(formData).toString();
  console.log(asString);
}

This then gives us:

fullname=Todd+Motto&pizza=pepperoni&size=large&quantity=2

The only thing I noticed that’s different from encodeURIComponent and the result from URLSearchParams is the former uses the %20 approach versus the above + approach to join words (see fullname=Todd+Motto above).

Try the live StackBlitz demo:

So there you have it. The FormData API is lovely to use and effortless to integrate, it’s an integral part of the toolkit. Alongside URLSearchParams we can see it’s also a super efficient and readable solution to getting your data formatted and ready to send to your server.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

I hope you enjoyed the post, and if you’d love to learn more please check out my JavaScript courses, where you’ll learn everything you need to know to be extremely good and proficient. Enjoy and thanks for reading!

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover