1 行でエンコードとデコード
この API は、Base64 用に 3 つ、16進数用に 3 つのメソッドを追加します。
| メソッド | 機能 |
|---|---|
bytes.toBase64(options) | Uint8Array を Base64 文字列に |
Uint8Array.fromBase64(string, options) | Base64 文字列を新しい Uint8Array に |
bytes.setFromBase64(string, options) | 既存の Uint8Array にデコード |
bytes.toHex() | Uint8Array を小文字の 16進数文字列に |
Uint8Array.fromHex(string) | 16進数文字列を新しい Uint8Array に |
bytes.setFromHex(string) | 既存の Uint8Array に 16進数をデコード |
テキストは先にバイト列にする必要があります。TextEncoder は常に UTF-8 を出力するので、Unicode も小細工なしで扱えます。
const bytes = new TextEncoder().encode("Grüße, 世界");
const b64 = bytes.toBase64();
console.log(b64); // "R3LDvMOfZSwg5LiW55WM"
const back = new TextDecoder().decode(Uint8Array.fromBase64(b64));
console.log(back); // "Grüße, 世界"
fetch().arrayBuffer() や crypto.subtle.digest() などから得た ArrayBuffer には、ビューが必要です:new Uint8Array(buffer).toBase64()。その他の型付き配列にはメソッドそのものがありません。Uint16Array の生のメモリをエンコードするには、new Uint8Array(u16.buffer, u16.byteOffset, u16.byteLength) のように包みます。
オプション:alphabet、omitPadding、lastChunkHandling
alphabet
"base64"(デフォルト)は + と / を使い、"base64url" は - と _ を使います。このオプションはエンコードとデコードの両方に適用され、一方のアルファベットに設定したデコーダーは、もう一方のアルファベットの文字を拒否します。
const data = new Uint8Array([251, 255]);
data.toBase64(); // "+/8="
data.toBase64({ alphabet: "base64url" }); // "-_8="
data.toBase64({ alphabet: "base64url", omitPadding: true }); // "-_8"
Uint8Array.fromBase64("-_8", { alphabet: "base64url" }); // Uint8Array [251, 255]
Uint8Array.fromBase64("-_8"); // SyntaxError
omitPadding
toBase64 は、Base64URL のアルファベットでもデフォルトで = のパディングを付けます。JWT 形式の出力が必要なら omitPadding: true を渡してください。デコード時のパディングは lastChunkHandling で制御します。
lastChunkHandling
このオプションは、fromBase64 と setFromBase64 が最後の文字のグループをどう扱うかを決めます。Hi?> のバイト列にデコードされる入力で 3 つのモードを試した結果です。
| 入力 | "loose"(デフォルト) | "strict" | "stop-before-partial" |
|---|---|---|---|
SGk/Pg== | Hi?> | Hi?> | Hi?> |
SGk/Pg(パディングなし) | Hi?> | SyntaxError | Hi?(Pg の手前で停止) |
SGk/Ph==(パディングのビットがゼロでない) | Hi?> | SyntaxError | Hi?> |
SGk/P(1 文字だけ余る) | SyntaxError | SyntaxError | Hi? |
- loose は
atobと同じように振る舞います。パディングは任意で、最後の文字の余ったビットは無視されます。 - strict は、RFC 4648 が定める正規でパディング付きの入力だけを受け付けます。Base64 文字列を比較したり署名したりする場合など、エンコード後の形が一意でなければならないときに使います。
- stop-before-partial は、完全な 4 文字のグループをすべてデコードし、不完全な末尾には手を付けません。グループの残りが次のチャンクで届くストリーミング処理のためのモードです。
どのモードでも ASCII の空白(スペース、タブ、改行)は読み飛ばされるので、MIME 形式で折り返された入力もそのままデコードできます。それ以外のアルファベット外の文字は SyntaxError になります。
既存のバッファーに書き込む
setFromBase64 と setFromHex は、手元にある Uint8Array にデコードし、どれだけ使ったかを返します。
const target = new Uint8Array(8);
const result = target.setFromBase64("SGVsbG8gd29ybGQ=");
console.log(result); // { read: 8, written: 6 }
Hello world は 11 バイトで、書き込み先は 8 バイトです。デコーダーは収まる 3 バイト単位のグループしか書き込まないので、6 バイトで止まり、8 文字を読んだと報告します。続けるには、文字列の残りとターゲットの subarray を渡してもう一度呼び出します。stop-before-partial と組み合わせれば、ストリームをチャンクごとに、あらかじめ確保したバッファーへデコードできます。
const out = new Uint8Array(1024);
const { read, written } = out.setFromBase64("SGk/Pg", {
lastChunkHandling: "stop-before-partial",
});
// read: 4, written: 3. Keep "Pg" and prepend it to the next chunk.
16進数のメソッドも同じパターンです。toHex() は常に小文字で書き、fromHex() は大文字も小文字も受け付け、桁数が奇数だと SyntaxError になります。
new Uint8Array([0xde, 0xad, 0xbe, 0xef]).toHex(); // "deadbeef"
Uint8Array.fromHex("CAFE"); // Uint8Array [202, 254]
btoa と atob が扱いにくい理由
btoa と atob はブラウザの初期の時代からあるもので、「バイナリ文字列」、つまりすべての文字コードが 0〜255 のバイト値である JavaScript の文字列を扱います。これが 3 つの問題を引き起こします。
Unicode は例外になるか、黙っておかしくなる。 U+00FF より大きい文字があると btoa は InvalidCharacterError を投げます(Chrome では「The string to be encoded contains characters outside of the Latin1 range」)。U+0080 から U+00FF までの文字はさらにたちが悪く、エラーなしに Latin-1 としてエンコードされます。btoa("Grüße") は R3L832U= を返しますが、他のあらゆるシステムが期待する UTF-8 でのエンコード結果は R3LDvMOfZQ== です。
デコード結果がバイト列ではなく文字列になる。 atob("Q2Fmw6k=") は "Café" を返します。本来のテキストを得るには、文字列をバイト列に変換してから UTF-8 としてデコードする必要があり、ループか Uint8Array.from(atob(s), c => c.charCodeAt(0)) が要ります。
大きな入力には注意が必要。 よく使われるワンライナー btoa(String.fromCharCode(...bytes)) はすべてのバイトを関数の引数として渡すため、1 MB の配列では RangeError: Maximum call stack size exceeded が発生します。文字列はチャンクに分けて組み立てる必要があります。
さらに、Base64URL への対応も、パディングや厳格さの制御もありません。昔からある回避策 btoa(unescape(encodeURIComponent(text))) は確かに UTF-8 の Base64 を出力しますが、非推奨の unescape に頼っています。新しい API なら、これらはすべて new TextEncoder().encode(text).toBase64() で済みます。
ブラウザと Node.js の対応状況
これらのメソッドは TC39 の提案「Uint8Array to/from Base64」に由来し、2025 年 7 月にステージ 4 に到達して ECMAScript 2026 に含まれています。
| ランタイム | 6 つのメソッドすべてを備えた最初のバージョン |
|---|---|
| Chrome、Edge | 140 |
| Firefox | 133 |
| Safari(macOS と iOS) | 18.2 |
| Node.js | 25.0.0 |
Chrome 140 によって、この機能は 2025 年 9 月に Baseline になり、現行の主要ブラウザはすべて対応しています。Node.js では、メソッドを直接テストしました。Node 25.0.0 と 26.12.1 は 6 つすべてを備えています。LTS リリースの Node 24.22.1 は、デフォルトではどれも備えていません。V8 は実験的な --js-base-64 フラグを受け付けますが、私たちのテストでは、このフラグで使えるようになったのは toBase64、fromBase64、toHex、fromHex で、setFromBase64 と setFromHex はありませんでした。Node 24 は非対応とみなしてください。Node 24 以前では Buffer.from(b64, "base64") と buf.toString("base64") を使います。こちらも "base64url" を受け付けます。MDN の互換性データによれば、Deno 2.5 と Bun 1.1.22 もこの API に対応しています。
古いランタイム向けのフォールバック
機能を検出し、なければチャンク分割した btoa/atob にフォールバックします。このコードは Node 24(フォールバック側)と Node 26(ネイティブ側)で、UTF-8 テキストと 1 MB の配列を使ってテストしました。
function bytesToBase64(bytes) {
if (typeof bytes.toBase64 === "function") return bytes.toBase64();
let bin = "";
for (let i = 0; i < bytes.length; i += 0x8000) {
bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
}
return btoa(bin);
}
function base64ToBytes(b64) {
if (typeof Uint8Array.fromBase64 === "function") return Uint8Array.fromBase64(b64);
return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
}
このフォールバックはデフォルトのオプションにしか対応していません。base64url、strict、setFromBase64 をどこでも使いたいなら、完全なポリフィルを使ってください。core-js は es.uint8-array.to-base64、es.uint8-array.from-base64 などのモジュールとして提供しています。
コードを書かずにエンコードを試すなら、Base64 エンコーダーとデコーダー、URLセーフなアルファベットなら Base64URL をお使いください。Node の Buffer を含む JavaScript の例は、JavaScript での Base64 と Node.js での Base64 にあります。
よくある質問
新しい Base64 API は今すぐ本番で使えますか?
ブラウザなら使えます。Chrome と Edge 140、Firefox 133、Safari 18.2 で搭載されています。Node.js ではバージョン 25 以降が必要です。古いブラウザや Node 24 以前をサポートするなら、フォールバックを用意してください。
Uint8Array.fromBase64 は Base64URL を受け付けますか?
はい、{ alphabet: "base64url" } を指定すれば受け付けます。このオプションがないと、- と _ で SyntaxError になります。デフォルトの loose モードでは、どちらのアルファベットでもパディングは任意です。
toBase64 で文字列をエンコードするにはどうすればいいですか?
先に UTF-8 のバイト列にします:new TextEncoder().encode(text).toBase64()。デコードは逆に new TextDecoder().decode(Uint8Array.fromBase64(b64)) とします。
Buffer や Uint16Array で toBase64 が関数でないと言われるのはなぜですか?
これらのメソッドは Uint8Array とそのサブクラスにしかありません。Node の Buffer には Node 25 以降で備わります。その他の型付き配列は、メモリを包んでください:new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength)。