# swc-node: running TypeScript with node, without typechecking

> swc-node is a set of packages that let node execute TypeScript directly by transforming it with SWC and skipping type checks. It is fast, it is MIT licensed, and it deliberately does not tell you when your types are wrong.

**swc-project/swc-node** — Faster ts-node without typecheck. Detail: @swc-node/core Benchmark transform RxJS AjaxObservable.ts to ES2015 & CommonJS JavaScript.

- Repository: https://github.com/swc-project/swc-node
- Stars: 1,982 · Forks: 95
- Language: TypeScript
- License: MIT
- Published: 2026-08-04 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/swc-project-swc-node

## What swc-node replaces, and for whom

The README describes the project as a fast TypeScript and JavaScript transformer without node-gyp and without a postinstall script. That sentence is the whole pitch. Tools in this space usually ship a native binding that has to be compiled on install, or they download a platform binary after install. swc-node's packages avoid both, which matters in CI images where a compile step either fails or adds minutes.

The audience is narrower than "TypeScript users". It is people who run TypeScript through node or jest and who have decided that type errors should be caught by a separate tsc run, not by the process that executes the file. The README states the register package plainly: it is a faster alternative to ts-node/register/transpile-only. That qualifier is doing the work. If you never used transpile-only, you were paying for type checking at startup, and swc-node is asking you to give that up.

The repository is a pnpm workspace with three published packages under packages/: core, register, and jest. The root package.json sets private to true, so the repository itself is not what you install. You install one of the three packages depending on whether you need a transformer, a node loader, or a jest transform.

## How the transform pipeline is wired

@swc-node/core is the transformer. The README calls it the fastest TypeScript transformer and points at a bench directory for the code that produced the numbers. It exposes transformSync and a parallel transform. The benchmark transforms RxJS's AjaxObservable.ts to ES2015 and CommonJS JavaScript on a 6-core Intel i7 MacBook Pro. In the synchronous run the README reports esbuild at 510 ops/sec and @swc-node/core at 438 ops/sec, with typescript at 28.83 and babel at 24.21. In the parallel run with UV_THREADPOOL_SIZE=11, @swc-node/core is listed at 1,253 ops/sec against esbuild's 914, and the README marks it fastest. Those are the project's own numbers from its own hardware description, not an independent measurement, and the parallel result depends on setting UV_THREADPOOL_SIZE yourself.

@swc-node/register sits on top of core and hooks into node's module resolution. In CommonJS you load it with node -r. In ESM you load it as a loader or an import hook, and the README splits that by Node version. @swc-node/jest is the same idea applied to jest's transform interface.

The data flow is one-directional: node asks for a .ts file, the hook hands the source to SWC, SWC emits JavaScript with types erased, and node executes that. There is no type graph, no project-wide analysis, and no diagnostic pass. That is why it is fast and why it cannot warn you. A file with a wrong argument type runs exactly as if it were correct until the wrong value reaches code that cares.

## Installing @swc-node/register and running a first file

The README's usage section installs the register package as a dev dependency and then runs node with the -r flag. The package name and flag are exactly as documented.

```bash
npm i -D @swc-node/register
node -r @swc-node/register script.ts
```

If script.ts contains type annotations, the process should start and print whatever the file prints. Nothing else changes: no output directory, no build artifact, no .js file on disk.

For an ESM project the README gives version-specific entry points, and it asks you to pass --enable-source-maps to node so stack traces point back at the TypeScript source.

```bash
node --import @swc-node/register/esm-register-next --enable-source-maps script.ts # node>=22.15
node --import @swc-node/register/esm-register --enable-source-maps script.ts # node>=20.6
```

A third form, --loader @swc-node/register/esm, is documented for Node 20.5 and earlier and marked deprecated in the README itself.

Configuration is opt-in. The README states that you set SWCRC=true to load a .swcrc file. If you do not set it, your .swcrc is ignored and the defaults apply.

```bash
SWCRC=true node -r @swc-node/register script.ts
```

The README also shows a shebang form for executable scripts, and notes that adding TS_NODE_PROJECT=null makes the loader ignore tsconfig.json.

## The typechecking gap is the design, not a bug

Every speed claim in the README comes from removing work, and the work removed is type checking. The register package is positioned against transpile-only, which means the failure mode is identical to transpile-only: a type error that would have stopped ts-node surfaces at runtime instead, or never surfaces because the branch is not exercised.

That has a practical consequence for how you place swc-node in a pipeline. It is a runtime and test-time tool. The README does not document a typecheck command, and the repository's own build script is tsc -b tsconfig.json, which is the maintainers type checking their source with the TypeScript compiler rather than with swc-node. That is a fair signal about intended use: transform with SWC, check with tsc.

The second limitation is configuration surface. Because SWCRC defaults to off, a project that keeps its transform settings in .swcrc will silently run with different settings until someone sets the environment variable. The README documents the switch but does not document rollback or a way to see which config was applied, so diagnosing a mismatch means reading the loader's behavior rather than a log line.

The third is version coupling. The ESM story is split across three entry points keyed to Node versions, and one of them is already marked deprecated. If your team runs mixed Node versions across local machines and CI, the invocation line has to be chosen per environment. That is friction that a single ts-node command does not have.

## swc-node versus ts-node and tsx

ts-node is the direct comparison, and the README frames swc-node as the faster alternative to its transpile-only mode. The real difference is that ts-node can also run with full type checking in the same command, using the TypeScript compiler's own program. swc-node cannot, because it never builds a type program. So the choice is not which tool is better but whether you want the checker in the execution path. If you do, ts-node with checking is the tool, and swc-node is the wrong one.

tsx is the other obvious option for running TypeScript on node, and the search data shows people comparing the two directly. The README does not mention tsx, so nothing here can describe its internals. What can be said is the shape of the difference: swc-node is a set of packages with a transformer, a register hook, and a jest transform, so the same core serves both your test runner and your script execution. A single-purpose runner does not give you the jest transform.

esbuild appears in the README only as a benchmark competitor inside @swc-node/core's numbers, where it wins the synchronous case and loses the parallel one. The README does not present esbuild as a runtime loader here, so treating those numbers as a general esbuild comparison would overread them.

## Jest and the maintenance picture

@swc-node/jest exists because jest's transform interface is separate from node's module loading, so the register hook does not help inside a test run. The README's performance glance compares it against ts-jest configured with isolatedModules: true, which is already ts-jest's fast path. On a pure TypeScript project targeting ES2018, run with npx jest --no-cache, the README reports ts-jest at 54.631 s for 49 suites and 254 tests, and @swc-node/jest at 10.511 s for the same counts. Both runs report all tests passing. The comparison is the project's own, on its own project, and isolatedModules: true means the ts-jest baseline was not type checking either, so the gap is transform cost rather than checking cost.

On maintenance: the last push to the default branch was on 2026-07-16, and the most recent release listed is @swc-node/core@1.15.0 on the same date. The register package's last listed releases are 1.10.9 on 2024-07-17 and 1.10.8 on 2024-07-16. So core has moved recently while the register line has not had a listed release in about two years. That does not mean register is abandoned, but it does mean the package most readers will install is the one with the older release history, and the ESM entry points it documents are the part most exposed to Node version changes.

The licence is MIT across the repository, which permits commercial use and modification with the licence text retained. That is a statement about the licence file, not legal advice about your situation.

## Conclusion

Adopt swc-node if you want node to execute .ts files or jest to transform TypeScript without paying for typechecking on every run, and you already run tsc separately or accept that types are not enforced at runtime. Do not adopt it if you need ts-node's type checking during execution, or if you rely on a build step that reads tsconfig paths and emit options at runtime. Verify first which entry point matches your Node version: node -r @swc-node/register for CommonJS, node --import @swc-node/register/esm-register on Node 20.6 and later, or --import @swc-node/register/esm-register-next on Node 22.15 and later. Then confirm whether your project needs .swcrc loaded, since that requires SWCRC=true.

## FAQ

### What is swc-node?

It is a set of packages that transform TypeScript and JavaScript so node and jest can run the files directly. The README describes it as a fast transformer without node-gyp and without a postinstall script, and it publishes @swc-node/core, @swc-node/register and @swc-node/jest.

### What is swc-node register?

@swc-node/register is the package that hooks into node's module loading so .ts files can be executed without a build step. The README calls it a faster alternative to ts-node/register/transpile-only.

### How does swc-node compare with ts-node?

The README positions @swc-node/register against ts-node/register/transpile-only, so the speed comes from not type checking. ts-node can run with type checking in the same command, which swc-node does not do.

### What is SWC in TypeScript?

SWC is the transformer that swc-node wraps: the README describes the project as a fast TypeScript and JavaScript transformer, and @swc-node/core is the package that exposes transformSync and a parallel transform. It erases types rather than checking them.

### How do I run a TypeScript file with node?

The README's usage section installs @swc-node/register as a dev dependency and runs node -r @swc-node/register script.ts. For ESM projects it gives --import entry points for Node 20.6 and later and for Node 22.15 and later, with --enable-source-maps recommended.

## Sources

- [Official README](https://github.com/swc-project/swc-node#readme)
- [Project repository](https://github.com/swc-project/swc-node)
- [Release notes](https://github.com/swc-project/swc-node/releases)

---

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