---
title: "Run JavaScript in C#"
url: https://recursiveai.org/oneecma/cookbook/run-javascript/
description: "Run JavaScript in C# and get the result back: a complete .NET program that evaluates a script with an embedded JavaScript engine, passes values in and reads numbers, strings and objects out."
version: "1.0.0"
---

# Run JavaScript in C#

Create a `ScriptEngine` and call `Evaluate`: it runs the script and returns the value of its last expression. The engine is pure C#, so there is no Node.js, V8 or other native library to install.

```shell
dotnet new console -o RunJs && cd RunJs
dotnet add package RecursiveAI.OneEcma --version 1.0.0
```

Put this in `Program.cs` and run `dotnet run`:

```csharp
using System.Text.Json;
using RecursiveAI.OneEcma;

var engine = new ScriptEngine();

// The value of the last expression comes back as a JsValue.
JsValue sum = engine.Evaluate("[1, 2, 3, 4].reduce((a, b) => a + b, 0)");
Console.WriteLine($"sum: {sum.AsNumber}");

// Pass a value in by putting it on the global object.
engine.SetProperty(engine.Global, "name", JsValue.FromString("Ada"));
JsValue greeting = engine.Evaluate("`Hello, ${name.toUpperCase()}!`");
Console.WriteLine(greeting.AsString);

// Globals and functions stay defined for the next Evaluate.
engine.Evaluate("""
    function invoice(items) {
      const total = items.reduce((t, i) => t + i.qty * i.price, 0);
      return { count: items.length, total: Math.round(total * 100) / 100 };
    }
    """, "invoice.js");

// Objects: return JSON from the script and parse it in C#.
JsValue json = engine.Evaluate(
    "JSON.stringify(invoice([{ qty: 3, price: 1.5 }, { qty: 2, price: 4.25 }]))");
using JsonDocument doc = JsonDocument.Parse(json.AsString);
Console.WriteLine($"items: {doc.RootElement.GetProperty("count")}, " +
    $"total: {doc.RootElement.GetProperty("total")}");
```

```text
sum: 10
Hello, ADA!
items: 2, total: 13
```

`Evaluate` takes a file name as its second argument; error reports and `err.stack` use it. A script that throws reaches C# as a `JsException` whose `Message` is what the script would see (`TypeError: …`), with a [readable report](https://recursiveai.org/oneecma/cookbook/error-reports/) on `Report`.

## Reading values

A `JsValue` is any JavaScript value. `AsNumber`, `AsString`, `AsBoolean` and `AsObject` read it as that type, `IsUndefined` checks for `undefined`, and `engine.ToDisplayString(value)` converts anything to a string the way JavaScript’s `String(value)` does. For arrays and objects, `JSON.stringify` in the script and `System.Text.Json` in C#, as above, is the shortest path; `engine.GetProperty(obj, "name")` reads one property at a time.

## Limits

- **The language and its built-ins only.** There is no `require`, `fetch`, `process`, file system or `console` unless you provide them: see [Call C# from JavaScript](https://recursiveai.org/oneecma/cookbook/host-functions/).
- **One engine per thread.** A `ScriptEngine` isn’t thread-safe; create one per thread or per script.
- **No time limit by default.** A script that loops forever runs forever. For code you didn’t write, set limits: see [Run untrusted JavaScript safely](https://recursiveai.org/oneecma/cookbook/sandbox/).
- **.NET 10.** The package targets `net10.0`.

To call a function defined in a script with arguments from C#, see [Call a JavaScript function from C#](https://recursiveai.org/oneecma/cookbook/call-javascript-function/). The full tour is [Getting started](https://recursiveai.org/oneecma/docs/getting-started/).
