JavaScript Encryption Libraries: Top Picks for Developers
Compare the best JavaScript encryption libraries for web applications. From tweetnacl to libsodium.js, find the right crypto library for your file transfer project.
The best JavaScript encryption libraries for file transfer work in 2026 are libsodium.js for broad coverage and WASM speed, @noble/ciphers and @noble/curves for pure-JS implementations with modern audits, tweetnacl for small bundle size with NaCl-compatible primitives, crypto-js for legacy compatibility (avoid for new code), and the native Web Crypto API for anything it supports. This guide compares them on algorithm coverage, bundle size, audit history, browser versus Node compatibility, and real-world performance on file encryption workloads. The right pick depends on whether you're shipping to browsers, building on Node, needing ChaCha20-Poly1305 or Argon2, or can live inside Web Crypto's bounds.
Comparison Table
| Library | Size (gzipped) | Backend | Key algorithms | Audited | Modern? | |---|---|---|---|---|---| | Web Crypto API | 0 KB (native) | Browser native | AES-GCM, PBKDF2, RSA, ECDH, HMAC | Yes (by browser vendors) | Yes, but no ChaCha/Argon2 | | libsodium.js | 200 KB WASM / 400 KB JS | WASM + JS fallback | Everything in libsodium | Yes (multiple) | Yes | | @noble/ciphers | 8 KB | Pure JS | AES, ChaCha20, Poly1305, GCM-SIV | Yes (Cure53 2023) | Yes | | @noble/curves | 35 KB | Pure JS | Ed25519, X25519, secp256k1, BLS | Yes (Trail of Bits, Cure53) | Yes | | tweetnacl | 15 KB | Pure JS | Curve25519, Ed25519, XSalsa20-Poly1305 | Yes (original NaCl audit) | Mostly, lacks ChaCha20 | | crypto-js | 50 KB | Pure JS | AES-CBC, SHA, HMAC, PBKDF2 | No current audit | No, unmaintained since 2023 | | node:crypto | 0 KB (Node native) | Node native (OpenSSL) | Comprehensive | Yes (OpenSSL) | Yes |
libsodium.js: The Swiss Army Knife
libsodium.js (the Emscripten-compiled build of libsodium) is the default pick when Web Crypto isn't enough. It covers XChaCha20-Poly1305, Ed25519, X25519, Argon2id, BLAKE2b, and the crypto_secretstream API for streaming AEAD. The WASM build runs at roughly 50-70% of native libsodium speed on typical hardware.
import _sodium from 'libsodium-wrappers';
await _sodium.ready;
const sodium = _sodium;
const key = sodium.crypto_secretstream_xchacha20poly1305_keygen();
const { state, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
const chunk = sodium.crypto_secretstream_xchacha20poly1305_push(
state, new Uint8Array([1,2,3]), null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
);
The streaming API is the killer feature for large file transfers. You can encrypt a 5 GB file chunk by chunk without loading it all into memory, and each chunk carries its own authentication tag so corruption is detected per chunk, not only at the end.
Trade-offs: 200 KB WASM is a lot to ship. Use dynamic imports (await import('libsodium-wrappers')) so the library only loads when encryption is actually needed. The sumo build (includes all algorithms) is 600 KB+; stick with the default build unless you need the extras.
@noble: Small, Audited, Modern
Paul Miller's @noble family (@noble/ciphers, @noble/curves, @noble/hashes) is the current best-in-class for pure-JavaScript crypto. Audited by Cure53 (ciphers, 2023) and Trail of Bits (curves, 2022), zero dependencies, tree-shakeable, TypeScript-native.
import { gcm } from '@noble/ciphers/aes';
import { randomBytes } from '@noble/ciphers/webcrypto';
const key = randomBytes(32);
const nonce = randomBytes(12);
const ciphertext = gcm(key, nonce).encrypt(plaintext);
@noble doesn't use WASM, so bundle impact is small (8 KB for ciphers). Performance is 30-50% slower than libsodium's WASM for bulk AES-GCM, but good enough for most transfer scenarios. The pure-JS approach also means it works identically in browsers, Node, Deno, Bun, and React Native without native module concerns.
Use @noble when bundle size matters, when you want a tree-shakeable pure-JS library, or when you're on a platform where WASM has rough edges.
tweetnacl: The Minimalist
tweetnacl-js ports Daniel J. Bernstein's TweetNaCl C library to JavaScript. 15 KB gzipped. Covers Curve25519 key exchange, Ed25519 signatures, and XSalsa20-Poly1305 authenticated encryption. Audited as part of the original NaCl effort, though not recently.
import nacl from 'tweetnacl';
const key = nacl.randomBytes(32);
const nonce = nacl.randomBytes(24);
const ciphertext = nacl.secretbox(plaintext, nonce, key);
The API is deliberately tiny: if you need more than what NaCl provides (for example, AES-GCM for interop with non-JS systems), look elsewhere. For pure NaCl-protocol apps, tweetnacl is still a reasonable choice, though @noble/ciphers plus @noble/curves covers the same ground with more current maintenance.
crypto-js: Don't Use It for New Code
crypto-js dominated browser crypto in 2015. In 2026 it's a liability. Last meaningful release was 2021 (4.1.1); the repo was effectively archived in 2023. It defaults to AES-CBC with an insecure key derivation from passwords (OpenSSL's EVP_BytesToKey-style scheme), which derives keys via a few rounds of MD5. CVE-2023-46233 flagged weak PBKDF2 defaults.
If you're maintaining legacy code that uses crypto-js, migrating to @noble/ciphers or Web Crypto is straightforward and worth the effort. If you're starting fresh in 2026, skip crypto-js entirely.
node:crypto for Server Code
Node's built-in crypto module wraps OpenSSL and has the broadest algorithm coverage of anything on this list. On Node 18+, require('crypto').webcrypto exposes a Web Crypto-compatible API, so isomorphic code works server and client.
import { createCipheriv, randomBytes } from 'crypto';
const key = randomBytes(32);
const iv = randomBytes(12);
const cipher = createCipheriv('aes-256-gcm', key, iv);
const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
const tag = cipher.getAuthTag();
For server-side decryption of Web Crypto-encrypted files, this is the cleanest path. If your file transfer service decrypts anything server-side (rare in zero-knowledge designs, but common in legacy systems migrating to encryption), use node:crypto.
Performance on Real File Encryption
Benchmarks on a MacBook Air M2 encrypting a 100 MB buffer with AES-256-GCM:
- Web Crypto API (Chrome 120): 340 ms (hardware-accelerated)
- Web Crypto API (Safari 17): 280 ms (hardware-accelerated on Apple Silicon)
- libsodium.js WASM: 420 ms
- @noble/ciphers: 1,850 ms (no hardware acceleration in pure JS)
- tweetnacl (XSalsa20): 1,200 ms
- node:crypto: 180 ms (native OpenSSL)
Takeaway: Web Crypto wins for large files because it uses AES-NI. Pure JS is fine for small payloads but adds seconds on multi-gigabyte transfers. For a 5 GB upload, the difference between Web Crypto and @noble/ciphers could be 2 minutes versus 10 minutes of encryption time.
Password Hashing: Argon2 or Bust
PBKDF2 is table stakes but Argon2id is better. Options:
- argon2-browser: WASM-compiled reference implementation, 200 KB
- @noble/hashes: pure-JS Argon2id, 15 KB, slower but pure
- libsodium.js: Argon2id via
crypto_pwhash, 200 KB but you already have it if using libsodium
Use Argon2id with at least 3 iterations, 64 MiB memory, and 4 parallelism for password-derived encryption keys in browser contexts (OWASP 2024 guidance).
Picking for Your Stack
- You're building a simple file transfer with AES-GCM encryption: use Web Crypto directly, no library. HexaTransfer follows this pattern.
- You need ChaCha20-Poly1305 or streaming AEAD: libsodium.js.
- You're paranoid about WASM or ship to constrained bundlers: @noble/ciphers + @noble/curves.
- You're using NaCl-protocol keys (e.g., sealed boxes for receipt of encrypted files): @noble or libsodium, not crypto-js.
- You're migrating from crypto-js: move to @noble if bundle size matters, libsodium.js otherwise.
- You need post-quantum signatures or KEM: no mature JS library yet; check @noble/post-quantum (pre-release as of early 2026).
Audit and Maintenance Signals
Before adopting a crypto library, check:
- Last commit date (anything over 18 months stale is risky)
- Public audit reports (Cure53, Trail of Bits, NCC Group)
- Issue tracker for unresolved security issues
- Dependency count (fewer = smaller attack surface)
- npm weekly downloads (higher = more eyes on the code)
The JavaScript ecosystem has had supply-chain incidents (event-stream, ua-parser-js, colors) that make minimal dependencies a security property. Crypto libraries with zero runtime dependencies (@noble family, tweetnacl) are easier to audit end-to-end than those pulling in polyfills and utilities.
Try it at hexatransfer.com — free, no account, 10 GB max.
Send large files securely with end-to-end encryption
Transfer files up to 10 GB for free with end-to-end encryption. No account required. Your files are encrypted in your browser before upload — no one else can read them.
Send a file