FileReader is an event-based browser object that reads the contents of a Blob or File into memory without blocking the page. It is defined by the W3C File API. You pick a read method, wait for the load event, then use the result property. The most searched method is readAsDataURL, which returns a base64 data URL such as data:text/plain;base64,aGkgw6k=.
A reader has a readyState: 0 EMPTY (no read started), 1 LOADING and 2 DONE. It also has result and error properties. The four read methods are:
Events fire in a fixed order: loadstart, then progress roughly every 50 ms while data arrives, then load on success (or error or abort), then loadend. None of them bubble. If you call a read method while the state is LOADING, the browser throws an InvalidStateError. Calling abort() during a read sets result to null and fires abort and then loadend.
Because the API is callback-based, people wrap it in a Promise. Modern code often skips it: blob.text() and blob.arrayBuffer() return promises directly, and text() always decodes as UTF-8.
Node.js has no FileReader, so this example runs the equivalent on a Blob and builds the data URL by hand, which is the string readAsDataURL yields for a blob of type text/plain:
(async () => {
const blob = new Blob(['hi é'], { type: 'text/plain' });
const bytes = Buffer.from(await blob.arrayBuffer());
console.log('data:' + blob.type + ';base64,' + bytes.toString('base64'));
console.log(bytes, await blob.text());
})();
Output from Node 22.22.0:
data:text/plain;base64,aGkgw6k=
<Buffer 68 69 20 c3 a9> hi é
The text "hi é" is 5 bytes in UTF-8, and base64 turns them into 8 characters.
Create a reader, set onload, then call a read method: reader.onload = () => use(reader.result); reader.readAsText(file). The result is only valid inside or after the load event, and it is null before that. For a Promise, wrap it: new Promise((ok, fail) => { r.onload = () => ok(r.result); r.onerror = () => fail(r.error); r.readAsText(f); }).