# Jint: a managed ECMAScript interpreter for .NET hosts

> Jint runs JavaScript inside a .NET process with no native dependencies or bytecode generation. It suits hosts that need to script user logic, and it is a poor fit for untrusted multi-tenant code that needs a real isolation boundary.

**sebastienros/jint** — Javascript Interpreter for .NET

- Repository: https://github.com/sebastienros/jint
- Stars: 4,724 · Forks: 607
- Language: C#
- License: BSD-2-Clause
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/sebastienros-jint

## The problem Jint solves for .NET applications

A .NET service that lets customers supply rules, templates or small automations has to execute code it did not write. Shelling out to Node adds a second runtime to deploy, a process boundary to supervise, and a serialization layer between the two languages. Jint takes the other route: it is a managed ECMAScript interpreter that evaluates JavaScript inside the host process, with no native dependencies and no bytecode generation. The README states that it targets .NET Framework 4.7.2, .NET Standard 2.0 and 2.1, .NET 8, and .NET 10, so the same library fits a legacy ASP.NET application and a current console tool.

The audience is narrower than "anyone who wants JavaScript in .NET". The projects listed as users are infrastructure and platform software: RavenDB, EventStoreDB, Orchard Core, Elsa Workflows, Docfx, and JavaScript Engine Switcher. That pattern is informative. These are systems where the host defines a small surface of functions and values, and the script supplies the decision logic. Jint's value is the projection layer that makes .NET delegates, objects, collections and types look like JavaScript values without a serialization step.

## How the Engine, values and prepared scripts fit together

The core type is `Engine`. You construct one, optionally configure it, and then call `Evaluate` with a string or a prepared script. The result is a `JsValue`, and you pull a CLR value out of it with a conversion method such as `AsNumber()` or `AsString()`. In the README's first example, `engine.Evaluate("40 + 2").AsNumber()` returns 42. That is the whole data flow for simple cases: source in, projected value out.

The second mechanism is host binding. `SetValue` registers a .NET object under a JavaScript name. In the README's example, an `Action<string>` wrapping `Console.WriteLine` is registered as `log`, then a script defines a `greet` function that calls it. `Invoke` calls that function from C# and returns the result as a `JsValue`. This is how a host exposes a narrow API rather than the whole CLR.

The third mechanism matters for throughput. `Engine.PrepareScript` parses source once and returns a `Prepared<Script>` that can be evaluated repeatedly against different engines. The README states that `Prepared<Script>` and `Prepared<Module>` are thread-safe and may be shared across engines, while JavaScript objects are not: a `JsValue` containing an object belongs to the engine and realm that created it. That split is the key architectural constraint for anyone building a pool. Prepared artifacts are shareable; live values are not.

## Installing Jint and running a first script

The core package comes from NuGet. The README gives one command, and it is the only install step it documents:

```bash
dotnet add package Jint
```

After that, the README's examples assume `using Jint;` at the top of the file. A minimal program that evaluates an expression and reads the number back looks like this:

```csharp
using Jint;

var engine = new Engine();
var result = engine.Evaluate("40 + 2").AsNumber();
Console.WriteLine(result);
```

Running it prints `42`. The interesting part is not the arithmetic but the fact that no Node process, no native library and no generated assembly were involved.

The next step is to expose a host function and call script code back. This is the README's expose-and-invoke example, which registers `Console.WriteLine` under the name `log`, defines a `greet` function, and invokes it from C#:

```csharp
var engine = new Engine()
    .SetValue("log", new Action<string>(Console.WriteLine))
    .Execute("""
        function greet(name) {
            const message = `Hello, ${name}!`;
            log(message);
            return message;
        }
        """);

var greeting = engine.Invoke("greet", "Ada").AsString();
```

You should see `Hello, Ada!` written to the console, and `greeting` should hold the same string. Note the shape of the API: `SetValue` and `Execute` return the engine, so calls chain.

For scripts that run on every request, prepare once and reuse. The README's prepared-script example parses a reducer expression with a source name and strict mode enabled, then evaluates it against a fresh engine that has `items` bound:

```csharp
var script = Engine.PrepareScript(
    "items.reduce((sum, value) => sum + value, 0)",
    source: "sum.js",
    strict: true);

var engine = new Engine();
engine.SetValue("items", new[] { 1, 2, 3 });

var total = engine.Evaluate(in script).AsNumber();
```

The `source` argument is what shows up in stack traces, and `strict: true` opts the script into strict mode. The README recommends enabling strict mode when the scripts you run are compatible with it.

## Optional packages and what stays out of the core

The README is explicit that browser and Node APIs are not installed by default and that hosts grant capabilities per script. The core `Jint` package covers the language and the host projection. Everything else is a separate NuGet package, and the README's table lists four: `Jint.DevTools` for a Chrome DevTools Protocol server used for debugging and profiling, `Jint.Browser` for headless HTML, DOM, navigation, networking, storage and content extraction, `Jint.Browser.Tool` for the `jint-browser` command-line tool, and `Jint.Browser.Mcp` for a Model Context Protocol server for browser automation.

That packaging choice has a practical consequence. A service that only needs to evaluate arithmetic and call a few host functions pulls in one dependency. A team that wants DOM emulation has to add `Jint.Browser` and accept its dependency graph. The README points readers to a "Additional packages" documentation page for dependencies, target frameworks and common combinations, which suggests the combinations are not obvious from the table alone. The repository also contains `Jint.Repl` and `Jint.AotExample` directories, but the README does not describe them, so their intended use is not documented in the repository's main README.

## Where Jint is the wrong tool

The README's own recommended usage section contains the sharpest limitation: "Use a fresh engine across trust domains. Global snapshots help trusted reuse, but they are not an isolation boundary." If your threat model is hostile code from mutually distrusting tenants, an in-process interpreter is not the boundary you need. A fresh engine per trust domain raises the cost of an escape but does not contain one.

The second limitation is performance shape. The README says to measure with your own scripts and host objects because interpreter performance depends heavily on call frequency and script-to-host traffic. That is an unusually direct warning. The benchmark summary in the README describes Jint as the fastest managed engine on 10 of 12 scripts and the fastest interpreter on all 12, but it also frames the comparison as the fastest interpreter. A JIT-compiled engine can beat an interpreter on long-running, compute-heavy JavaScript, and the README does not claim otherwise. Workloads that spend their time inside a tight numeric loop are the wrong fit; workloads that spend their time deciding which host function to call are the right one.

The third constraint is engine lifetime. The README advises giving each engine to only one operation at a time and awaiting async operations before reusing or disposing it. There is no documented thread-safe engine. If you were hoping to share one engine across concurrent requests, the documentation does not support that, and `Prepared<Script>` sharing is the mechanism it offers instead.

## Jint compared with ClearScript and other embedding options

The related searches include a direct comparison with ClearScript, and the architectural difference is worth stating plainly. ClearScript embeds an existing JavaScript engine, which means the JavaScript semantics and performance characteristics come from that engine rather than from managed code. Jint is the interpreter. It is written in C# and runs as managed code with no native dependencies and no bytecode generation, which is why the README can promise the same package across .NET Framework 4.7.2 and .NET 10.

That difference cuts both ways. A managed interpreter is easier to deploy in environments where native binaries are unwelcome, and it is easier to debug from C# because there is no interop boundary in the middle. It also means Jint's conformance is Jint's responsibility. The README reports 99.9% on ECMAScript and ECMA-402 across 102,692 generated test262 cases with 107 skipped, and describes those percentages as covering the pinned test corpora in this repository rather than the entire web platform. That caveat is the honest reading: the language surface is close to complete, while the WinterTC Minimum Common API result is 79.5% with the missing 16 members being WebAssembly, which the README says Jint declines by design. If your scripts need WebAssembly, this is not the engine for you, and no amount of host configuration changes that.

## Maintenance lanes, licence and upgrade cost

The repository is not archived, and the last push was on 2026-09-22. Releases are frequent: v4.16.1 on 2026-08-23, v4.16.2 on 2026-09-09, and v4.16.3 on 2026-09-19.

The branch policy is the part that affects planning. Jint 5 is under development on `main` and has not been released, and the README states that the branch includes breaking changes with development packages available from a preview feed. The `4.x` branch is the maintenance lane for Jint 4.16, where changes preserve compatibility and focus on non-breaking correctness and conformance fixes. The `3.x` branch receives only security and severe-correctness maintenance. Pull requests target `main` unless they are explicit backports.

For an adopting team the practical question is which lane you sit on. Staying on 4.x means you get correctness fixes without API churn, and the migration guide for Jint 5 exists because the jump is not trivial. Tracking `main` means accepting breaking changes before a release. The licence is BSD-2-Clause, which is permissive and imposes few obligations beyond retaining the copyright notice and licence text; as with any dependency, confirm the terms against your own distribution model rather than relying on a summary.

## Conclusion

Adopt Jint when a .NET application needs to run user-authored JavaScript in-process, especially when scripts are prepared once and executed many times. Avoid it when the workload is CPU-bound JavaScript that a JIT-compiled engine would run faster, or when separate trust domains require process isolation. Before committing, verify the target framework list against your deployment, check which optional packages your feature set needs, and read the 4.x to 5 migration guide if you plan to track main.

## FAQ

### What is Jint?

Jint is a managed ECMAScript interpreter for .NET. The README describes it as running JavaScript directly in your process without native dependencies, bytecode generation, or a separate runtime.

### Which .NET versions does Jint support?

The README states that Jint targets .NET Framework 4.7.2, .NET Standard 2.0 and 2.1, .NET 8, and .NET 10. The optional packages have their own target frameworks, documented separately.

### Can a Jint engine be shared across threads?

The README states that Prepared<Script> and Prepared<Module> are thread-safe and may be shared across engines, but JavaScript objects are not, because a JsValue containing an object belongs to the engine and realm that created it. It also advises giving each engine to only one operation at a time.

### Does Jint include browser or Node APIs by default?

No. The README states that browser and Node APIs are not installed by default and that hosts explicitly grant the capabilities each script needs. Jint.Browser and the related tooling are separate NuGet packages.

### Is Jint an isolation boundary for untrusted code?

The README's recommended usage says to use a fresh engine across trust domains and notes that global snapshots help trusted reuse but are not an isolation boundary. Execution constraints and hardened untrusted-code defaults exist, but the documentation does not present the engine itself as a security boundary.

## Sources

- [Issues](https://github.com/sebastienros/jint/issues)
- [License: BSD-2-Clause](https://github.com/sebastienros/jint/blob/main/LICENSE)
- [README](https://github.com/sebastienros/jint/blob/main/README.md)
- [Releases](https://github.com/sebastienros/jint/releases)
- [sebastienros/jint on GitHub](https://github.com/sebastienros/jint)

---

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