cheat sheet

oxc-transform

Package-level reference for oxc-transform on npm — Rust transformer from the oxc toolchain, TS/JSX support, current API maturity, and place in the ecosystem.

oxc-transform

What it is

oxc-transform is the transformer slice of the oxc (Oxidation Compiler) toolchain — a Rust rewrite of the entire JavaScript ecosystem (parser, linter, formatter, transformer, eventually resolver and bundler) led by Boshen and the oxc team. The transformer turns TypeScript and JSX into vanilla JavaScript using a Rust implementation that's measured at 5–10× faster than SWC and 50–100× faster than the TypeScript compiler.

oxc-transform is the npm-published binding to the transformer subset. It exposes a small JS API: feed it a string of TS/JSX, get back a string of JS. It does not bundle, does not resolve imports, and does not type-check — it's a transformer, like @babel/transform or swc's transform mode. Bundling sits in rolldown (which uses oxc's parser internally).

Caveat: the entire oxc toolchain is pre-1.0. The oxc-transform package surface is small enough to be reasonably stable, but the JS API has shifted between minors and may keep shifting until a 1.0 freeze. Treat as experimental for any production-critical path.

Install

bash
# Project-local — pin exact version
npm install -D oxc-transform
pnpm add -D oxc-transform
yarn add -D oxc-transform

Output: oxc-transform ships pre-built native binaries per platform via optional deps; no Rust toolchain needed on consumer machines.

Other oxc packages typically installed together:

bash
npm install -D oxc-parser oxc-resolver

Output: oxc-parser is the standalone parser; oxc-resolver is the module resolution layer. All three share a similar API style.

Versioning & Node support

  • Pre-1.0 0.x line. Each minor can include breaking API changes.
  • Requires Node 18 or newer.
  • Always pin exact versions; do not use caret ranges.
  • Underlying binary built per platform; if your CI / target lacks a published binary, the install fails — keep an eye on the published @oxc/<platform>-<arch> optional deps.
  • Active development; expect surface area to grow rather than shrink (e.g. new options for emit, decorators metadata, JSX runtime variations).

Package metadata

  • Maintainers: oxc team (Boshen et al.) under the oxc-project org
  • Project home: github.com/oxc-project/oxc
  • Docs: oxc.rs
  • npm: npmjs.com/package/oxc-transform
  • License: MIT
  • First released: 2024
  • Downloads: modest; growth is mostly transitive via tooling that embeds oxc internally.

Peer dependencies & extras

oxc-transform has zero peers. Companion packages within the oxc ecosystem:

PackageRole
oxc-parserThe standalone JS/TS parser. Returns an AST as JSON-style Node objects.
oxc-resolverRust port of Node's module resolution algorithm.
oxc-minifyMinifier (in active development; may merge into oxc-transform).
eslint-plugin-oxlintThe oxc linter (oxlint) as an ESLint plugin for gradual migration.
@oxc-project/runtimeHelper functions for some transforms (e.g. decorator metadata, async iter).

Alternatives

ToolTrade-off
esbuildMature, Go-based, similar role. oxc-transform is the Rust counterpart and aims to be faster + more correct on edge cases. esbuild wins on stability today.
swcRust transformer, the established choice. Larger surface, more mature, slightly slower than oxc. Used by Next.js, Vercel, Parcel.
@babel/coreThe classic, slow but most-extensible transformer. Use when you need exotic plugins.
tscTypeScript's own compiler. Type-checks; slow; mostly used for .d.ts emit.
sucraseJS-implemented transformer; fast for simple TS strip. Now in maintenance.

Real-world recipes

The current API is intentionally small. Recipes here cover the common shapes; verify against current docs before adopting.

Transform a single TS string

typescript
import { transform } from "oxc-transform";

const source = `
const greet = (name: string): string => \`hello, \${name}\`;
export default greet("world");
`;

const result = transform("input.ts", source);
console.log(result.code);

Output:

text
const greet = (name) => `hello, ${name}`;
export default greet("world");
//# sourceMappingURL=...

The first argument is a filename hint — used for source maps and to infer JSX vs TSX. Replace .ts with .tsx to enable JSX parsing.

Transform JSX/TSX

typescript
import { transform } from "oxc-transform";

const source = `
function Hello({ name }: { name: string }) {
  return <h1>Hello, {name}</h1>;
}
`;

const result = transform("Hello.tsx", source, {
  jsx: {
    runtime: "automatic",      // new JSX transform — react/jsx-runtime
    importSource: "react",
  },
});

console.log(result.code);

Output:

text
import { jsx as _jsx } from "react/jsx-runtime";
function Hello({ name }) {
  return _jsx("h1", { children: ["Hello, ", name] });
}

JSX-runtime options match React's modern JSX transform. Use runtime: "classic" for the older React.createElement form (rare in new projects).

Strip-types-only mode

For environments that just want types removed (no JSX, no down-leveling):

typescript
import { transform } from "oxc-transform";

const result = transform("file.ts", source, {
  typescript: {
    onlyRemoveTypeImports: false,
  },
});

Useful for Node 22+ tooling that piggy-backs on the runtime's native TS stripping but wants a programmatic API.

Sourcemap

typescript
const result = transform("input.ts", source, {
  sourcemap: true,
});

console.log(result.code);
console.log(result.map);    // JSON sourcemap v3 string

Output:

text
... compiled code ...
{"version":3,"sources":["input.ts"],...}

The sourcemap format is standard v3. Pipe through source-map-cli or upload to your error tracker.

Use inside a build script

javascript
import { transform } from "oxc-transform";
import { readFile, writeFile } from "node:fs/promises";

const code = await readFile("src/index.ts", "utf8");
const out = transform("src/index.ts", code, {
  sourcemap: true,
  jsx: { runtime: "automatic" },
});

await writeFile("dist/index.js", out.code);
await writeFile("dist/index.js.map", out.map);
bash
node build.mjs

Output:

text
(transformed src/index.ts → dist/index.js in ~3ms)

Pair with your own resolver/bundler if you need to compose multiple files. For full bundling, use rolldown (which embeds oxc) or esbuild.

As a Node loader (experimental)

There's no first-party oxc-loader for Node yet, but you can build one:

javascript
// register.mjs
import { register } from "node:module";
register("./oxc-loader.mjs", import.meta.url);
javascript
// oxc-loader.mjs
import { transform } from "oxc-transform";
import { readFile } from "node:fs/promises";

export async function load(url, context, nextLoad) {
  if (!/\.(ts|tsx)$/.test(url)) return nextLoad(url, context);
  const source = await readFile(new URL(url), "utf8");
  const result = transform(new URL(url).pathname, source);
  return { format: "module", source: result.code, shortCircuit: true };
}
bash
node --import ./register.mjs ./src/server.ts

Output:

text
Server listening on :3000

This pattern is what tsx does internally with esbuild — swapping in oxc is a one-file experiment.

Production deployment

Production use of oxc-transform is mostly indirect — your bundler embeds it, you don't call it. Direct production use scenarios:

  • Build-time TS stripping in custom toolchains. A monorepo with bespoke build steps can drop esbuild in favour of oxc-transform for marginal speed gains.
  • Code generators / editors / language servers. Anywhere you'd reach for @babel/core, oxc-transform is a faster alternative.
  • Sandboxed transformation services. A SaaS that compiles user-submitted TS to JS — oxc-transform's speed and memory profile are attractive.

Watch the version churn — pre-1.0 means breaking changes between minors. Always test against your fixtures before bumping.

Performance tuning

oxc-transform is fast by default; tuning is minimal.

Skip sourcemap for batch transforms

typescript
transform("input.ts", source, { sourcemap: false });

Sourcemap generation is the dominant cost for tiny inputs. Disable if you don't need them.

Reuse the transformer

Each call to transform() is independent. There's no context object today (unlike esbuild's incremental API). For repeated invocations, the cost is per-call — for batch jobs, parallelise across worker threads.

Avoid string round-trips

If you're chaining transformers (oxc → custom plugin → minifier), each step parses + serialises. Compose at the AST level via oxc-parser to skip serialisation.

Parallel with worker threads

javascript
import { Worker } from "node:worker_threads";
// dispatch many files across workers, each running oxc-transform

Native parallelism within oxc is being designed; until then, multi-worker is the way to scale beyond one core.

Version migration guide

Below 1.0, expect each minor to bring breakage. Migration patterns observed historically:

  • API shape: transform(filename, source, options) has been stable for several minors; older releases used a single-object signature.
  • jsx options: the option shape moved from top-level keys (jsxRuntime: "automatic") into a nested jsx: { ... } object.
  • Return value: older releases used { source, map }; current is { code, map }. Verify on the version you install.

For any specific version, the authoritative migration source is the CHANGELOG in github.com/oxc-project/oxc.

A 1.0 freeze is on the roadmap; until then, pin and test.

Security considerations

  1. Pre-1.0 supply chain. Frequent releases mean less external security review. Pin tightly.
  2. Native binary install. Same model as esbuild and rolldown — platform-specific binaries via optional deps. Lockfile pins the binary version; for offline builds, stage in advance.
  3. No type checking. oxc-transform strips types but doesn't enforce them — same as esbuild / tsx. Type-driven security boundaries need explicit runtime validation.
  4. Source-map paths. Default sourcemap output includes the filename you passed; if that's a real disk path, it leaks into the map. Pass a virtual path for libraries published as artefacts.
  5. AST output (oxc-parser). If you use oxc-parser directly, the AST is in a shape inspired by ESTree but with oxc-specific fields. Code that walks it assumes that schema — verify cross-version.

Testing & CI integration

yaml
# .github/workflows/ci.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci --include=optional
      - run: npx tsc --noEmit                       # type-check
      - run: node ./scripts/build.mjs               # invokes oxc-transform
      - run: node dist/index.js                     # smoke test

For tooling that embeds oxc-transform, snapshot-test the transformed output across a corpus — pre-1.0 means real output diffs across versions.

Ecosystem integrations

  • rolldown uses oxc-parser internally for parsing during bundling. Going forward, more of the oxc toolchain (resolver, transformer) is being threaded in.
  • Vite indirectly via rolldown — Vite is the consumer that brings oxc to most developers without their having to think about it.
  • oxlint — the linter half of oxc. Already used in some Next.js / Vercel projects as a faster ESLint replacement.
  • rspack — community-maintained oxc plugins exist; not the default transformer there yet.
  • tsdown — a thin tsup-compatible CLI built on oxc-transform; published under tsdown on npm.

Troubleshooting common errors

Cannot find module 'oxc-transform' after install

The platform binary optional dep didn't install. Fix:

bash
npm install --include=optional
npm rebuild oxc-transform

Output:

text
added 3 packages, and audited 87 packages in 1s

SyntaxError on valid TS

oxc's parser is still maturing — rare syntax (deeply nested decorators, very old TS features) may parse differently than tsc. File a repro at the oxc issue tracker. Workaround: fall back to esbuild for that file.

Output bytes differ from esbuild

Expected — different implementations. Verify behaviour with smoke tests; raw bytes will differ.

decorators option ignored

Decorator support is in progress; some target/runtime combinations don't yet emit the same metadata as tsc with emitDecoratorMetadata: true. If you need metadata for NestJS / TypeORM, stay on tsc or swc until oxc covers it fully.

Performance regression in newer version

Possible during pre-1.0 — micro-benchmarks shift between releases as the team focuses on correctness vs speed. Run your own benchmark against your codebase, not the oxc README numbers.

When NOT to use this

  • Production-critical builds today. Pre-1.0 churn means version pinning + tests on every bump. esbuild / swc are stabler choices.
  • Decorator-metadata-heavy stacks (NestJS, TypeORM). Metadata emission isn't on full parity yet. Stay on tsc until oxc closes the gap.
  • Edge-case TS syntax. The parser is excellent but newer; rare edge cases may misparse. Fall back to esbuild / tsc for unusual syntax.
  • Indirect consumption. Most developers should let Vite + rolldown adopt oxc on their behalf; direct consumption is only worth it for tooling authors.
  • Type-checking pipelines. oxc-transform doesn't type-check. For type safety, pair with tsc --noEmit regardless of which transformer you use.

See also