Report this

What is the reason for this report?

How To Encode and Decode Strings with Base64 in JavaScript

Updated on October 2, 2025
Nicholas CerminaraAnish Singh WaliaManikandan Kurup

By Nicholas Cerminara, Anish Singh Walia and Manikandan Kurup

How To Encode and Decode Strings with Base64 in JavaScript

Introduction

Base64 is a binary-to-text encoding scheme that represents binary data in an ASCII string format. In JavaScript, you can perform Base64 encoding and decoding using the native btoa() and atob() functions. These functions are useful when you need to transmit complex data, such as images or other media files, over channels that only reliably support text.

This article explains how to use btoa() to encode strings into Base64 and atob() to decode them back. We will also cover common applications for Base64 in web development and address the limitations of these functions, particularly when working with Unicode characters, providing a more robust solution for modern use cases. The tutorial also provides a method for handling non-ASCII text and explains the correct approach for Node.js environments.

Key Takeaways:

  • JavaScript’s btoa() and atob() functions handle Base64 encoding and decoding for ASCII strings.
  • Base64 encoding is used to convert binary data into a text format for safe transmission.
  • The btoa() and atob() functions do not support Unicode characters and will cause an error if used with them.
  • Common applications for Base64 include embedding images or other media directly into web pages.
  • To handle Unicode in browser-based JavaScript, use the TextEncoder and TextDecoder APIs.
  • In Node.js, all Base64 operations should be performed using the built-in Buffer module.
  • Failing to use the correct method for Unicode characters can lead to data corruption or application errors.

Prerequisites

To get the most out of this tutorial, you should have:

What is Base64 Encoding?

Base64 encoding is a method that converts binary data into a limited set of ASCII characters. This set contains 64 common characters that are safe for transmission across most text-based systems. The character set includes uppercase letters (A-Z), lowercase letters (a-z), numbers (0-9), and two special characters (+ and /). A padding character (=) is also used at the end of the encoded string if necessary.

The primary purpose of Base64 is to ensure data remains intact without modification during transport. This is important for protocols that are designed to process text and might misinterpret certain byte sequences as control characters.

Encoding a String to Base64 with btoa()

To encode a string to Base64 in JavaScript, you use the btoa() function. This function takes a string as its only argument and returns the Base64-encoded version of that string.

The name btoa is a reference to the old Unix command for converting “binary to ASCII”.

Let’s take the string "Hello World!" and encode it. You can run this code in your browser’s developer console.

// Define a string.
const stringToEncode = 'Hello World!';

// Encode the string using btoa().
const encodedString = btoa(stringToEncode);

console.log(encodedString);

The btoa() function returns the encoded string, which you can see in the output:

Output
SGVsbG8gV29ybGQh

While btoa() is simple to use, it has a significant limitation: it only works correctly with strings that contain ASCII characters. If you pass a string with characters outside of this range, such as ‘é’ or ‘ü’, the function will throw a DOMException error.

For example, trying to encode the string "Hello, 世界!" will fail:

// This will cause an error.
btoa('Hello, 世界!');
Output
Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

Later in this article, we will cover a method to handle Unicode characters correctly.

Decoding a Base64 String with atob()

To decode a Base64-encoded string, you can use the atob() function. This function performs the reverse operation of btoa(), converting a Base64 string back into its original representation.

The name atob comes from the old Unix command for converting “ASCII to binary”.

Using the encoded string from the previous example, 'SGVsbG8gV29ybGQh', let’s decode it back to the original text.

// Define a Base64 encoded string.
const stringToDecode = 'SGVsbG8gV29ybGQh';

// Decode the string using atob().
const decodedString = atob(stringToDecode);

console.log(decodedString);

The output confirms that the string was successfully decoded:

Output
Hello World!

Just like btoa(), atob() is designed to work with strings that fall within the ASCII character set.

Handling Unicode and Non-ASCII Characters

Since btoa() and atob() do not support Unicode characters, you need a different approach for internationalized applications. The recommended solution involves using the TextEncoder and TextDecoder APIs, which are built to handle a wide range of character encodings, including UTF-8.

This process requires two steps:

  1. First, encode the Unicode string into a sequence of UTF-8 bytes using TextEncoder.
  2. Then, convert those bytes into a Base64 string.

The following functions provide a reliable way to encode and decode UTF-8 strings to and from Base64.

// Function to encode a UTF-8 string to Base64
function utf8ToBase64(str) {
    const encoder = new TextEncoder();
    const data = encoder.encode(str);

    const binaryString = String.fromCharCode.apply(null, data);
    return btoa(binaryString);
}

// Function to decode a Base64 string to UTF-8
function base64ToUtf8(b64) {
    const binaryString = atob(b64);
    // Create a Uint8Array from the binary string.
    const bytes = new Uint8Array(binaryString.length);
    for (let i = 0; i < binaryString.length; i++) {
        bytes[i] = binaryString.charCodeAt(i);
    }
    const decoder = new TextDecoder();
    return decoder.decode(bytes);
}

// Example usage
const unicodeString = 'Hello, 世界!';
const encodedUnicode = utf8ToBase64(unicodeString);
console.log(`Encoded: ${encodedUnicode}`);

const decodedUnicode = base64ToUtf8(encodedUnicode);
console.log(`Decoded: ${decodedUnicode}`);

Running this code produces the following output:

Output
Encoded: SGVsbG8sIOS4lueVjCE= Decoded: Hello, 世界!

This method is more robust and suitable for applications that handle text in multiple languages.

Node.js Considerations

In a Node.js environment, the btoa() and atob() functions are not the standard way to handle Base64. While they have been added as global functions in recent versions for compatibility, the idiomatic and more powerful approach is to use the built-in Buffer object.

The Buffer API is designed to handle binary data efficiently. It can convert data between different encodings, including utf8 and base64.

Here’s how to perform Base64 encoding and decoding in Node.js:

// Example string, which can include Unicode characters
const originalString = "Hello, 世界! 😊";

// Encode to Base64
// 1. Create a Buffer from the string, specifying its original encoding (utf-8).
const buffer = Buffer.from(originalString, 'utf-8');
// 2. Convert the Buffer to a Base64-encoded string.
const encodedString = buffer.toString('base64');

console.log(`Encoded: ${encodedString}`);


// Decode from Base64
// 1. Create a Buffer from the Base64 string, specifying its encoding.
const decodedBuffer = Buffer.from(encodedString, 'base64');
// 2. Convert the Buffer back to a UTF-8 string.
const decodedString = decodedBuffer.toString('utf-8');

console.log(`Decoded: ${decodedString}`);

Output:

Encoded: SGVsbG8sIOS4lueVjCEg8J+YgA==
Decoded: Hello, 世界! 😊

The Buffer API is the recommended method for all Base64 operations in Node.js because it’s robust, handles Unicode natively, and is integrated with other Node.js APIs like fs for file I/O.

Feature btoa() / atob() (Browser) Buffer (Node.js)
Environment Browser Node.js
Unicode Support No (requires workaround) Yes (natively handles UTF-8)
Data Types Strings (Latin1) Binary data, strings, files
Primary Use Simple string encoding/decoding High-performance binary data manipulation

Practical Use Cases

Base64 is useful in specific scenarios where binary data must be handled within text-based systems. Applying it thoughtfully is key to avoiding its drawbacks, such as increased data size.

Embedding Assets in HTML and CSS (Data URIs)

One of the most common uses of Base64 is to embed small files directly into HTML or CSS using Data URIs. This technique is particularly effective for small images, icons, and fonts that are critical for the initial rendering of a page.

  • How it works: By encoding a file into Base64 and embedding it, you eliminate the need for the browser to make a separate HTTP request to fetch that asset. This can reduce the initial page load time.

  • HTML Example: Instead of linking to an image file, you can embed it directly in an <img> tag.

    <img src="/path/to/logo.png" alt="My Logo">
    
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." alt="My Logo">
    
  • CSS Example: You can also embed background images in your stylesheets.

    .icon-user {
    background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ....");
    }
    
  • Trade-off: While this reduces HTTP requests, it increases the size of the HTML or CSS file. This can also negatively impact caching, as the embedded asset cannot be cached separately from the document it’s in. This method is best reserved for very small and critical assets.

Transmitting Binary Data in Text-Based Payloads (JSON/XML)

APIs that use text-based formats like JSON or XML cannot handle raw binary data. If you need to send a file, such as a user-uploaded profile picture or a generated PDF, through such an API, Base64 is the standard solution.

  • How it works: The binary file is encoded into a Base64 string on the client side. This string is then included as a standard field in the JSON object. The server receives the JSON, extracts the Base64 string, and decodes it back into the original binary file.

  • JSON Example: A payload for creating a new user might include a profile picture.

    {
    "username": "alex_doe",
    "email": "alex@example.com",
    "profilePicture": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAIBAQIBAQ...",
    "pictureMimeType": "image/jpeg"
    }
    

HTTP Basic Authentication

The HTTP Basic Authentication scheme uses Base64 to encode user credentials. The username and password are combined into a username:password string, which is then Base64-encoded and sent in the Authorization header.

  • How it works: The client encodes the credentials and sends them with every request.

    Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
    

    The server decodes dXNlcm5hbWU6cGFzc3dvcmQ= back to username:password to authenticate the user.

  • Important Note: This method is not secure over an unencrypted HTTP connection because Base64 is easily reversible. It should only be used over HTTPS, where the entire request, including the headers, is encrypted.

Storing Binary Data in Text-Only Fields

Some systems, like certain database configurations, legacy systems, or configuration file formats (e.g., .env files), only support text. Base64 allows you to store small binary objects in these systems.

  • Use Case: You might need to store a small thumbnail image, a configuration key file, or a digital signature directly in a database text column.

  • Example: Storing a small avatar icon in a database TEXT field. The application would encode the image to Base64 before saving and decode it after retrieval.

  • Trade-off: This is inefficient for large files. For anything more than a few kilobytes, it’s better to store the file in a dedicated object storage service (like Amazon S3 or Google Cloud Storage) and save only the file’s URL or identifier in the database.

Pitfalls and Best Practices

While Base64 is a convenient standard, its misuse can lead to security vulnerabilities and performance issues. Understanding its limitations is key to using it correctly.

Security: Base64 is Not Encryption

A common misconception is that the scrambled appearance of a Base64 string provides security. This is incorrect and can lead to dangerous implementations.

  • The Pitfall: Base64 is an encoding scheme, which means it’s a publicly known, reversible process for representing data. It is not an encryption algorithm. There is no secret key required to decode it; anyone with the encoded string can trivially convert it back to its original form. Relying on Base64 to protect sensitive data like passwords, API keys, or personal user information provides a false sense of security.

  • Best Practice:

    • Never use Base64 to hide or secure sensitive data.
    • For data confidentiality, use proper encryption algorithms like AES (Advanced Encryption Standard).
    • For storing passwords, use a strong, salted, one-way hashing algorithm like Argon2 or bcrypt.
    • Always transmit data over a secure channel like HTTPS, which encrypts the entire communication pipeline, but remember this does not protect the data if it is stored or logged in its Base64-encoded form.

Performance: Data Size and Bandwidth

Base64 encoding always makes data larger. The formula is simple: 3 bytes of binary data are represented as 4 characters of text.

  • The Pitfall: This results in a size increase of approximately 33% (plus a small amount for padding). While this overhead may be negligible for a few kilobytes of data, it becomes a significant problem for larger files.
    • A 100 KB image becomes roughly 133 KB.
    • A 10 MB video file becomes roughly 13.3 MB. This increase leads to higher bandwidth consumption, longer transfer times for users, and increased storage costs.

Best Practices

  • Avoid Base64 for large files. A general guideline is to question its use for anything larger than a few dozen kilobytes.
  • For file transfers, especially user uploads, use multipart/form-data. This is the standard HTTP mechanism for sending binary data efficiently without encoding overhead.
  • For serving large media assets, link to them directly and let the browser handle fetching and caching them.

Performance: CPU Overhead

The process of converting binary data to a Base64 string (and back) requires computational resources.

  • The Pitfall: While modern CPUs can handle Base64 operations very quickly for small amounts of data, this can become a performance bottleneck in certain scenarios.

    • Client-Side: Encoding a large file (e.g., a multi-megabyte image) in the browser can cause the UI to become unresponsive or “freeze” temporarily.
    • Server-Side: An API that frequently encodes or decodes large Base64 payloads can experience high CPU load, increasing response times and server costs.
  • Best Practice:

    • Profile your application. If you notice performance issues related to data handling, check if Base64 operations are a contributing factor.
    • Whenever possible, work with binary data directly using constructs like Blob in the browser or Buffer streams in Node.js to avoid unnecessary encoding and decoding cycles.

Caching: The Impact of Embedded Data

Embedding assets as Base64 data URIs can interfere with standard browser caching mechanisms.

  • The Pitfall: When an asset (like an image or font) is embedded in a CSS or HTML file, it becomes part of that file. The browser cannot cache the asset separately. If any part of the parent file changes, the entire file (including the embedded Base64 string) must be re-downloaded. In contrast, an externally linked asset can be cached independently for a long time.

  • Best Practice:

    • Use data URIs for small, critical assets that are essential for the initial page render (e.g., a logo or an above-the-fold icon).
    • For larger, non-critical, or frequently used assets, link to them as separate files. This allows the browser to cache them efficiently, improving load times on subsequent page visits.

FAQs

1. How do I Base64 encode a string in JavaScript?

To Base64 encode a string, the method depends on your environment.

In a web browser, use the built-in btoa() function. This function works well for strings containing only Latin1 characters.

const originalString = "Hello, World!";
const encodedString = btoa(originalString);

console.log(encodedString); // "SGVsbG8sIFdvcmxkIQ=="

In Node.js, the standard approach is to use the Buffer object, which correctly handles all character sets, including Unicode.

const originalString = "Hello, World!";
const encodedString = Buffer.from(originalString, "utf-8").toString("base64");

console.log(encodedString); // "SGVsbG8sIFdvcmxkIQ=="

2. How do I decode a Base64 string in JavaScript?

To decode a Base64 string, you use the counterpart method for your environment.

In a web browser, use the atob() function.

const encodedString = "SGVsbG8sIFdvcmxkIQ==";
const decodedString = atob(encodedString);

console.log(decodedString); // "Hello, World!"

In Node.js, you again use the Buffer object, specifying the input encoding as base64.

const encodedString = "SGVsbG8sIFdvcmxkIQ==";
const decodedString = Buffer.from(encodedString, "base64").toString("utf-8");

console.log(decodedString); // "Hello, World!"

3. What is the difference between btoa() and atob()?

btoa() and atob() are browser functions that perform opposite operations:

  • btoa() (binary-to-ASCII): Encodes a string into its Base64 representation.
  • atob() (ASCII-to-binary): Decodes a Base64 string back into its original form.

Think of them as a pair: what you encode with btoa(), you can decode with atob().

const encoded = btoa("Test String");  // Creates Base64 string: "VGVzdCBTdHJpbmc="
const decoded = atob(encoded);      // Converts it back to: "Test String"

4. How do I handle Unicode strings in Base64 with JavaScript?

The browser’s btoa() function will throw an error if you pass it a string containing characters outside the Latin1 range, such as emojis or non-English characters.

To handle Unicode, you must first convert the string into a sequence of UTF-8 bytes that btoa() can process. The modern way to do this is with TextEncoder.

Here is a reliable helper function for encoding Unicode strings in the browser:

function unicodeToBase64(str) {
  const utf8Bytes = new TextEncoder().encode(str);
  const binaryString = String.fromCharCode(...utf8Bytes);
  return btoa(binaryString);
}

const unicodeString = "你好, world!";
const encoded = unicodeToBase64(unicodeString);
console.log(encoded); // "5L2g5aW9LCB3b3JsZCEg8J+YgA=="

In Node.js, this is not an issue, as the Buffer API handles Unicode natively.

5. How do I Base64 encode in Node.js?

In Node.js, you should always use the built-in Buffer object for Base64 operations. It is designed for handling binary data and supports various encodings, including UTF-8, without any workarounds.

  • To encode: Create a Buffer from your string (specifying its encoding, usually utf-8) and then convert the buffer to a base64 string.
  • To decode: Create a Buffer from your Base64 string (specifying its encoding is base64) and then convert the buffer back to a utf-8 string.
// A string with Unicode characters
const originalString = "你好, world!";

// Encode to Base64
const encoded = Buffer.from(originalString, "utf-8").toString("base64");
console.log(`Encoded: ${encoded}`);

// Decode from Base64
const decoded = Buffer.from(encoded, "base64").toString("utf-8");
console.log(`Decoded: ${decoded}`);

Output:

Encoded: 5L2g5aW9LCB3b3JsZCEg8J+YgA==
Decoded: 你好, world!

Conclusion

In this tutorial, we covered the use of btoa() and atob() for Base64 encoding and decoding in JavaScript. While these functions are effective for ASCII strings, they cannot handle the Unicode characters frequently used in modern web applications.

For browser-based applications, the TextEncoder and TextDecoder APIs provide a reliable method for working with UTF-8. In Node.js environments, the built-in Buffer module is the correct tool for these operations because it is designed to handle multiple character encodings, including Base64 and UTF-8.

By selecting the appropriate tool for your environment, you can prevent common data corruption errors and build more dependable applications.

To explore more JavaScript-related concepts, check out the following articles:

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the author(s)

Nicholas Cerminara
Nicholas Cerminara
Author
Anish Singh Walia
Anish Singh Walia
Editor
Sr Technical Writer
See author profile

I help Businesses scale with AI x SEO x (authentic) Content that revives traffic and keeps leads flowing | 3,000,000+ Average monthly readers on Medium | Sr Technical Writer @ DigitalOcean | Ex-Cloud Consultant @ AMEX | Ex-Site Reliability Engineer(DevOps)@Nutanix

Manikandan Kurup
Manikandan Kurup
Editor
Senior Technical Content Engineer I
See author profile

With over 6 years of experience in tech publishing, Mani has edited and published more than 75 books covering a wide range of data science topics. Known for his strong attention to detail and technical knowledge, Mani specializes in creating clear, concise, and easy-to-understand content tailored for developers.

Category:

Still looking for an answer?

Was this helpful?


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

doesn’t work for unicode

This comment has been deleted

These functions are depreciated.

Creative CommonsThis work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 4.0 International License.
Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.