# hasha: a small wrapper over Node's crypto hashing

> hasha is an MIT licensed module that wraps the core `crypto` Hash class with a friendlier API, better defaults and file and stream helpers. It also moves the work off the main thread, which is its one real deviation from the platform, and that deviation has a cost worth understanding.

**sindresorhus/hasha** — Hashing made simple. Get the hash of a buffer/string/stream/file.

- Repository: https://github.com/sindresorhus/hasha
- Stars: 952 · Forks: 34
- Language: JavaScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/sindresorhus-hasha

## A convenience wrapper over createHash

hasha describes itself as a convenience wrapper around the core `crypto` Hash class with a simpler API and better defaults, and the repository description is blunter still: hashing made simple, get the hash of a buffer, string, stream or file.

The wrapper is thin by design. The API surface is four functions and one stream factory. `hash(input, options?)` returns a hash asynchronously, `hashSync(input, options?)` returns one synchronously, `hashFile(filePath, options?)` returns a Promise for the calculated file hash, `hashFileSync(filePath, options?)` returns it directly, and `hashingStream(options?)` returns a hash transform stream.

What the wrapper adds over the platform is mostly ergonomics. The options object has the same names you would expect, `encoding` and `algorithm`, but with defaults chosen sensibly and with the value sets documented in one place. The readme points to the Node.js crypto documentation for the underlying behaviour and the full specification of hashing, which is a sensible division of labour.

The dependency list confirms how little is added. `is-stream` is there to detect streams and `type-fest` for the TypeScript types, and that is the whole runtime dependency set. The package is ESM only, with `type` set to `module` and a single export mapping to `index.js`, declares `sideEffects: false`, and requires Node 20 or newer.

The repository layout matches: `index.js` and `index.d.ts` at the root, a separate `thread.js` for the worker, a `test.js`, and a `media/` directory with the logo. The published `files` list contains only those three source files, so the install is a few kilobytes of code.

## A worker thread is spawned lazily and then unrefed

The detail that separates hasha from a plain wrapper is in the API documentation for both `hash` and `hashFile`. The operation is executed using `worker_threads`. A thread is lazily spawned on the first operation and lives until the end of the program execution, and it is unrefed, so it will not keep the process alive.

Three consequences follow, and they cut in different directions.

The first is that the work leaves the main thread. For a large file, the hashing happens on a worker thread, so the event loop of your process stays responsive while the digest is computed. For a build script that hashes a directory of assets while also serving requests, that is the entire point of the library.

The second is a persistent resource. The thread is not created per call. It is spawned on the first operation and then lives until the process exits, so a long running server that hashes occasionally holds that thread for its whole lifetime. The unref means it will not by itself prevent the process from exiting, which is the right default, but it also means the thread is not torn down after use.

The third is that hashing is now genuinely asynchronous in a way the platform API is not. The platform `createHash` is synchronous in its update and digest calls, and people usually reach for `pipeline` or a stream to avoid blocking. hasha packages that as a promise returning function.

The synchronous variants exist for the cases where you want a plain return value, and the readme notes that a `NodeJS.ReadableStream` input is not available in `hashSync`, so the sync path is limited to buffers and strings.

## Defaults of sha512 and hex, and the warning about md5

The `encoding` option defaults to `hex` and accepts `hex`, `base64`, `buffer` or `latin1`. The `algorithm` option defaults to `sha512` and accepts `md5`, `sha1`, `sha256` or `sha512`, with a note that the set is platform dependent, since it is whatever the underlying Node crypto build supports.

`sha512` as the default is a deliberate choice rather than an inheritance from the platform, where `createHash` requires an algorithm argument. It is a strong default for a general purpose hashing helper, and it is worth knowing that it is also slower than sha256 for large inputs, so a project that only needs a content fingerprint may want to pass `algorithm: 'sha256'` explicitly.

The readme attaches a security note to the algorithm option that should be quoted rather than paraphrased. The `md5` algorithm is good for file revving, but you should never use `md5` or `sha1` for anything sensitive, because they are insecure.

That distinction matters for how the library is used. File revving is the pattern where a file's URL or name embeds a hash of its contents so that a changed file gets a new identifier and a cached copy is invalidated. That is a content addressing use, not a security use, and md5 remains perfectly serviceable there. Using the same library for integrity or authentication without changing the algorithm is the mistake the note is aimed at.

## Buffers over strings, arrays over concatenation

The `input` type is `Uint8Array | string | Array<Uint8Array | string> | NodeJS.ReadableStream`, with the caveat that streams are not available in `hashSync`.

The readme gives two pieces of guidance about how to use it. While strings are supported, you should prefer buffers as they are faster to hash, although if you already have a string you should not convert it to a buffer.

That is a precise recommendation, and the reasoning is worth keeping: the conversion is a copy, and hashing the copy is slower than hashing the string directly, so the advice is not that strings are wrong but that a conversion you do not need is a cost you should not pay.

The second point is about arrays. Pass an array instead of concatenating strings and or buffers. The output is the same, but arrays do not incur the overhead of concatenation.

So the library accepts a heterogeneous list of buffers and strings and hashes the same bytes as the concatenated version, without the copy. For anything hashing more than one piece, that is both the faster and the clearer way to express the intent.

There is also an `AbortSignal` option, which is an `AbortSignal` to abort the hashing operation. That matters in a request handler where the client disconnects while a large file is being hashed, and where you would otherwise have a worker thread doing work nobody is waiting for.

## File and stream helpers, and how to hash many files

`hashFile` and `hashFileSync` take a file path and the same options, and the readme's example shows the file revving case with an explicit algorithm.

```js
import {hashFile} from 'hasha';

// Get the MD5 hash of an image
await hashFile('unicorn.png', {algorithm: 'md5'});
//=> '1abcb33beeb811dca15f0ac3e47b88d9'
```

`hashingStream` returns a hash transform stream, which is the shape you want when the data is arriving rather than sitting on disk.

```js
import {hashingStream} from 'hasha';

// Hash the process input and output the hash sum
process.stdin.pipe(hashingStream()).pipe(process.stdout);
```

The readme closes with a tip that is really about how to use the worker thread well. For hashing multiple files, limit concurrency to `os.availableParallelism()`.

```js
import {availableParallelism} from 'node:os';
import {hashFile} from 'hasha';
import pLimit from 'p-limit';

const limit = pLimit(availableParallelism());

await Promise.all(files.map(file => limit(() => hashFile(file))));
```

The reasoning is straightforward once you see that the work happens on a worker thread. There is one worker, so the parallelism available to you is bounded by what one thread can do plus the main thread, and the sensible bound is the number of logical processors the machine reports. Unbounded `Promise.all` over a thousand files queues a thousand operations behind a single worker, which buys nothing.

## Where hasha is the wrong tool

The clearest case is a short string. `crypto.createHash('sha256').update(value).digest('hex')` is one line, synchronous, and has no worker thread, no dependency and no package. Reaching for a wrapper to hash one small value adds a dependency and moves the work to another thread for no benefit, since the cost of hashing a short string is far smaller than the cost of setting it up.

The second case is a browser bundle. hasha is a Node package with `node:os` and `node:crypto` underneath and a worker thread implementation, and the readme itself points at a sibling project for the browser case, `crypto-hash`, described as a tiny hashing module that uses the native crypto API in Node.js and the browser. The readme also lists `hasha-cli` for command line use, `hash-object` for hashing an object and `md5-hex` for creating an MD5 hash with hex encoding, so the ecosystem already covers the variants.

The third is synchronous hashing in a hot path. The sync variants block the event loop by definition, and `hashSync` cannot take a stream, so for a large file it is the wrong shape.

The fourth is a runtime constraint. The package is ESM only and requires Node 20 or newer, so a CommonJS project on an older runtime cannot use it without a build step or a dynamic import workaround.

One more practical note. The `test` script in package.json runs `xo && node --test && tsc index.d.ts`, meaning linting, the Node test runner and a type check of the declaration file. For a package this small that is a good sign about how the author treats the public surface, and it is also a reminder that the typed API is part of the contract rather than an afterthought.

## Conclusion

hasha suits a Node project that hashes files in a build step or an upload pipeline and wants a typed, promise based API without assembling streams and encodings by hand, and it is the wrong tool for a project that only hashes short strings, since the platform API already covers that in one line. Before adopting it, confirm your runtime is Node 20 or newer, because the package is ESM only and declares `node` `>=20`, and check whether your workload is dominated by one very large file, since hashing many files is where the worker thread and the concurrency guidance start to matter.

## FAQ

### How do I install hasha?

Run `npm install hasha`. The package is ESM only, declares `node` `>=20` and ships `index.js`, `index.d.ts` and `thread.js`.

### What is the default hash algorithm and encoding in hasha?

The `algorithm` option defaults to `sha512` and accepts `md5`, `sha1`, `sha256` or `sha512` depending on platform support, while `encoding` defaults to `hex` and also accepts `base64`, `buffer` and `latin1`.

### Does hasha run hashing on a separate thread?

Yes. The asynchronous `hash` and `hashFile` operations are executed using `worker_threads`, with a thread lazily spawned on the first operation that lives until the program exits. It is unrefed, so it will not keep the process alive.

### How do I hash many files without overloading the machine?

The readme recommends limiting concurrency to `os.availableParallelism()`, for example by wrapping each `hashFile` call in `pLimit(availableParallelism())` and running them under `Promise.all`.

### Is md5 safe to use with hasha?

The readme says the md5 algorithm is good for file revving, but that you should never use md5 or sha1 for anything sensitive, because they are insecure. Use a stronger algorithm for anything that depends on the hash for security.

## Sources

- [Issues](https://github.com/sindresorhus/hasha/issues)
- [License: MIT](https://github.com/sindresorhus/hasha/blob/main/LICENSE)
- [README](https://github.com/sindresorhus/hasha/blob/main/README.md)
- [Releases](https://github.com/sindresorhus/hasha/releases)
- [sindresorhus/hasha on GitHub](https://github.com/sindresorhus/hasha)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sindresorhus-hasha
