Let’s say that you have an array of arrays that is dynamically generated and you would like to provide a download link/button to download it as a CSV. How would you go about doing it? Ordinarily you would go about sending the data to the server which would then be able to make a downloadable version of the data. Believe it or not, there is also a way to do it in modern browsers without ever needing to make additional requests to a server. The following JSBin proves this by providing a large textarea that can be modified and a download link which when clicked (in modern browsers) will download a file with the specified contents:
JS Bin

How does it work? We basically just rely on data URIs. For instance, clicking here should result in downloading a file with text saying Hello World!!!. This an simply an anchor element (<a>) with an href of data:text/plain;charset=utf-8,Hello%20world!!!. In addition, some newer browsers (Chrome and FireFox) support the download attribute being set to specify the default file name when downloaded.

How do you get it to be dynamic? You can simply modify the href attribute when the link is clicked, by binding the necessary code to the click event of the link. Here is a simple version of the JSBin shown:

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">...</style>
    <script type="text/JavaScript">
    window.onload = function() {
      var txt = document.getElementById('txt');
      txt.value = window.onload + '';
      document.getElementById('link').onclick = function(code) {
        this.href = 'data:text/plain;charset=utf-8,'
          + encodeURIComponent(txt.value);
      };
    };

    main();
    </script>
  </head>
  <body>
    <div id="txtWrap">
      <textarea id="txt"></textarea>
    </div>
    <a href="" id="link" download="code.txt">Download Above Code</a>
  </body>
</html>

Ignoring the omitted CSS rules, you will notice that the concept is quite simple. The one thing you want to remember is that in order to make sure that all characters are downloaded correctly you will want to use encodeURIComponent(). I have seen implementations that use escape(), but that function is deprecated and doesn’t always encode the characters correctly.

Have fun making your quick custom download buttons 😎

Categories: BlogJavaScript

15 Comments

opensas · October 24, 2014 at 2:00 PM

Is there any constrain on the size of the file???

    Joe · October 28, 2014 at 10:08 AM

    I think the only thing you’d really have to be careful about is that you’re injecting the raw data into the browser itself. The `href` attribute is literally holding the data that the user is downloading. As you’ll see sometimes with base 64 image files, having too much there can cause a strain on the client side. So while there might not be a browser limitation size-wise, you definitely want to be careful with how much data you’re storing there.

    Greg Bulmash · October 28, 2014 at 2:21 PM

    Looks like it’s 4GB in modern browsers. Caniuse specifies that IE8 limited it to 32kb and IE9 upped it to 4GB. A quick browse around doesn’t give specific numbers for other browsers, so a reasonable conclusion is that IE’s numbers were called out because IE8 differed from spec, but IE9 fixed that.

    Obviously, when you start getting up that high, there are some serious performance issues to think about. A 2GB binary, encoded to B64, is going to be about a 2.6GB data URI that is essentially part of the memory being used by the window. Even if the browser is technically capable of handling that, you’ve got Chrome, Safari, and IE running on mobile devices with less than that much RAM in total, and definitely less than that much free RAM.

    I’d say that the max theoretical size is probably an order of magnitude bigger than the max advisable size.

    Max · November 2, 2014 at 11:10 AM

    “Support in Internet Explorer 8 is limited to images and linked resources like CSS files, not HTML files. Max URI length in IE8 is 32KB. In IE9+ JavaScript files are supported too and the maximum size limit set to 4GB.”

    http://caniuse.com/#feat=datauri
    https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs

Francisc · October 25, 2014 at 5:37 PM

Nice and helpful post.
However, I would have liked to see a bit about browser support and limitations (e.g. filesize like opensas mentioned).

Andrea Giammarchi · October 27, 2014 at 5:55 AM

More on this from this 3 years ago post of mine …

André Jaenisch · October 27, 2014 at 5:33 PM

Interesting idea!

I was thinking about how to overwork the .ics file compiled from the microdata of our homepage.
So maybe it is possible to create it on-the-fly using your trick …

Will have to test it on my FirefoxOS device and another one’s Android one, though.

Moses · September 2, 2015 at 6:13 PM

can this code allow me to download a table? if so how do i write that code?

Clem · November 5, 2015 at 12:35 PM

This does not work in IE at all. Can you provide an example that works in IE?

    aerien · November 18, 2017 at 6:10 AM

    2017 now, this works fine with my smartphone, but still NOT in iExplorer ! Microsoft…

alex · May 11, 2016 at 12:08 AM

This ONLY WORKS ON CHROME.
Putting download attribute in links does not work for Safari or IE.

http://caniuse.com/#feat=download

Mashud · December 2, 2016 at 10:14 PM

Hi, can I create any downloadable file in the browser? Such as wallpapers, images, mp3 etc.

sajith · April 7, 2017 at 3:41 AM

I setup Internet information service manager an c folder and also keep one html file in that location and try to run it in browser as http://localhost its working fine.
But i need to perform download operation using java script how to do that.

This week’s JavaScript news, issue 204 | blog1 · December 5, 2014 at 9:09 PM

[…] Creating a Downloadable File in the Browser from JavaScript tutorial Chris West […]

Javascript Weekly No.204 | ENUE Blog · August 11, 2015 at 1:53 PM

[…] Creating a Downloadable File in the Browser from JavaScript […]

Leave a Reply

Your email address will not be published. Required fields are marked *