Open-source project
zig-utils/zig-js avatar
zig-utils/zig-js

zig-js: a pre-stabilization JS engine with JavaScriptCore shaped exports

A JavaScript engine in pure Zig.

31 stars0 forksZigMIT

At a glance

What is it?
A JavaScript engine written in Zig whose static library presents JavaScriptCore-shaped C headers and symbols, with test262 and WebAssembly conformance tracked in the README down to exact sample counts. The benchmark tables are the honest part, including where JavaScriptCore still wins.
Who is it for?
Read zig-js if you want an engine whose conformance and performance claims come with the raw sample files attached, and treat the version as pre-stabilization while the published API is called pre-stabilization. Do not plan around the JSC throughput rows for JavaScript parsing, since the project's own diagnostics put the system engine well ahead there.
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 1 day ago.
What is it written in?
Mainly Zig, 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.

From daily snapshots

Star history

  • zig-utils/zig-js31 ★ · +2
Star history
Repository2026-08-212026-10-22
zig-utils/zig-js2931

2 Stars in the last 62 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

JSC shaped exports, with Objective-C glue on macOS

The engine and its importable `js` module are written in Zig, and the shape of the deliverable is chosen for drop-in familiarity: the static library exports JavaScriptCore-shaped C headers and symbols. On macOS that means bridge glue in `src/objc_bridge.m`. Notably, the package does not bundle JavaScriptCore or V8. It depends on `zig-regex` and `zig-gc` instead, and a system JavaScriptCore is used only by explicit differential and benchmark targets. The APIs are described as pre-stabilization, and the smallest complete program looks like this:

zig
const js = @import("js");

const ctx = try js.Context.create(allocator);
defer ctx.destroy();

const value = try ctx.evaluate("let x = 40; x + 2");

A context is created, evaluated and destroyed, with `defer` doing the cleanup. Because the library mimics JavaScriptCore at the boundary, existing embedding knowledge transfers, which is the argument the shape makes even though the README does not spell out a migration path.

Conformance rows carry the evidence file names

The status table is unusual in that each row links to the artifact behind it rather than asking to be trusted. The configured test262 suite reports 53,175 of 53,175 passing. The WebAssembly matrix reports 151,802 of 151,802 applicable tests across ten profiles, with Core 3 accounted for at 63,964 of 63,964. A third row covers a pinned private ABI against two embedders, Home at 447 of 447 and Bun at 437 of 437. Underneath every figure sits a path into the documentation data directory, a run log for test262, a conformance matrix, a shadow inventory taken against upstream main, and an ABI folder described as holding inventories, a provider audit, a lifecycle description and exact reproduction steps. Nothing here is a summary without a way back to the raw record.

Ten wins out of ten, with the harness arguing the details

The comparison table against JavaScriptCore is guarded by stated method: equal-work checks, alternating order, dispersion limits and a 50 ms timing floor are enforced by the harness, and the accepted report is backed by 1,540 raw samples. On one warmed lane the engine wins all ten comparisons at 2.32x throughput. At eight independent steady contexts it wins ten of ten at 2.53x, with scaling of 5.35x against 4.96x for JSC. Cold lifecycles come out at 2.52x with 5.67x against 4.97x. The shared realm row with no GIL reaches 4.84x scaling but has no public JSC equivalent to compare against, since JSC offers no public shared-realm embedding path. Ratios above 1.00x favor zig-js.

WebAssembly scales hard and starts slow

The WebAssembly numbers split cleanly in two, and both halves matter. Scaling is the strong side, with SIMD reaching 3.67 to 4.58 times across eight lanes. Threads report 17.23 million contended adds per second and 287,444 wait and notify handoffs per second at eight workers. Against JavaScriptCore the throughput figures are the opposite, landing at 0.10 to 0.14 times JSC for the SIMD runs. In other words the engine parallelizes well and compiles slowly, and a reader who only looks at the scaling column will draw the wrong conclusion about which workload suits it. Both WebAssembly rows are dated and link to a report plus a sample file, so the two halves of that verdict can be checked separately rather than taken as one average.

Collector rows are ratios, and one of them counts timeouts

The garbage collection section is phrased as distance from a baseline rather than as an absolute number, which is the honest way to present a collector. The moving age-three generation runs at 0.63 to 1.01 times its exact non-moving parents and 0.84 to 1.02 times moving age-one across accepted single-mutator rows, with 480.42 MiB copied in the recorded moving age-three rows. The shared no-GIL minor pause maximum is 110.56 ms, and that row carries twelve timeouts. Explicit compaction is the cleaner result: retained fragmented backing drops 90.8 percent, from 8.81 to 0.81 MiB, at a 0.99 ms median pause with unchanged post-action throughput.

The JSON rows say where JavaScriptCore still wins

One diagnostic is unusually candid. The debug pass on Apple M2 Pro with Zig 0.17.0-dev.1770, in default arena mode with the collector off, shows 51.7 percent fewer retired instructions for long strings with a sparse escape, against a control of minus 0.08 percent, and 27.5 percent fewer for arrays of short integers with a fractional control of plus 0.41 percent. A later production pass switches to ReleaseFast with the real collector and reports 6.74 and 6.72 percent fewer instructions on string-length rows, with a guarded bit-and removing another 0.96 and 0.53 percent while controls stay within plus or minus 0.11 percent. Then the same document reports the system engine as 4.05 and 4.04 times faster on the frozen base and variant, with an explicit statement that no parity claim is made.

The docs site builds from paths outside the repository

The engine repository also contains a documentation site, and its build scripts assume a machine layout that a fresh clone does not have. The package is named `zig-js-docs`, private, and its scripts all invoke a bunpress command through a relative path that climbs out of the repository to `../../Tools/bunpress`, with the dev dependency declared the same way through a `file:` reference. The data regeneration script goes further and calls a binary at an absolute path under a personal home directory. So the documentation tooling works for the author and its collaborators and will not resolve for anyone who clones the project, while the engine itself needs only `zig build`, which installs `libzig-js.a` and matching headers under `zig-out/`.

Editorial conclusion

Read zig-js if you want an engine whose conformance and performance claims come with the raw sample files attached, and treat the version as pre-stabilization while the published API is called pre-stabilization. Do not plan around the JSC throughput rows for JavaScript parsing, since the project's own diagnostics put the system engine well ahead there.

Frequently asked questions

What is Zig used for?

In this project Zig is the implementation language and the build tool. The engine and its importable js module are written in Zig, and zig build installs libzig-js.a with compatible headers under zig-out/.

What companies use Zig?

The repository does not name any company. It attributes its API shape to JavaScriptCore as the thing being emulated, and it reports ABI compatibility results for two embedders, Home at 447 of 447 and Bun at 437 of 437.

Why use Zig instead of C?

That argument is not made here. What the project states instead is that the static library exports JavaScriptCore-shaped C headers and symbols, so C-shaped consumers keep the interface they expect while the implementation is Zig, with Objective-C bridge glue in src/objc_bridge.m.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
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/zig-utils-zig-js.svg)](https://hysenlabs.com/projects/zig-utils-zig-js)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/zig-utils-zig-js.svg)](https://hysenlabs.com/projects/zig-utils-zig-js)