Before you reach out to a form library, I suggest to look at what browsers nowadays have to offer by default. In a lot of cases it will be overkill to include a form library that has implemented a ton of features that you're not going to use anyways. In this blog post I want to dive into the FormData
instance.
What is it?
FormData
is an instance that you can use to create a set of key/value pairs that works seamlessly together with other native browser methods, for example:
- It can be used to directly send key/value pairs as body in
POST
requests viafetch
orXMLHttpRequest.send()
. - It can be passed directly into
URLSearchParams
.
It is often used to retrieve and process data from form inputs.
Feed it with data
Either, you manually add data to it using its set
or append
methods, or you connect it to a form for it to automatically create it's key/value pairs based on the form.
Manually adding key/value pairs
To create a FormData
instance from scratch you initialize it without passing any arguments. Then you use its append
and/or set
method to add data to the instance.
const userFormData = new FormData();
// Use set to create or overwrite a value for the key passed as the first argument
formData.set('name', name);
formData.set('age', age);
preferredFruits.forEach(fruit => {
// Use append when a key can have multiple values
formData.append('fruit', fruit);
});
You are responsible for managing the data and use it to cal these methods to feed the FormData
instance.
Attach it to a form element
When you want the FormData
instance to use data from a form
element, you pass the form
element as an argument to FormData
.
<form id="user-form">
<input name="name" type="text" />
<input name="age" type="number" />
<select name="preferred-fruits" multiple>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
<option value="grape">Grape</option>
</select>
<button>Submit</button>
</form>
<script>
const form = document.getElementById("user-form");
form.addEventListener("submit", () => {
const userFormData = new FormData(form);
// ...
})
</script>
In React you use useRef
to pass the form
element, like so:
function UserForm() {
const formRef = useRef<HTMLFormElement>(null);
return (
<form
ref={formRef}
onSubmit={(event) => {
event.preventDefault();
// We need to check for `formRef.current`, because it can be `undefined` according to TypeScript.
if (!formRef.current) {
return;
}
const userFormData = new FormData(formRef.current);
// ...
}}
>
<input name="name" type="text" />
<input name="age" type="number" />
<select name="preferred-fruits" multiple>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
<option value="grape">Grape</option>
</select>
<button>Submit</button>
</form>
);
}
Note: you can always add, change or remove data from the FormData instance after you initialise it by passing a form element as an argument.
Using the data
We have a FormData
instance that contains all the data you need. Now we can start using it.
Specific value
To get and use the value of a specific key in the FormData
instance you can use the get
or getAll
method.
/**
* `get` returns the value associated to the key that is passed as an argument.
*
* If the instance has more values for the key it return the first one.
*/
const name = userFormData.get("name");
/**
* `getAll` returns an array of all the values associated to the key that is passed as an argument.
*/
const preferredFruit = userFormData.getAll('preferred-fruits');
// ...
Note: you could've seen this in action in my blog post about React's key attribute already. Read that blog post here.
Now you can use the value in any way you want.
If you just want to know if a specific key is present in the FormData
instance you can use the has
method.
if(userFormData.has('name')) {
// ...
} else {
// ...
}
Process all data
If you want to process the data as an iterator you can use the entries
, keys
and values
method. These methods return an iterator that you can use to iterate over the FormData
instance's data.
const keys = userFormData.keys();
const values = userFormData.values();
const entries = userFormData.entries();
// Now loop over them using the for..of statement
for(const key in keys /** or `values` or `entries` */) {
// ...
}
Use it directly in other browser native methods
Now that you know how you can create, manipulate and use data in a FormData
instance it time to look at applications that highlight why this instance is interesting to use. This is where it actually isolates away a lot of potentially complex code to get data from form inputs to a request that is send.
POST request
Let's assume that we want to send data from our form inputs to a POST
endpoint. To realise that you have to do the following:
- Create a form
- Once a submit event is fired you need to initialise a FormData instance, passing the form element as an argument.
- Manipulate the form data in any way you want.
- Send the request
<form id="user-form">
<input name="name" type="text" />
<input name="age" type="number" />
<select name="preferred-fruits" multiple>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
<option value="grape">Grape</option>
</select>
<button>Submit</button>
</form>
<script>
const form = document.getElementById("user-form");
form.addEventListener("submit", async () => {
const userFormData = new FormData(form);
// Edit `userFormData` in any way you like here
const response = await fetch("https://your.api/api/post-endpoint", {
method: "POST",
// Add the `FormData` directly as the body for the request
body: userFormData,
});
console.log(await response.json());
});
</script>
Instead of worrying about trivial things, now you can focus on what matters for you. Editing the form data in any ways necessary.
Note: if you do not want to edit the form input data in any way you do not need to use JavaScript. You can simplify things even more by using the form
element's method
and action
attributes.
GET request
Let's do the same thing, but with a GET
request. A real world scenario here, could be a form that functions as a filter. For example, in a webshop where you can filter on color and size.
<form id="filter-form">
<select name="color">
<option value="red">Red</option>
<!-- ... -->
</select>
<select name="size">
<option value="s">Small</option>
<!-- ... -->
</select>
<button>Submit</button>
</form>
<script>
const form = document.getElementById("filter-form");
form.addEventListener("submit", async () => {
const filterFormData = new FormData(form);
// Edit `filterFormData` in any way you like here
// Pass the `FormData` instance directly into `URLSearchParams`
const params = new URLSearchParams(filterFormData);
// `params` resolves to `"color=red&size=s"`
const response = await fetch(`https://your.api/api/get-endpoint?${params}`);
console.log(await response.json());
});
</script>
Again, all trivial things are isolated away and you can focus on the data.
Conclusion
I think it's always wise to stay as close to the browser as possible. The more abstractions between your code and the browser the clunkier, heavier and more complex it gets. I hope this blog post illustrates some use-cases where the browser already has some great API's ready for you to work with.
If you liked this article and want to read more make sure to check my other articles. Feel free to contact me on Twitter with tips, feedback or questions!
Top comments (0)