Skip to content

Repository files navigation

react-native-fast-base64

Just another native implementation of Base64 for React Native, done in C++ with a NEON fast path and reached from JS through JSI. Drop-in replacement for btoa/atob and for JS Base64 libraries such as base64-js.

Installation

npm install react-native-fast-base64
cd ios && pod install

Usage

import {
  encode,
  decode,
  btoa,
  atob,
  fromByteArray,
  toByteArray,
} from 'react-native-fast-base64';

encode('Hello, world!');            // 'SGVsbG8sIHdvcmxkIQ=='
decode('SGVsbG8sIHdvcmxkIQ==');     // 'Hello, world!'
encode('foob', true);               // 'Zm9vYg'  (URL-safe, unpadded)

fromByteArray(new Uint8Array([1, 2, 3]));  // 'AQID'
toByteArray('AQID');                       // Uint8Array(3) [1, 2, 3]

To shim the globals, import once at the top of your entry file:

import 'react-native-fast-base64/shim';

API

Function Takes Returns
encode(text, urlSafe?) text, encoded as its UTF-8 bytes Base64 string
decode(base64) Base64 string decoded bytes, read as UTF-8 text
btoa(binary) binary string, one character per byte Base64 string
atob(base64) Base64 string binary string, one character per byte
fromByteArray(bytes, urlSafe?) Uint8Array Base64 string
toByteArray(base64) Base64 string Uint8Array
isLoaded() whether the JSI bindings installed

Passing urlSafe swaps +/ for -_ and drops the padding. Decoding accepts either alphabet, padded or not, so you never have to say which you have, and ignores ASCII whitespace, so MIME- and PEM-wrapped input decodes as it stands instead of needing a replace first.

Text vs binary

encode/decode are text helpers: they convert through UTF-8. btoa/atob are binary-string helpers, one character per byte over the full 0x00-0xFF range, exactly as the DOM defines them. Use btoa/atob (or the byte-array functions) for anything that is not text — routing binary data through the UTF-8 pair corrupts every byte above 0x7F.

Invalid Base64 throws instead of returning an empty string.

Performance

Run it yourself with yarn example ios or yarn example android and tap Run benchmark. Median of three runs, debug builds, Hermes.

iPhone 17 Pro simulator:

Payload fast-base64 quick-base64 base64-js Buffer
1 KiB encode 1788 MB/s 614 MB/s 11 MB/s 11 MB/s
decode 1338 MB/s 257 MB/s 10 MB/s 3 MB/s
64 KiB encode 2541 MB/s 845 MB/s 10 MB/s 10 MB/s
decode 1627 MB/s 320 MB/s 10 MB/s 3 MB/s
1 MiB encode 1465 MB/s 745 MB/s 11 MB/s 11 MB/s
decode 2939 MB/s 327 MB/s 10 MB/s 3 MB/s

At 16 bytes every option collapses to roughly 30 MB/s, because the per-call cost across the JS boundary dominates the codec itself.

Where the remaining speed comes from:

  • NEON on arm64 -- 48 bytes per encode iteration, 64 characters per decode iteration. Other ABIs use the scalar path, which writes through a raw pointer rather than pushing character by character.
  • One decode table for both alphabets, storing value + 1 so invalid input is a zero test rather than a branch per character.
  • toByteArray hands its buffer to JS without a second copy, and fromByteArray passes a view's offset and length across instead of slicing a copy in JS first.

Compatibility

React Native 0.71 and newer, old architecture and new, bridgeless included. There is one code path: a small native module installs the JSI bindings, and every function is a host function on the JS runtime, so no call crosses the bridge on any architecture.

Testing

yarn test        # C++ codec self-check, then the JS tests
yarn test:cpp    # codec only, runs the SIMD and scalar paths

Contributing

See the contributing guide.

License

MIT

About

Just another native implementation of Base64 in C++ for React-Native

Resources

Code of conduct

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages