Library / SDK
emn178/js-sha256 avatar
emn178/js-sha256

js-sha256: one SHA-256 and SHA-224 API for the browser, Node and bundlers

A simple SHA-256 / SHA-224 hash function for JavaScript supports UTF-8 encoding.

975 stars263 forksJavaScriptMIT

At a glance

What is it?
js-sha256 is an MIT licensed JavaScript library that hashes strings, byte arrays and binary data with SHA-256 and SHA-224, exposes the same function names in the browser and in Node, and ships HMAC, incremental hashing and TypeScript declarations. It is aimed at front-end and Node projects that need a digest without pulling in a native module.
Who is it for?
js-sha256 fits a project that hashes in the browser or in a bundler pipeline and wants one API across environments, or a Node project that wants the same call signature the front end uses, and the incremental update API plus the .hex, .array and .arrayBuffer outputs cover the usual shapes.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 22 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A digest function with the same name everywhere

js-sha256 is described as a simple SHA-256 and SHA-224 hash function for JavaScript that supports UTF-8 encoding. The scope is small on purpose. It is not a cryptography toolkit, it is a digest function with a consistent surface.

The consistency is the actual product. The library exposes sha256 and sha224, and the README shows the same import in a browser script tag, in an ES module, in CommonJS, in TypeScript and through RequireJS. A developer can move a hashing call between a front end and a service without rewriting it, and without a bundler-specific import path.

Who it is for is anyone who has hit the usual problem: a browser application needs to compute a digest, and the obvious options are a native module that will not bundle, a Web Crypto call that is asynchronous and therefore awkward inside synchronous code, or a hand-rolled implementation in a file. This library is the third option, packaged.

The repository layout is consistent with a small, well-tested package. The tree has src/, build/, tests/, an index.d.ts for TypeScript declarations, a bower.json for the older Bower ecosystem, a rollup.config.mjs for bundling, a playwright.config.mjs for browser tests, and a CHANGELOG.md. The source directory is separate from the build output, and the package.json exports map routes consumers to different builds depending on whether they are in Node, using import or require.

One oddity is worth flagging straight away. The keywords array in package.json lists encryption alongside hash and cryptography. A digest is not encryption, and reading the keywords as a feature list would be a mistake. The library computes hashes, and the README is accurate where the package metadata is loose.

Why the Node build and the browser build are not the same code

The mechanism behind the API is the part of the project that has the most design content, and it is visible in the exports map of package.json.

The main field points at build/sha256.node.cjs, the module field at src/index.mjs, and the exports entry has separate node and browser branches. Under node, import resolves to src/node.mjs and require resolves to build/sha256.node.cjs. Outside Node, import resolves to src/index.mjs and require to build/sha256.cjs, with src/index.mjs as the default. The exports map then lists individual paths for the UMD build, the minified browser build, the minified ES module build and the Node CommonJS build.

The reason for that split is stated in the README: Node.js automatically uses its native crypto implementation with the same API. In other words, on Node the hashing is done by the platform, and js-sha256 is a compatibility layer over it. In the browser, where the same native primitive is not available as a synchronous function, the library's own implementation does the work.

That design has a consequence worth thinking about before you rely on it. The API is the same, but the two paths are different implementations of the same algorithm, one from the platform and one from the package. The README's own example values, including the SHA-256 digest of the empty string, are the kind of check that settles whether the two agree for your inputs.

The packaging also explains why there are so many build artefacts. Rollup produces the various formats from the src/ directory, and the package exposes the minified UMD file for a plain script tag, the .mjs build for modern bundlers, and the .cjs builds for require. The devDependencies list Rollup with the terser plugin, Mocha with the c8 coverage tool, and Playwright, and the test scripts run unit tests, distribution tests, and browser tests separately, which is a sensible split for a package whose failure mode is a bundler resolving the wrong file.

Four output shapes and an incremental API

The API surface is small and every method on it earns its place, which is unusual for a hashing library and is the reason to prefer it over a general crypto wrapper.

The base call returns a hex digest:

js
sha256('Message to hash');
sha224('Message to hash');

The incremental form is a hasher object you feed and then finalise:

js
var hash = sha256.create();
hash.update('Message to hash');
hash.hex();

There is also a chaining form where the update call returns the hasher itself, so you can feed it in pieces and read the result at the end with array():

js
var hash2 = sha256.update('Message to hash');
hash2.update('Message2 to hash');
hash2.array();

The output is not fixed to hex. The README shows the same empty input through four accessors, sha256('') for the default hex string, sha256.hex(''), sha256.array('') for the array of byte values, and sha256.digest('') for the same array, plus sha256.arrayBuffer('') for an ArrayBuffer. The array form matters when you are comparing raw digests rather than hex strings, and the ArrayBuffer form is what you want when the digest is being written into a binary structure or handed to a Web Crypto call.

HMAC is on the same object, with the same three styles. The one-shot form takes the key first:

js
sha256.hmac('key', 'Message to hash');
sha224.hmac('key', 'Message to hash');

and the incremental form is sha256.hmac.create('key') with the same update and hex or array finalisation, plus a chaining variant where sha256.hmac.update('key', 'Message to hash') returns a hasher you keep feeding.

The input types are equally broad. The README states the library supports UTF-8 encoding, with the digests for a non-ASCII string given as examples, and it also supports byte Array, Uint8Array and ArrayBuffer input:

js
sha256('中文'); // 72726d8818f693066ceb69afa364218b692e62ea92b385782363780f47529c21
sha224('中文'); // dfbab71afdf54388af4d55f8bd3de8c9b15e0eb916bf9125f4a959d4

That last point is the one to get right in a real integration. A string is encoded as UTF-8 before hashing, and a Uint8Array is hashed as bytes. If your application treats the same logical value as a string in one place and as bytes in another, the two digests differ, and the difference is invisible until someone compares a hash against a database.

Installing for npm, Bower or a plain script tag

Installation has three documented paths, one for each way of consuming JavaScript.

For Node.js, the README gives one command:

bash
npm install js-sha256

For Bower, the older package manager, the command is:

bash
bower install js-sha256

For a browser with no build step, the README's Download section points at a Compress link for the minified build and an Uncompress link for the readable one, both under the build/ directory. The browser usage section shows how to load the minified UMD build so that sha256 and sha224 are exposed as globals:

html
<script src="sha256.min.js"></script>
<script>
  console.log(sha256('Message to hash'));
</script>

For module-based projects the import is the same in both module systems:

js
import { sha256, sha224 } from 'js-sha256';
js
const { sha256, sha224 } = require('js-sha256');

and the README's TypeScript section notes that TypeScript users can import the same way, which is backed by the index.d.ts declaration file in the repository root and the types entry in the exports map. RequireJS is covered too, since the UMD browser build supports AMD:

js
require(['your/path/sha256.min.js'], function(sha256) {
// ...
});

Two things to check after installing. First, the exports map has many entries, and a bundler will resolve one of them, so if your build produces an unexpected artefact, look at which entry was chosen rather than suspecting the library. Second, the repository's own build is driven by Rollup, with the prepack script running the build before packing, and the build script is rollup -c. If you vendor the source rather than the published package, you need that build step.

What this library does not do

The gaps are as important as the features, and several of them are gaps in the documentation rather than only in the code.

There is no key stretching and no password hashing. A SHA-256 digest of a password is fast to compute, which is exactly what you do not want when an attacker gets the hash table. The README does not document any work factor, any memory hardness parameter or any password-oriented helper, so it should not be used for that purpose, and nothing in the API suggests otherwise.

There is no SHA-1, no SHA-384 and no SHA-512. The package name, the exports and the README are all specific to SHA-256 and SHA-224, and SHA-224 is the truncated variant of SHA-256 with a different initial value rather than a different algorithm.

The HMAC is limited to the two digests in the library. There is no HMAC-SHA512 or HMAC-SHA1 here.

On timing, the README says nothing about constant-time comparison. A digest comparison written by a caller, for example checking a hash given by a user against a stored one, is the caller's code and the README does not guide it. If your verification path compares digests, the safe comparison is something you write.

On the streaming side, the incremental API takes update calls and a finalise, but the README does not describe feeding a file stream in chunks. The demo pages listed at the top of the README include a SHA256 File Online page and a SHA224 File Online page, which shows file checksums are a use case the author had in mind, but the README's API section documents string and byte input rather than a stream interface. For large files, check how you would feed the data before assuming it is covered.

Finally, the benchmark section of the README is a single link to a jsPerf result. That tells you where to look for comparative numbers, not what the numbers are, and a jsPerf page records one run on someone else's machine rather than a measurement you can rely on for your own workload.

Version 1.0.0, a maintained master branch and a permissive licence

On maintenance, the repository is not archived and the last push was on 2026-10-01, on the master branch. The two most recent tags are v1.0.0 from 2026-07-27 and v0.12.0 from 2026-07-18, and the version in package.json is 1.0.0, matching the tag. The 1.0.0 release came after a long series of point releases, and the two dates are close together, so the project moved to a stable major version in July 2026 and has been committing to master since.

The version scheme in the package.json is worth a note. The version field is static at 1.0.0 while the build output, the src directory and the exports map carry several parallel artefacts, which means a consumer pinning a version is pinning a set of files, and the build script regenerates those files with Rollup on prepack. Anyone vendoring rather than installing from npm inherits that responsibility.

The licence is MIT, which the README states directly and the LICENSE.txt file in the repository root holds. MIT permits use in closed-source products and paid services, with the condition that the copyright and licence notice is kept, which is why a library like this tends to appear inside commercial front ends without a licensing conversation. This is not a licensing review and none of the terms are interpreted here.

The test setup tells you something about the project's habits that the version numbers do not. The scripts separate unit tests, distribution tests, browser tests and a coverage run, with the coverage command scoped to three source files, src/shared.mjs, src/core.mjs and src/node.mjs. Those three names reveal the internal split: shared code, the browser core, and the Node path. The Node path is the one that delegates to the platform, and it is the one where a behavioural difference from the browser build would show up.

Contact information is in the README, with the author listed as Chen, Yi-Cyuan and the project website pointing at the GitHub repository. The CHANGELOG.md at the repository root is where version-level changes are recorded, and it is the file to read when upgrading across a minor version.

Editorial conclusion

js-sha256 fits a project that hashes in the browser or in a bundler pipeline and wants one API across environments, or a Node project that wants the same call signature the front end uses, and the incremental update API plus the .hex, .array and .arrayBuffer outputs cover the usual shapes. It is the wrong choice for password storage, since the README documents no work factor and no key stretching, and it is the wrong choice if you need SHA-1, SHA-384 or SHA-512, which the library does not provide. Before you adopt it, read the exports map in package.json to see which build your bundler will resolve, and confirm that the Node path, which the README says uses the native crypto implementation, gives byte-identical output to the browser build for the inputs you care about.

Frequently asked questions

How do I install js-sha256 in a Node.js project?

Run `npm install js-sha256`, then import it with `import { sha256, sha224 } from 'js-sha256';` or require it with `const { sha256, sha224 } = require('js-sha256');`. The README also documents `bower install js-sha256` for Bower users and a plain script tag for the minified UMD browser build.

Does js-sha256 use Node's native crypto?

Yes. The README states that Node.js automatically uses its native crypto implementation with the same API, and the package.json exports map routes Node consumers to src/node.mjs or build/sha256.node.cjs, while browsers get the package's own implementation.

What output formats does js-sha256 return?

The README shows the default call returning a hex string, plus sha256.hex, sha256.array, sha256.digest for the array of bytes, and sha256.arrayBuffer for an ArrayBuffer. The same accessors exist on sha224, and each function also has an incremental form using create() and update().

Does js-sha256 support HMAC?

Yes. The API includes sha256.hmac and sha224.hmac for one-shot calls, plus sha256.hmac.create for the incremental form, and the same hex and array finalisers.

Which hash algorithms does js-sha256 implement?

Only SHA-256 and SHA-224. The README documents no SHA-1, SHA-384 or SHA-512 support, and the package keywords list hash and cryptography rather than naming other algorithms.

Official sources

  1. emn178/js-sha256 on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/emn178-js-sha256.svg)](https://hysenlabs.com/projects/emn178-js-sha256)

Was this useful?