Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

To load JavaScript held in a Go string, embed a JavaScript runtime and pass the string to its evaluation method. Goja is the clearest current option for this job: create a runtime with goja.New(), call RunString, check the returned error, and use the returned goja.Value or export it into a Go variable. The complete minimum program is:

package main

import (
    "fmt"
    "github.com/dop251/goja"
)

func main() {
    vm := goja.New()
    value, err := vm.RunString(`2 + 2`)
    if err != nil {
        panic(err)
    }
    fmt.Println(value.Export())
}

This prints 4. RunString evaluates source in the runtime’s global context; it does not create a browser or Node.js environment. Goja documents ECMAScript 5.1 support, with much of ES6 still in progress, so confirm that the syntax and APIs in your script are supported before selecting it.

Install Goja and run a string

Create a module, add Goja, and run a program:

mkdir go-js-string
cd go-js-string
go mod init example.com/go-js-string
go get github.com/dop251/goja

Save this as main.go:

package main

import (
    "fmt"
    "log"

    "github.com/dop251/goja"
)

func main() {
    source := `
        var answer = 6 * 7;
        answer;
    `

    vm := goja.New()
    value, err := vm.RunString(source)
    if err != nil {
        log.Fatalf("JavaScript failed: %v", err)
    }

    fmt.Printf("JavaScript returned %v (Go type %T)n", value.Export(), value.Export())
}

Run it with go run .. The value returned by the final expression is exported as a Go representation. Keeping the error check immediately after RunString matters: parsing errors and runtime exceptions are reported through that error, and the value should not be used as though evaluation succeeded.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The API described in the Goja package documentation executes the supplied string in the runtime’s global context. A runtime can execute several related snippets, and variables defined in one call remain in that runtime for later calls:

vm := goja.New()
if _, err := vm.RunString(`var greeting = "hello"`); err != nil {
    return err
}
value, err := vm.RunString(`greeting + " Go"`)
if err != nil {
    return err
}
fmt.Println(value.Export()) // hello Go

Use one runtime per independent script context. Sharing a runtime between unrelated users or requests can leak variables and state.

Pass data from Go into JavaScript

Set simple values

Use Runtime.Set to expose a Go value as a global JavaScript variable. Goja also provides Runtime.ToValue when you need an explicit JavaScript value:

package main

import (
    "fmt"
    "log"

    "github.com/dop251/goja"
)

func main() {
    vm := goja.New()
    if err := vm.Set("name", "Ada"); err != nil {
        log.Fatal(err)
    }

    value, err := vm.RunString(`"Hello, " + name`)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(value.Export())
}

Goja converts common Go values such as strings, numbers, booleans, slices, maps, and structs into JavaScript values. For a stable boundary, define the data shape you expect and export the result into a typed Go destination rather than relying only on reflection’s default output.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Export into a typed Go value

Value.Export() is convenient for logging or simple results. The README also documents ExportTo for conversion into a specified Go variable:

type Result struct {
    Total int `json:"total"`
}

vm := goja.New()
value, err := vm.RunString(`({ total: 3 + 4 })`)
if err != nil {
    return err
}

var result Result
if err := value.ExportTo(&result); err != nil {
    return err
}
fmt.Println(result.Total) // 7

Return an object expression in parentheses when the entire source is an object literal; otherwise JavaScript may parse the braces as a block statement.

Call a JavaScript function from Go

Define a function in the string, retrieve it from the runtime, and assert that it is callable. Goja’s README demonstrates this pattern with goja.AssertFunction:

package main

import (
    "fmt"
    "log"

    "github.com/dop251/goja"
)

func main() {
    vm := goja.New()
    if _, err := vm.RunString(`
        function multiply(a, b) {
            return a * b;
        }
    `); err != nil {
        log.Fatal(err)
    }

    value := vm.Get("multiply")
    multiply, ok := goja.AssertFunction(value)
    if !ok {
        log.Fatal("multiply is not a JavaScript function")
    }

    result, err := multiply(goja.Undefined(), vm.ToValue(6), vm.ToValue(7))
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result.Export()) // 42
}

The first argument is the JavaScript this value. Use goja.Undefined() when the function does not require a receiver. Convert arguments with ToValue, and check the call’s error just as you check the initial evaluation error.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build a reusable evaluation helper

For application code, centralize error handling and typed conversion instead of scattering calls throughout handlers:

package jsrunner

import "github.com/dop251/goja"

func Evaluate(source string, input any) (any, error) {
    vm := goja.New()
    if input != nil {
        if err := vm.Set("input", input); err != nil {
            return nil, err
        }
    }

    value, err := vm.RunString(source)
    if err != nil {
        return nil, err
    }
    return value.Export(), nil
}

This helper creates a fresh global scope for every call. If you intentionally need a warm runtime for repeated scripts, manage its lifetime explicitly and document which state is allowed to persist.

What JavaScript environment are you getting?

Goja is an embedded, pure-Go engine, not a web browser. A script loaded from a string does not automatically have window, document, DOM APIs, browser storage, network fetch, or Node.js modules. If the source expects those globals, it will fail unless your program deliberately supplies compatible host functions and objects.

The Goja README describes ECMAScript 5.1 support and says most ES6 functionality remains in progress. Check the exact Goja version in your go.mod, then test syntax such as arrow functions, modules, optional chaining, or built-ins before deploying. Do not infer browser compatibility from the fact that ordinary JavaScript expressions run.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle syntax and runtime errors

Syntax errors

Malformed source, unsupported syntax, and unterminated strings cause RunString to return an error. Preserve the source location in your own error message so a caller can identify which script failed:

value, err := vm.RunString(source)
if err != nil {
    return fmt.Errorf("evaluate JavaScript %q: %w", scriptName, err)
}

Exceptions thrown by the script

A valid program can still throw at runtime:

value, err := vm.RunString(`throw new Error("bad input")`)
if err != nil {
    // err contains the JavaScript exception and, where available, source details.
    return err
}
_ = value

Never ignore this error. Logging the source itself may disclose credentials or personal data, so log a script identifier and a sanitized error instead.

Unexpected result types

JavaScript has values that do not map cleanly to ordinary Go values, including undefined, null, NaN, functions, and cyclic objects. Inspect the value before exporting when your input is not fully controlled, and prefer ExportTo with a known schema for API boundaries.

Security and resource limits

An embedded interpreter is not automatically a security sandbox. The reviewed Goja and Otto documentation does not establish isolation from hostile code. Treat JavaScript supplied by users, plugins, or remote configuration as untrusted.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Run untrusted scripts in a separately isolated process or stronger OS/container boundary appropriate to your threat model.
  • Apply CPU, memory, wall-clock, and output limits outside the interpreter.
  • Do not expose secrets, filesystem access, network clients, or powerful Go callbacks to untrusted code.
  • Use an allowlist for every host function you register.
  • Audit dependencies and pin the Goja version in your module.

Goja documents an interruption mechanism for stopping execution, but an interruption example is not a complete security guarantee. Design cancellation and process-level resource controls before accepting arbitrary code.

Goja versus Otto

Otto is another Go JavaScript interpreter. Its documentation says Run accepts source text, parses it when needed, and returns a value and an error. That makes it a reasonable alternative for basic embedded execution:

vm := otto.New()
value, err := vm.Run(`2 + 2`)
if err != nil {
    return err
}
fmt.Println(value.Export())

Choose between them using the requirements you can verify for your project:

Question Goja Otto
String evaluation API Runtime.RunString VM.Run
Exchange values Set, ToValue, Export, ExportTo Use the APIs documented by the project for its value type
Language support evidence README documents ECMAScript 5.1, with much ES6 in progress Not stated in the reviewed documentation
Performance or compatibility ranking Not established by the cited sources Not established by the cited sources
Security isolation Not established as a sandbox Not established as a sandbox

The cited sources do not provide an apples-to-apples current benchmark or comprehensive compatibility matrix, so test your actual scripts rather than choosing on an unsupported performance claim.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common failures and fixes

undefined is not defined or document is not defined

Your script expects browser globals. Rewrite it for an embedded runtime or provide a narrowly scoped host implementation; Goja does not supply a DOM.

require is not defined

require is a Node.js convention, not a built-in Goja feature. Bundle the code into one supported script or implement a controlled module loader yourself.

Modern syntax fails to parse

Check the ECMAScript feature against the Goja version and its documented support. Transpile the source to syntax the runtime supports, or select an engine whose documented language level matches your application.

The result is empty or the wrong type

The value returned is the final expression’s value. Add an explicit final expression, return an object in parentheses, and use ExportTo when you need a typed result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A second script cannot see variables

You likely created a new runtime. Reuse the same *goja.Runtime for intentionally shared state, or explicitly pass values between isolated runtimes.

The process becomes slow or unresponsive

Bound script size and execution time, avoid exposing expensive callbacks, and use a cancellation or interruption design. For hostile workloads, move execution to a separately supervised process.

Or skip the browser setup

If your actual goal is obtaining a clean image or PDF of a page rather than executing JavaScript inside Go, ScreenshotNeo provides a one-request screenshot API. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and each response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());

See the ScreenshotNeo documentation for request options. An MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for the free plan.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can I execute JavaScript without adding a runtime dependency?

No. Go does not include a JavaScript interpreter in its standard library; you need an embedded engine such as Goja or Otto, or an external process.

Does RunString load JavaScript modules from a file or URL?

No. It evaluates the source text supplied to it. Fetching, resolving, and securely loading additional modules is application code you must design.

Can one Goja runtime be used concurrently?

Treat a runtime as stateful and do not assume concurrent safety. Serialize access or give each concurrent task its own runtime unless the Goja version’s documentation explicitly guarantees your usage pattern.

How do I return JSON from a script?

Have the script produce an object or JSON string, then validate and convert it in Go. For structured data, ExportTo into a defined Go type and reject unexpected fields or types.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.