crypto-js, js-sha256, sha.js, and sha256 are JavaScript libraries that provide SHA-256 cryptographic hashing functionality for use in browser environments. These packages enable developers to generate secure hashes from strings, typed arrays, or other data sources without relying on native Web Crypto APIs, which may not be available or suitable in all contexts. While all four support the SHA-256 algorithm, they differ significantly in scope, API design, implementation approach, and maintenance status.
When you need to compute SHA-256 hashes in the browser — for password pre-hashing, integrity checks, or blockchain-related tasks — you’ll likely consider one of these four libraries. They all promise SHA-256, but their design philosophies, APIs, and suitability vary widely. Let’s break them down.
sha256First, the bad news: sha256 is deprecated. Its npm page explicitly states: "This package is no longer maintained." It hasn’t been updated since 2016 and only accepts strings as input. For any new project, skip this entirely.
// sha256 (deprecated — do not use)
import sha256 from 'sha256';
const hash = sha256('hello'); // works only with strings
Now, let’s compare the three viable options.
js-sha256: Simple Function CallsThis library gives you a direct, stateless function. Pass in data, get a hex string back. No instantiation, no chaining.
// js-sha256
import { sha256 } from 'js-sha256';
const hash1 = sha256('hello');
const hash2 = sha256(new Uint8Array([104, 101, 108, 108, 111])); // also supports typed arrays
It also offers a .create() method if you need incremental updates:
const hasher = sha256.create();
hasher.update('he');
hasher.update('llo');
const final = hasher.hex();
crypto-js: Word Arrays and Method Chainingcrypto-js uses its own WordArray type internally. You typically pass strings or typed arrays, but the output is a WordArray object that you must convert to a string.
// crypto-js
import CryptoJS from 'crypto-js';
const hash = CryptoJS.SHA256('hello').toString(); // toString() gives hex
// Or with explicit encoding
const hash2 = CryptoJS.SHA256(CryptoJS.enc.Utf8.parse('hello')).toString();
It’s more verbose but integrates with other crypto-js features like HMAC:
const hmac = CryptoJS.HmacSHA256('message', 'key').toString();
sha.js: Stream-Like Interface (Node.js Style)sha.js mimics Node’s crypto.createHash('sha256') API. You create a hasher instance, feed it data, then call .digest().
// sha.js
import { sha256 } from 'sha.js';
const hasher = sha256();
hasher.update('hello');
const hash = hasher.digest('hex'); // specify output format
It also supports piping and works seamlessly in Node.js:
// Same code runs in Node.js and browser
const hash = require('sha.js').sha256().update('hello').digest('hex');
All three active libraries accept strings, but their support for binary data differs.
js-sha256: Accepts string, Array<number>, Uint8Array, and ArrayBuffer. Very flexible for modern web apps dealing with files or crypto keys.sha256(new Uint8Array([72, 101, 108, 108, 111]));
crypto-js: Accepts strings and WordArray. To use Uint8Array, you must first convert it:const wordArray = CryptoJS.lib.WordArray.create(uint8Array);
const hash = CryptoJS.SHA256(wordArray).toString();
sha.js: Accepts string and Buffer (in Node) or Uint8Array (in browser via polyfill). In practice, you’ll often pass strings or convert binary data to buffers.hasher.update(new Uint8Array([72, 101, 108, 108, 111]));
Need to hash large files in chunks? All three support it, but with different ergonomics.
js-sha256:const hasher = sha256.create();
hasher.update(chunk1);
hasher.update(chunk2);
const result = hasher.hex();
crypto-js does not support incremental updates for SHA-256. Each call to SHA256() is stateless. You’d need to concatenate all data first.
sha.js:
const hasher = sha256();
hasher.update(chunk1);
hasher.update(chunk2);
const result = hasher.digest('hex');
So if you’re processing file uploads or streams, avoid crypto-js for this use case.
js-sha256: Pure ES modules, works everywhere modern JS runs. No dependencies.crypto-js: Built for browsers, but can run in Node.js with bundlers. Uses its own encoding system.sha.js: Designed to mirror Node’s crypto API, so it’s ideal for isomorphic apps. Works in browsers via bundlers or direct inclusion.All three use well-reviewed, constant-time(ish) implementations suitable for non-cryptographic secret handling (e.g., client-side proof-of-work). However, never use client-side hashing as a substitute for server-side security — secrets can always be extracted from the browser.
None of these libraries use the native Web Crypto API (window.crypto.subtle.digest), which is faster and more secure but requires async/await and doesn’t support incremental updates. If you only need one-off hashes and can use async, consider Web Crypto instead:
// Native Web Crypto (async, no external deps)
const buffer = new TextEncoder().encode('hello');
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
But if you need sync hashing, incremental updates, or IE11 support, stick with one of these libraries.
| Feature | crypto-js | js-sha256 | sha.js | sha256 |
|---|---|---|---|---|
| Maintenance | ✅ Active | ✅ Active | ✅ Active | ❌ Deprecated |
| SHA-256 Only? | ❌ (Many algos) | ✅ (SHA family) | ❌ (Multiple algos) | ✅ |
| Input Types | String, WordArray | String, Array, TypedArray | String, Buffer, Uint8Array | String only |
| Incremental Hashing | ❌ | ✅ | ✅ | ❌ |
| API Style | Object-oriented | Functional | Streaming (Node-like) | Functional |
| Bundle Impact | High | Very Low | Medium | Low (but old) |
js-sha256. It’s tiny, fast, supports modern data types, and has a dead-simple API.crypto-js for AES or HMAC: Stick with it for consistency, but know you’re pulling in extra weight.sha.js to keep your hashing logic identical across environments.sha256 — it’s outdated and unmaintained.Remember: client-side hashing is about convenience or protocol compliance, not security. Always validate and re-hash on the server when it matters.
Choose sha.js if you prefer a Node.js-compatible stream-based API that mirrors the standard crypto module and supports multiple hash algorithms including SHA-256. It’s useful when you need consistent hashing logic across both Node.js and browser environments, especially in isomorphic applications, though its streaming model may be overkill for simple one-off hashes.
Choose crypto-js if you need a full-featured cryptography library that includes SHA-256 alongside many other algorithms (AES, HMAC, PBKDF2, etc.) and utilities like encoders and word arrays. It’s well-suited for applications requiring multiple cryptographic operations beyond just hashing, though its broader feature set comes with increased bundle size and complexity.
Choose js-sha256 if you want a lightweight, zero-dependency, and modern implementation focused exclusively on SHA-256 (and related variants like SHA3-256). It supports strings, arrays, and typed arrays out of the box, offers a clean functional API, and is actively maintained — ideal for performance-sensitive frontend applications where minimal footprint matters.
Do not choose sha256 for new projects. The package is deprecated (as noted on its npm page) and unmaintained. While it works for basic string hashing, it lacks support for modern input types like Uint8Array, has no updates since 2016, and offers no advantages over actively maintained alternatives.
Node style SHA on pure JavaScript.
var shajs = require('sha.js')
console.log(shajs('sha256').update('42').digest('hex'))
// => 73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049
console.log(new shajs.sha256().update('42').digest('hex'))
// => 73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049
var sha256stream = shajs('sha256')
sha256stream.end('42')
console.log(sha256stream.read().toString('hex'))
// => 73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049
sha.js currently implements:
Note, this doesn't actually implement a stream, but wrapping this in a stream is trivial. It does update incrementally, so you can hash things larger than RAM, as it uses a constant amount of memory (except when using base64 or utf8 encoding, see code comments).
This work is derived from Paul Johnston's A JavaScript implementation of the Secure Hash Algorithm.