JSON files download with the anchor download attribute
The a anchor element and the download attribute
Let's say you want an HTML form that lets you download a JSON file of the form data after submit. Is that even possible on the client side?
The role of an HTML anchor element should be no surprise to you. Hyperlinks are the pillar of the web as we know it. With an <a></a>
we can link to pages and to external websites or to internal resources.
Every HTML element has attributes, and <a></a>
makes no exceptions. href
is the most common attribute attached to anchor elements and it's the one that links out to pages in the same domain or to external resources.
A typical hyperlink in a web page has a destination and some text for the user:
<a href="https://somewebsite.com/page/">Read this</a>
There are also other attributes like rel
and target
. And then there's this attribute called download
, part of HTML5. Guess what, when you put this attribute on a link the user is prompted to download whatever you have in the anchor element.
This bring us again to the original question: can we have an HTML form that lets you download a JSON file of the form data after submit? Turns out we can with <a></a>
and the download
attribute. Let's see!
Laying out the HTML
To start off let's create the HTML structure for our experiment: we need a form and an anchor:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label for="first_name">Your first name</label>
<input type="text" name="first_name" id="first_name">
<label for="age">Your age</label>
<input type="number" min="0" max="100" name="age" id="age">
<button type="submit">Send</button>
</form>
<a download>Download your data</a>
</body>
<script src="form.js"></script>
</html>
The form has two fields and a submit button. The anchor does not have an href
(for now), and more important, the download
attribute is already set. With this in place let's wire up the JavaScript logic for handling the form.
Handling the form data
First things first let's handle the quintessential submit event. Create a new file named form.js
:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
buildJSON(this);
});
We prevent the form from sending data to the backend and we forward this
to a utility function. Here this
is the form itself. Now in the utility function named buildJSON
we'll build a FormData object out of the form:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
buildJSON(this);
});
function buildJSON(form) {
const data = new FormData(form);
}
We can do a lot of things with a FormData object, I already touched the topic in Working with FormData in JavaScript. But for now we need to build a JavaScript object from form fields.
FormData objects have an entries()
method. This method returns an iterator from which we can build out an array of arrays. Let's say we filled the form with first name and age for Jules, 34 years old, we can build an object from these entries with:
// omit for brevity
function buildJSON(form) {
const data = new FormData(form);
const entries = data.entries();
const obj = Object.fromEntries(entries);
}
Here we pass our entries to Object.fromEntries
(ECMAScript 2019). With this code we get back a nice object:
{
first_name: "Jules",
age: 34
}
What now? Let's wire up the last piece of the puzzle: the anchor element.
How to download a JSON file with the anchor download attribute
When an anchor element has the download
attribute set the user is prompted to download the link. If the attribute is associated to a value, this value is used as the filename for the download:
<a download="Filename.txt" href="javascript.txt">Download your data</a>
This example assumes that javascript.txt is a real file present on the server. What instead if we want to generate a JSON file on the fly and let the user download it?
Data URL to the rescue! A Data URL is composed of four parts:
- the prefix
data:
- the MIME type for the file
- an optional
base64
token - the actual data
In our case since the data is a simple JSON string we can build our Data URL this way:
data:application/json,ourstringhere
In JavaScript:
const dataUrl = `data:application/json,${json}`
So back to our code we can provide our stringified JavaScript object to a Data URL:
// omit for brevity
function buildJSON(form) {
const data = new FormData(form);
const entries = data.entries();
const obj = Object.fromEntries(entries);
const json = JSON.stringify(obj);
const dataURL = `data:application/json,${json}`;
}
As a last step let's apply our Data URL to the anchor element. Here's the complete code:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
buildJSON(this);
});
function buildJSON(form) {
const data = new FormData(form);
const entries = data.entries();
const obj = Object.fromEntries(entries);
const json = JSON.stringify(obj);
const dataURL = `data:application/json,${json}`;
const anchor = document.querySelector("a");
anchor.setAttribute("download", "Your_data.txt");
anchor.setAttribute("href", dataURL);
}
Try yourself, fill and submit the form. You should see the anchor element in the inspector complete of download
and href
:
As simple as it looks, this approach is handy for small pieces of data.
Thanks for reading!