jschardet 4: a TypeScript rewrite of the character encoding detector
Character encoding auto-detection in JavaScript (port of python's chardet)
At a glance
- What is it?
- jschardet 4 is a ground-up TypeScript port of Python's chardet 7, with two functions, detect and detectAll, and a table of measured differences against the old jschardet 3. The accuracy jumps from 43.1 percent to 99.4 percent, and the bundle grows from 334 KiB to 1141 KiB because of the detection model.
- Who is it for?
- Adopt jschardet 4 if you are reading bytes of unknown encoding in Node or a browser and the current detector is getting it wrong, since the reported accuracy on 3138 test files goes from 43.1 percent to 99.4 percent. Keep away if bundle size or first-call latency dominates your constraints, because the detection model makes the package three times larger and adds roughly 110 ms to the first detect call.
- Can I use it commercially?
- Yes. 0BSD 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 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A rewrite, not a patch, of a Python library
jschardet is a character encoding detector for JavaScript that runs in Node.js and browsers with zero runtime dependencies. Version 4 is described as a ground-up TypeScript port of chardet 7, and the claim is that it is much faster and more accurate than jschardet 3 while keeping its API.
The scale of the change is unusual. The comparison table in the README puts jschardet 4.0.0 next to jschardet 3.1.4 and next to chardet 7.6.0 in Python, and the accuracy line reads 99.4 percent on 3138 test files for version 4, against 43.1 percent for version 3 and 99.5 percent for the Python original. So version 3 was not merely slower than the Python version, it was in a different league on accuracy, and version 4 closes that gap while going faster than the Python original at 780 files per second against 222.
The API is two functions. detect returns the best match, detectAll returns every candidate above a confidence threshold sorted by confidence, and both return an object with encoding, confidence, language and mimeType fields. That the API is unchanged is what makes the upgrade a dependency bump rather than a rewrite, which is why the README has a section on what behaves differently.
The licence is 0BSD, which is the most permissive form of the BSD licence and a change from the LGPL of version 3. The primary language is TypeScript, the default branch is main, the last push was on 2026-10-16, and the release history shows 4.0.0-rc.3 on 2026-09-04, 4.0.0-rc.4 on 2026-09-25 and 4.0.0 on 2026-09-26.
The cost of a bigger model, stated plainly
The comparison table is the most useful thing in the README because it does not only list improvements.
Version 4 is faster and more accurate, and it is also bigger. The bundle size line reads 1141 KiB minified and 691 KiB gzipped for version 4, against 334 KiB and 119 KiB for version 3. The cold start line, which is import plus first detect, reads 109.2 ms for version 4 against 29.9 ms for version 3 and 101.7 ms for the Python original.
The README explains the cause in one sentence. Compared to jschardet 3, v4 has a larger bundle and a roughly 110 ms first-call cost, and both come from shipping a larger detection model. The model decompresses once on the first detect call and stays in memory afterwards, so subsequent calls run at full speed.
That is a fair trade for a server that processes many files, and a poor one for a browser bundle that only reads one small file at page load. The peak memory line shows 85.5 MiB for version 4 against 831.8 MiB for version 3, so version 4 is also better on that axis, though it is still above the 37.6 MiB of the Python original.
The MIME type detection is a genuine new feature rather than a speed story. Version 3 did not have it, and the Python original does. That is the feature to weigh if you are processing binary files and currently guess the type from the extension.
The full benchmark methodology and per-encoding accuracy are in docs/performance.md, and the README points there rather than asking you to take the table on trust.
Two functions, four fields, and a threshold you can move
The API surface is small enough to describe completely. detect takes a string, a Uint8Array, an ArrayBuffer, or any ArrayBufferView, with Node Buffer and DataView working too, and returns the best match. detectAll takes the same input types and returns all the candidates above the confidence threshold, which defaults to 0.20, sorted by confidence.
The returned object has four fields. encoding is the detected encoding name or null if unknown, confidence is a score from 0.0 to 1.0, language is a language hint when available, and mimeType is a MIME type hint when available.
The options object is three fields. minimumThreshold overrides the default 0.20 for detectAll filtering, detectEncodings is an allowlist of encoding names to consider, and excludeEncodings is a blocklist of encoding names.
The allowlist is the option that matters in practice. A detector that is uncertain between several single-byte encodings is much more useful when you know the document is one of a small set, and the threshold alone cannot express that. There is also enableDebug, which logs the full candidate lists from detect and detectAll, described as useful when tuning thresholds or allowlists.
The quick start examples show the shape of the results. ASCII text comes back as ascii with confidence 1 and language en. A UTF-8 string with accented characters comes back as utf-8 with confidence 0.84. An EUC-JP byte array comes back as EUC-JP with confidence 0.56 and language ja. And a windows-1252 example through detectAll shows the classic ambiguity, with Windows-1252, iso8859-15 and ISO-8859-1 all at 0.32 and MacRoman at 0.31, which is the case where an allowlist saves you.
Installing it, and the three ways to load it in a browser
The npm install is one line:
npm install jschardetBoth module systems are supported, with an ESM import and a CommonJS require of the same detect function.
For browsers there are three routes, and they are worth distinguishing because the package manifest points at different files for each. The classic script tag route copies jschardet.min.js next to your HTML and includes it, which attaches a global jschardet. The ESM route copies jschardet.esm.min.js and imports from it. The third route is a CDN, and the README gives a table of jsDelivr URLs for the IIFE format and the ESM format.
The package manifest is where the fine detail shows. The jsdelivr and unpkg fields both point at dist/jschardet.min.js, the browser field points at the ESM bundle, the main field points at a CommonJS build, and the exports map gives a browser condition first, then import, require and default. There is also a bin entry, which is the CLI, and the published files list includes the dist bundles, the build directory, the type declarations for both module systems, the readme and the licence.
So the same package resolves to an ESM bundle in a browser bundler, a CommonJS file for a Node require, and a plain script for a script tag, without you having to know which one is which.
The CLI, and the encodings it can tell apart
There is a command line tool in the package, and its options are aimed at the same uncertainty the API has.
Running it on a file prints the file name, the encoding and the confidence. The minimal form prints only the encoding. The -l flag adds the detected language, giving output like a file name, then utf-8, then en, then English, then with confidence 1. The -i flag takes a comma separated list of encodings to consider, so you can restrict the detector to utf-8 and windows-1252 for example. And the tool reads from standard input, so you can pipe a file into it and get a line back.
The encodings supported are the same as chardet's, with aliases and encoding-era filters documented on the chardet site. The list is divided into modern web encodings and legacy ISO ones, and the modern list is long: ascii, big5hkscs, cp874, cp932, cp949, euc-jis-2004, euc-kr, gb18030, hz-gb-2312, iso-2022-kr, iso2022-jp-2, iso2022-jp-2004, iso2022-jp-ext, koi8-r, koi8-u, shift_jis_2004, tis-620, the UTF family from utf-7 through utf-32-le including utf-8-sig, and the windows-125x series from 1250 to 1258. The legacy ISO section lists iso-8859-1.
The repository shows the testing effort behind those numbers. There are separate vitest configurations for the main suite, the browser suite with playwright installing chromium, the bundle tests for both jschardet and chardet, and an accuracy configuration, plus a docs directory, a chardet directory that is the upstream Python project pulled in as a submodule, and a scripts directory with a sync-version step and an update-chardet step that keeps the port in sync with upstream.
Where jschardet is the wrong choice
The first wrong case is a client side bundle where size matters. The jump from 119 KiB gzipped to 691 KiB gzipped is a large change for a library you might include once on a page, and the first detect call costs around 110 ms on top of that. If you are reading one small file from a known-ish source, the old version or a simpler heuristic may serve you better.
The second is a server that reads a lot of text in the dominant encoding. If your inputs are UTF-8, a check for a valid UTF-8 byte sequence is both faster and more accurate than any statistical detector, and it is a few lines of code. Statistical detection earns its keep on messy, mixed, legacy input, and it costs you a model to load.
The third is a case where the answer is genuinely ambiguous and the metadata is not available. The windows-1252 example in the README is the honest illustration: four candidate encodings within 0.01 of each other. In a situation like that, a detector is telling you it does not know, and the right move is to look for a charset declaration, a filename convention or a byte order mark, not to trust the top candidate. The allowlist option helps, but it only helps if you already know the answer.
The fourth is a project that needs to stay in step with upstream by hand. The port is maintained by porting chardet, and the repository has an update-chardet script to pull the Python changes across, which is a workflow that has to be run and verified by a person. A new chardet release does not arrive in the JavaScript package by itself.
The alternative worth comparing is chardet itself, running in Python, if your pipeline is already Python. The accuracy figures are the same, and the speed is lower, but you skip the port entirely and the divergence risk with it.
Editorial conclusion
Adopt jschardet 4 if you are reading bytes of unknown encoding in Node or a browser and the current detector is getting it wrong, since the reported accuracy on 3138 test files goes from 43.1 percent to 99.4 percent. Keep away if bundle size or first-call latency dominates your constraints, because the detection model makes the package three times larger and adds roughly 110 ms to the first detect call. Before you depend on the results, read docs/performance.md for the benchmark methodology, and check the version you install, since 4.0.0 shipped on 2026-09-26 and the last push was on 2026-10-16.
Frequently asked questions
What is the difference between jschardet 4 and jschardet 3?
Version 4 is a ground-up TypeScript port of Python's chardet 7 rather than an evolution of version 3. The README reports 99.4 percent accuracy on 3138 test files against 43.1 percent for version 3, about six times the throughput, lower peak memory, language detection and MIME type detection, at the cost of a larger bundle and roughly 110 ms on the first detect call.
How do I use jschardet to detect an encoding?
Import detect or detectAll and pass a string, a Uint8Array, an ArrayBuffer or any ArrayBufferView. Both return an object with encoding, confidence, language and mimeType. detect gives the best match, detectAll gives every candidate above a threshold that defaults to 0.20, sorted by confidence.
Can I restrict which encodings jschardet considers?
Yes. The options object takes detectEncodings as an allowlist of encoding names and excludeEncodings as a blocklist, and there is a minimumThreshold to override the default 0.20 used by detectAll. There is also enableDebug, which logs the full candidate list so you can tune thresholds and allowlists.
How do I use jschardet from the command line?
The package installs a jschardet binary. Run jschardet somefile.txt to get the encoding with confidence, --minimal to print only the encoding, -l to include the detected language, and -i with a comma separated list to restrict the encodings considered. It also reads from standard input.
Official sources
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.
[](https://hysenlabs.com/projects/aadsm-jschardet)