# How to convert a file to base64 in JavaScript

Six ways to turn a file into base64 in JavaScript, tested in three browsers, and why a form backend should get the file itself instead.

Author: Formgong
Published: 2026-11-03
Language: en
Canonical: https://formgong.com/en/blog/file-to-base64-javascript/

Read the file with FileReader.readAsDataURL() and keep the text after the comma, or call toBase64() on its bytes where the browser has it. Both were correct for files up to 5 MiB in three browsers on 3 November 2026. Base64 is a third bigger, so a form backend should get the file itself: 102,708 bytes for our 100 KiB file, against 136,569 as base64.

## Why do people convert files to base64 at all?

Usually because something in the chain only takes text. You are posting JSON to an API that has no file field, saving a small image in `localStorage`, or pasting an icon straight into CSS. Base64 turns any bytes into letters, digits, `+`, `/` and `=`, so the file fits anywhere a string fits.

The trouble starts when the same trick is used for an upload form. A visitor picks a 3 MB photo, your script turns it into a 4 MB string, and the backend refuses it or stores it as a wall of text, depending on the size. Before you write the conversion, it is worth knowing which method is safe for big files and what the string costs you on the wire. We measured both on 3 November 2026 in Chromium 153, Firefox 155 and WebKit 26.6.

## How do you convert a file to base64 in JavaScript?

Pick a file with `<input type="file">` and you get a `File` object. From there we tried the six methods that turn up most often in tutorials, on random files of 1 KiB, 100 KiB, 1 MiB and 5 MiB. Each result was compared with the base64 Node.js produced for the same file, and each time is the middle of three runs.

Six conversion methods in Chromium, Firefox and WebKit, 3 November 2026. Times are the range across the three browsers.
MethodWhat happened
`FileReader.readAsDataURL()`, then the part after the commaCorrect for all 4 sizes in 3 of 3 browsers. 75–139 ms for 5 MiB
`Uint8Array.prototype.toBase64()`Correct for all 4 sizes in 3 of 3 browsers. 60–84 ms for 5 MiB
`readAsBinaryString()`, then `btoa()`Correct, 3 of 3 browsers. 88–129 ms for 5 MiB
Bytes in chunks of 32 KiB through `String.fromCharCode`, then `btoa()`Correct, 3 of 3 browsers. 106–158 ms for 5 MiB
`reduce()` over every byte into one string, then `btoa()`Correct, but the slowest in 3 of 3 browsers. 412–842 ms for 5 MiB
`String.fromCharCode(...bytes)`, then `btoa()`Worked at 1 KiB and 100 KiB. At 1 MiB it threw a RangeError in 3 of 3 browsers
`readAsDataURL()` is the one to reach for first, because every browser has had it for years and it turned 5 MiB into base64 in under a fifth of a second. It gives you a data URL such as `data:image/png;base64,iVBOR…`, so drop everything up to the comma if the receiver wants plain base64. `toBase64()` is newer and was as quick or quicker; all three browsers we tested had it, but older ones do not, so feature-check it.

The last row is the one that bites people later. Spreading the bytes into `String.fromCharCode` passes every byte as a separate argument, and at 1 MiB that was too many arguments in every browser. The code works on your test icon and fails on a customer's scanned PDF. If you need the `btoa()` route, feed it in chunks, as in the fourth row. If you only need to send the file with a form, you can skip all of this; our article on the [HTML file input](/en/blog/html-file-input/) shows what the browser already gives you.

## How much bigger does base64 make a file?

About a third. Base64 writes every 3 bytes as 4 characters, so our 100 KiB file became 136,536 characters in all three browsers, 1.333 times the original. How you send that string adds a little more.

One 100 KiB file sent five ways to a test receiver from Chromium, 3 November 2026. Firefox and WebKit sent the same sizes within 50 bytes. The URL-encoded size moves a little with each file, because it depends on how many `+`, `/` and `=` the base64 holds.
How the file was sentRequest body
The file itself in `FormData` (multipart)102,708 bytes, 1.003 times the file
Base64 in JSON136,569 bytes, 1.334 times
A data URL in JSON136,606 bytes, 1.334 times
Base64 as a text field in `FormData`136,778 bytes, 1.336 times
Base64 in a hidden field of a normal form (URL-encoded)145,175 bytes, 1.418 times
The worst row is the plain HTML form. A hidden field is sent URL-encoded, and every `+`, `/` and `=` in the base64 becomes a three-character escape, so the post grew to 1.418 times the file. The best row is no base64 at all: `FormData` with the file in it sent 308 bytes more than the file, which is the multipart wrapping and the name field.

That row also settles a claim that comes up in tutorials. Formspree's “How to Convert an Image URL to Base64” (28 August 2024) says multipart uploads often convert files to base64 on the client side. In our test they did not. The receiver found the file's raw bytes, unchanged, inside the request body in 3 of 3 browsers. The [JavaScript FormData](/en/blog/javascript-formdata/) article covers why you should let the browser write that request's `Content-Type` header.

## Should you send base64 to a form backend?

Usually not, and our test against Formgong shows why. Formgong is a form backend that sends each submission to your email or Telegram. We ran its own code locally and sent it the same PDFs both ways.

Formgong's Worker under wrangler dev, posts from Chromium, 3 November 2026. Attachments on, as they must be for files.
What was sentWhat Formgong did
30 KiB PDF as a file (multipart)Accepted. Stored as a 30,720-byte PDF attachment
The same PDF as a base64 data URL in JSONAccepted as text. No attachment; the row held 41,023 characters of base64
47 KiB PDF as base64 in JSONAccepted as text, 64,235 characters, no attachment
49 KiB PDF as base64 in JSONRefused with 413: over the 64 KB text limit
4 MiB PDF as a file (multipart)Accepted. Stored as a 4,194,304-byte PDF attachment
A file sent as a file arrived as a PDF you can download. The same file as base64 arrived as 41,023 characters of text in the submission, which is what the owner would see in the message instead of an attachment. A slightly bigger file, 49 KiB, crossed the 64 KB limit for text and was refused. Formgong takes files only from multipart posts, up to 5 MiB each in a plain post, and it reads the bytes to check that a `.pdf` really is a PDF before it keeps it. None of that can happen to a string.

So keep base64 for what it is good at, such as small images inside JSON, CSS or a canvas preview, and send uploads as files. The [contact form with file upload](/en/blog/contact-form-with-file-upload/) guide has a complete form, and the [HTML forms docs](/en/docs/html/) list the fields Formgong reads.

```javascript
// Two ways that worked for every file size in all three browsers.
function fileToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const dataUrl = reader.result;            // "data:image/png;base64,iVBOR..."
      resolve(dataUrl.slice(dataUrl.indexOf(",") + 1));
    };
    reader.onerror = () => reject(reader.error);
    reader.readAsDataURL(file);
  });
}

async function fileToBase64Native(file) {
  // Newer built-in; check for it before you rely on it.
  const bytes = new Uint8Array(await file.arrayBuffer());
  return bytes.toBase64();
}

// Sending the file to a form backend? Skip base64 and send the file itself.
const form = document.querySelector("form");
form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const response = await fetch(form.action, {
    method: "POST",
    body: new FormData(form),            // the file goes as raw bytes
    headers: { Accept: "application/json" },
  });
  console.log(response.status);
});
```

## Which form backends accept base64 files?

Some do. Formcarry's guide “How to upload files from your HTML form using Base64 encoding” (16 September 2021, updated 6 January 2023) says Formcarry accepts files sent as base64 data URLs and turns them into downloadable files, and it calls that a Pro feature. Formgong does not do this; a data URL sent to Formgong stays text, as the table above shows. If your stack can only produce base64, that is a real reason to pick a service that decodes it. What Formgong offers instead is the ordinary route done properly: real files over multipart on every plan, including Free, up to 3 per post, each checked by its first and last bytes against its extension before anyone downloads it. For most sites the file input already in your form is the better path anyway, because the request is a quarter smaller and the attachment arrives as an attachment.

## Did base64 behave differently for you?

If one of these methods failed on a browser or file size we did not try, or your backend handles base64 in a way we did not cover, write to [support@formgong.com](mailto:support@formgong.com). We will rerun the test with your case and add it here.

## Frequently asked questions

### How do I get base64 from a file input?

Read input.files[0] with FileReader.readAsDataURL() and keep the text after the comma. It worked for files up to 5 MiB in Chromium, Firefox and WebKit.

### How much bigger is base64 than the file?

About 33%. Our 100 KiB file became 136,536 characters; in a URL-encoded form field the post grew to 1.42 times the file.

### Why does String.fromCharCode(...bytes) throw a RangeError?

Spreading passes every byte as a separate argument. At 1 MiB that was too many in all three browsers. Convert in chunks or use readAsDataURL().

### Do multipart uploads use base64?

No. In our test the file's raw bytes appeared unchanged in the request body in 3 of 3 browsers, with about 300 bytes of overhead.

### Can I send base64 files to Formgong?

Not as files. Base64 arrives as text and is refused above the 64 KB text limit. Send the file itself in a multipart post instead.

## Sources and documentation

- [Formgong: HTML file input: accept, multiple, capture and size checks](https://formgong.com/en/blog/html-file-input/)
- [Formgong: JavaScript FormData and what fetch actually sends](https://formgong.com/en/blog/javascript-formdata/)
- [HTML forms](https://formgong.com/en/docs/html/)

[Get a form key](https://formgong.com/en/#top)
