Open-source project
folke/esbuild-runner avatar
folke/esbuild-runner

esbuild-runner runs TypeScript directly without a build step

⚡️ Super-fast on-the-fly transpilation of modern JS, TypeScript and JSX using esbuild

732 stars21 forksTypeScriptApache-2.0

At a glance

What is it?
esbuild-runner is a small Apache-2.0 wrapper around esbuild that transpiles JavaScript, TypeScript and JSX on the fly, either through the esr binary, a require hook, a Jest transform, or a VSCode debug configuration. It is fast and small, and its release history shows it has not been touched in a long time.
Who is it for?
esbuild-runner suits a developer who wants to run a single TypeScript file, a test file or a small script without setting up a bundler, since the esr binary and the require hook cover both the command line and the in-process cases in one line of configuration.
Can I use it commercially?
Yes. Apache-2.0 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 received new commits within the last day.
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 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two ways to get esbuild-runner into a process

esbuild-runner provides on-the-fly transpilation of modern JS, TypeScript and JSX using esbuild, and it gives you two ways in.

The first is the global binary:

shell
$ esr hello-world.ts

The second is a require hook, so any nodejs process can pick it up:

shell
$ node -r esbuild-runner/register hello-world.ts

That second form is the one that matters for tooling. Because it is a standard Node require hook, it works anywhere Node lets you inject a preload module, which is why the same package can also serve as a Jest transform and as a VSCode debug configuration.

The stated purpose is to run arbitrary code or tests without needing to build your whole project. That framing is the whole point of the project. esbuild itself is a bundler and transpiler that is fast, and esbuild-runner is the small adapter that lets you use it without writing a build configuration file for every script you want to try.

The package is published on npm as esbuild-runner with the main entry point at lib/index.js and a single binary called esr, mapped to ./bin/esr.js. The repository is written in TypeScript, the licence is Apache 2.0, and the author is Folke Lemaitre.

Bundling versus transform, and the cache flag

The configuration section explains that esr provides two different ways to transpile your code, and the difference matters for how fast the first run is and what gets rebuilt.

Bundling is the default. It transpiles the script and all its dependencies in typically one invocation of esbuild. Dependencies defined in package.json or in node_modules will never be transpiled. Running esr will always transpile the code, and no caching is used.

Transform is the other mode, enabled with --cache. It invokes esbuild for every source file, but caches the result, so the initial run is slower and after that only changed source files are transpiled.

That is the classic bundle versus per-file trade-off, and the README states it plainly. The default is the right choice for a one-off script run, where a single esbuild invocation beats assembling a cache. The cached mode is the right choice for a watch loop or a test run over many files, where the first cost is amortised.

The command line is small enough to read in full:

shell
$ bin/esr.js --help
Usage: esr [options] <source-file> [file-options]

  --cache       Transform on a file per file basis and cache code
  --clearCache  Clear transform cache
  --help|-h     Display this help message

Note the --clearCache flag alongside --cache. A cached transpile that keeps serving stale output after you change a dependency is a real failure mode, and the escape hatch is part of the interface rather than something you have to find in the source.

The config file for esbuild options

If you need to pass options through to esbuild, the mechanism is a file rather than a flag, and the README is explicit about where it looks.

You can create an esbuild-runner.config.js file in the current directory or one of the ancestor directories, and it is picked up automatically:

js
// example esbuild-runner.config.js
module.exports = {
  type: "bundle", // bundle or transform (see description above)
  esbuild: {
    // Any esbuild build or transform options go here
    target: "esnext",
  },
}

Two things are worth noticing. The type key sets the mode described above, bundle or transform, so the config file can change the default rather than only supplement it. And the esbuild key is a free-form pass-through, so any esbuild build or transform option goes there, which means the full esbuild option surface is available without esbuild-runner needing to mirror it.

The target: esnext in the example is the setting that matters most in practice. It asks esbuild to emit code at the newest language level, which is the right default when the runtime is a modern Node, and it is what makes the on-the-fly path quick, since there is no downlevelling work to do.

Wiring it into Jest and into VSCode

The two integration recipes in the README are the parts most people actually copy.

For Jest, you need to configure a Jest transform in your jest.config.js:

js
module.exports = {
  transform: {
    "\\.ts$": "esbuild-runner/jest",
  },
}

The transform key is a glob pattern mapped to a transformer module, and the esbuild-runner/jest entry is the adapter for that role. The repository carries a jest.js file at the top level, and it is listed in the package.json files array alongside lib, bin and register.js, so the transformer is published with the package rather than living in the repository only.

For VSCode, the README gives a complete launch configuration:

JSON
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug with esbuild-runner",
      "program": "${workspaceFolder}/hello-world.ts",
      "runtimeArgs": [
        "-r",
        "esbuild-runner/register"
      ],
      "request": "launch",
      "sourceMaps": true,
      "skipFiles": [
        "<node_internals>/**"
      ],
      "type": "pwa-node"
    }
  ]
}

The two settings that matter here are sourceMaps set to true and the runtimeArgs array passing -r esbuild-runner/register, which is the require hook again. The sourceMaps flag is what makes stepping through transpiled code bearable, since without it the debugger is looking at generated output rather than your TypeScript.

Installing it

Installation is the npm package, installed globally or locally, with the choice depending on how you plan to use it.

Globally, if you want the esr binary on your path:

shell
$ npm install -g esbuild-runner

Or locally in your project, which is the usual choice for a dev dependency:

shell
$ npm add --dev esbuild-runner

The repository uses pnpm for its own development, with a pnpm-lock.yaml at the top level, and the package.json scripts show the shape of the build: clean runs rimraf lib, lint runs eslint over src with the .ts,.js extensions, prebuild runs clean and lint together, build:ts runs tsc --pretty, and build runs build:ts.

The dependency versions in the devDependencies are worth noting for anyone pinning their own setup. esbuild is pinned at 0.15.12 and the TypeScript tooling, @types/node and the eslint plugins, sit in the same 2022 era, matching the release dates. The package.json version field reads 2.2.1 while the newest tagged release is 2.2.0.

There is a .husky directory and a renovate.json in the repository root, so git hooks and automated dependency updates are part of the contributor setup, and the release process is handled by release-it with a conventional-changelog preset writing to CHANGELOG.md.

The release gap, and what it means for a dependency

Here is the part of the record that deserves attention before you add this to a project.

The recent releases are 2.2.0 from 2021-06-04, 2.1.0 from 2021-06-01 and 2.0.0 from 2021-05-29, three versions in a week, and then nothing. The last push to the repository was on 2026-10-16, so the code is being touched, but the published npm versions have not moved in more than five years. The package.json version field reads 2.2.1, one above the newest tag, which suggests the working tree is ahead of what is published.

The topics list includes ts-node and tsnode, which is the clearest signal of what this project is competing with, and the difference is mechanism rather than speed. ts-node type checks by default and its behaviour is configurable, while esbuild-runner transpiles only. esbuild does not type check at all, so a type error in your file will not stop esr from running it.

That is the trade you are making. You give up static type feedback at the moment you run the file, and you get a transpile path that does not need a build configuration. For a script you are iterating on, that is usually a good deal. For a codebase where type errors are caught early in the pipeline, it is not.

The other difference to weigh is ecosystem age. A package last released in 2021 depends on an esbuild from 2022, and esbuild is a native binary distributed as platform-specific packages, so the version your project pins and the version esbuild-runner was built against can drift apart. Nothing in the README addresses that, and it is the first thing to test in your own project.

Editorial conclusion

esbuild-runner suits a developer who wants to run a single TypeScript file, a test file or a small script without setting up a bundler, since the esr binary and the require hook cover both the command line and the in-process cases in one line of configuration. It is the wrong tool for a production build, because the README describes it as a way to run code or tests without needing to build your whole project, and it is the wrong tool if you need type checking, since it transpiles without checking types. Before adopting it, check the release situation first, because the newest tagged release is 2.2.0 from 2021-06-04 while the repository itself was pushed to on 2026-10-16, and confirm that the esbuild version your project pins still works with it.

Frequently asked questions

How do I run a TypeScript file with esbuild-runner?

Install the package globally with npm install -g esbuild-runner and run esr hello-world.ts, or require the hook inside a nodejs process with node -r esbuild-runner/register hello-world.ts. The hook form is what the Jest transform and the VSCode debug configuration also rely on.

What is the difference between the bundle and transform modes?

Bundling is the default and transpiles the script and its dependencies in typically one invocation of esbuild, with no caching. Transform, enabled with --cache, invokes esbuild for every source file but caches the result, so the initial run is slower and afterwards only changed source files are transpiled.

How do I configure esbuild options in esbuild-runner?

Create an esbuild-runner.config.js file in the current directory or one of the ancestor directories. Its type key sets bundle or transform, and its esbuild key holds any esbuild build or transform options, such as target: esnext.

How do I use esbuild-runner with Jest?

Add a transform entry to your jest.config.js mapping the file pattern to the transformer, for example transform: { "\\.ts$": "esbuild-runner/jest" }. The transformer itself is published as jest.js in the package.

Does esbuild-runner type check TypeScript?

No. It transpiles with esbuild, which does not perform type checking, so type errors will not stop the file from running. The project is positioned as a way to run code or tests without building your whole project, not as a replacement for a type checking step.

Official sources

  1. folke/esbuild-runner on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
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/folke-esbuild-runner.svg)](https://hysenlabs.com/projects/folke-esbuild-runner)

Was this useful?