js-sha256
A dependency-free implementation of SHA-256, SHA-224 and their HMAC variants with the same small synchronous API in browsers and Node.js. It hashes strings as UTF-8 or accepts arrays, Uint8Array and ArrayBuffer, supports incremental updates, and can return hex, byte arrays or ArrayBuffer output. Version 1.0 uses Node's native crypto module on Node while shipping a JavaScript implementation for browsers, workers and direct script-tag use.
A focused and recently modernized cross-runtime SHA-256 API, especially good when browser-side incremental hashing is the requirement. Skip it for Node-only code, password storage, or applications that can use Web Crypto directly.
Use it if
- You need one synchronous SHA-256 or SHA-224 API that works across Node, browsers and workers
- You need incremental hashing for a file or stream but cannot rely on Web Crypto's one-shot digest API
- You maintain browser code that consumes a UMD global, CommonJS and ESM from the same package
- You need HMAC-SHA-256 with string or binary keys and several output formats without pulling in a general cryptography suite
- You only target Node: node:crypto already provides SHA-256 and HMAC, and version 1.0 delegates to that native module anyway
- You can use Web Crypto and an asynchronous one-shot digest fits your input: crypto.subtle.digest removes a third-party cryptography implementation from the browser bundle
- You are hashing passwords: SHA-256 is deliberately fast and the package provides no salt management or password-hardening work factor; use Argon2, scrypt or bcrypt instead
- You need algorithms beyond SHA-224 and SHA-256, such as SHA-512, SHA-3, BLAKE2 or BLAKE3; this package intentionally exposes only two hash families
- You need built-in signature verification or constant-time tag comparison: the HMAC API computes tags, but callers must compare them safely and manage secret keys themselves
Setup reality
npm install js-sha256 is the whole install, with no dependencies, peer packages, native compilation or runtime configuration. Version 1.0 has explicit package exports for ESM, CommonJS, Node and browser builds. Use named imports from the package root instead of old unpublished or improvised deep paths; the changelog says legacy generated entry points were removed. Node automatically receives an adapter backed by node:crypto, while browser imports execute the package's JavaScript core. The public calls are synchronous in both environments. Strings are encoded as UTF-8, so hash bytes directly when a protocol specifies exact octets or an existing text encoding. Incremental hashing does not open files or consume streams for you: read chunks, call update for each one, then select hex(), array(), digest() or arrayBuffer(). Calling an output method finalizes the hasher, and version 1.0 now throws if update is called afterward, so create a new instance for every message. HMAC keys receive the same string-or-bytes treatment as messages. The library does not generate keys, salt passwords, compare tags in constant time, canonicalize JSON, or protect secrets stored in frontend code. Those are application responsibilities, not missing setup flags.
Patterns
Hash a UTF-8 string with SHA-256hash-text
import { sha256 } from 'js-sha256';
const digest = sha256('Message to hash');
console.log(digest); // lowercase hexadecimalString input is encoded as UTF-8. If a protocol defines a different encoding or exact bytes, encode it yourself and pass a Uint8Array.
Produce a SHA-224 digesthash-sha224
import { sha224 } from 'js-sha256';
const digest = sha224('Message to hash');
console.log(digest);SHA-224 produces a shorter digest than SHA-256. Use the algorithm named by your protocol rather than choosing based only on output length.
Hash bytes without text conversionhash-binary-data
import { sha256 } from 'js-sha256';
const bytes = new Uint8Array([0xd3, 0xd4]);
const digest = sha256(bytes);Uint8Array, ArrayBuffer and number[] are accepted. Passing bytes avoids accidental UTF-8 encoding of a string representation.
Return hex, bytes or an ArrayBufferchoose-output-format
const hex = sha256.hex(data);
const bytes = sha256.array(data);
const sameBytes = sha256.digest(data);
const buffer = sha256.arrayBuffer(data);array and digest are aliases returning number[]. The default callable form and hex both return lowercase hexadecimal.
Hash a message in chunkshash-incrementally
const hash = sha256.create();
hash.update('Message');
hash.update(' to hash');
const digest = hash.hex();An output call finalizes the instance. Version 1.0 throws if update is called after hex, array, digest or arrayBuffer.
Hash a browser File without one giant bufferhash-browser-file
async function hashFile(file: File) {
const hash = sha256.create();
const reader = file.stream().getReader();
for (;;) {
const { value, done } = await reader.read();
if (done) break;
hash.update(value);
}
return hash.hex();
}The library supplies incremental updates, not file I/O. File.stream is a browser API and availability depends on the browser versions you support.
Hash a Node.js readable streamhash-node-stream
import { createReadStream } from 'node:fs';
import { sha256 } from 'js-sha256';
const hash = sha256.create();
for await (const chunk of createReadStream('archive.tar')) {
hash.update(chunk);
}
console.log(hash.hex());On Node, version 1.0 routes hashing through native node:crypto. The wrapper is not itself a Transform stream, so feed chunks in a loop.
Authenticate a message with HMAC-SHA-256create-hmac
const tag = sha256.hmac('secret-key', 'message');
console.log(tag);A string key is UTF-8 encoded. Do not embed a real server secret in browser JavaScript, where every user can read it.
Build an HMAC from chunksstream-hmac
const mac = sha256.hmac.create(secretKeyBytes);
mac.update(headerBytes);
mac.update(bodyBytes);
const tagBytes = mac.array();The chunk boundaries do not affect the result, but byte order does. Keep protocol framing explicit so different field combinations cannot become ambiguous.
Compare HMAC tags safely in Node.jsverify-hmac-node
import { timingSafeEqual } from 'node:crypto';
const expected = Buffer.from(sha256.hmac.array(secret, body));
const received = Buffer.from(receivedHex, 'hex');
const valid = received.length === expected.length &&
timingSafeEqual(received, expected);The package computes HMAC but does not provide verification. Compare equal-length byte strings with a constant-time primitive rather than === on hex strings.
Create a SHA-256 Subresource Integrity valuemake-sri-value
const bytes = new Uint8Array(await response.arrayBuffer());
const digest = new Uint8Array(sha256.arrayBuffer(bytes));
const base64 = btoa(String.fromCharCode(...digest));
const integrity = `sha256-${base64}`;SRI uses base64, not the package's default hex output. Spreading 32 digest bytes is safe; do not use this spread technique for the full downloaded resource.
Hash deterministic JSON byteshash-canonical-json
const canonical = JSON.stringify({
id: record.id,
name: record.name,
tags: [...record.tags].sort(),
});
const digest = sha256(canonical);Hashing ordinary JSON does not create canonical JSON automatically. Fix field order, number formatting and array ordering according to your protocol before hashing.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @noble/hashes | npm | Choose it for audited-style, dependency-free implementations covering SHA-2, SHA-3, BLAKE and password-oriented primitives |
| hash-wasm | npm | Choose it for large browser inputs or a wider algorithm list where WebAssembly performance is worth the extra machinery |
| crypto-js | npm | Choose it only for compatibility with an existing CryptoJS WordArray codebase that already depends on its broader API |