Skip to main content
Bun includes a fast transpiler that converts TypeScript, JSX, and modern JavaScript to browser-compatible JavaScript. It’s written in Zig and optimized for performance.

Basic Usage

import { Transpiler } from "bun";

const transpiler = new Transpiler({
  loader: "tsx",
});

const code = `
  const greeting: string = "Hello";
  export const App = () => <div>{greeting}</div>;
`;

const output = transpiler.transformSync(code);
console.log(output);

Transpiler Options

Loader

Specify the input format:
const transpiler = new Transpiler({
  loader: "tsx",  // .tsx files (TypeScript + JSX)
});

// Supported loaders:
// "js"   - JavaScript
// "jsx"  - JavaScript + JSX
// "ts"   - TypeScript
// "tsx"  - TypeScript + JSX

Target

Set the output JavaScript version:
const transpiler = new Transpiler({
  loader: "ts",
  target: "browser",  // or "bun", "node"
});
Targets:
  • "browser" - Modern browsers (ES2020+)
  • "bun" - Bun runtime (latest features)
  • "node" - Node.js compatibility

JSX Configuration

const transpiler = new Transpiler({
  loader: "tsx",
  jsxFactory: "h",                // Default: "React.createElement"
  jsxFragment: "Fragment",        // Default: "React.Fragment"
  jsxImportSource: "preact",      // For automatic JSX runtime
});

Classic JSX Transform

const transpiler = new Transpiler({
  loader: "tsx",
  jsxFactory: "h",
  jsxFragment: "Fragment",
});

// Input:
// <div>Hello</div>

// Output:
h("div", null, "Hello")

Automatic JSX Runtime

const transpiler = new Transpiler({
  loader: "tsx",
  jsxImportSource: "react",
});

// Input:
// <div>Hello</div>

// Output includes:
// import { jsx } from "react/jsx-runtime";
// jsx("div", { children: "Hello" })

TSConfig

Load TypeScript configuration:
const transpiler = new Transpiler({
  loader: "ts",
  tsconfig: "./tsconfig.json",
});
Or provide inline:
const transpiler = new Transpiler({
  loader: "ts",
  tsconfig: {
    compilerOptions: {
      target: "ES2020",
      jsx: "react",
    },
  },
});

Macros

Enable compile-time macros:
const transpiler = new Transpiler({
  loader: "ts",
  macro: true,
});

Transform Methods

Synchronous Transform

Fastest for small inputs:
const transpiler = new Transpiler({ loader: "ts" });

const code = `const x: number = 42;`;
const output = transpiler.transformSync(code);
console.log(output); // "const x = 42;"

Transform with Source Map

const result = transpiler.transformSync(code, {
  sourceMap: "inline",  // or "external"
});

console.log(result.code);
console.log(result.map); // Source map object

Scan Imports

Extract import statements:
const code = `
  import React from "react";
  import { useState } from "react";
  import * as utils from "./utils";
`;

const imports = transpiler.scan(code);
console.log(imports);
// [
//   { kind: "import-statement", path: "react" },
//   { kind: "import-statement", path: "react" },
//   { kind: "import-statement", path: "./utils" },
// ]

TypeScript Features

Type Stripping

Types are removed:
const transpiler = new Transpiler({ loader: "ts" });

const input = `
  interface User {
    name: string;
    age: number;
  }
  
  function greet(user: User): string {
    return \`Hello, \${user.name}\`;
  }
`;

const output = transpiler.transformSync(input);
// Types removed, logic preserved

Enums

const input = `
  enum Color {
    Red,
    Green,
    Blue,
  }
  
  const c = Color.Red;
`;

const output = transpiler.transformSync(input);
// Enum transpiled to object

Decorators

const transpiler = new Transpiler({
  loader: "ts",
  tsconfig: {
    compilerOptions: {
      experimentalDecorators: true,
    },
  },
});

const input = `
  @sealed
  class MyClass {
    @readonly
    name: string;
  }
`;

const output = transpiler.transformSync(input);

Namespace

const input = `
  namespace Utils {
    export function log(msg: string) {
      console.log(msg);
    }
  }
`;

const output = transpiler.transformSync(input);
// Namespace transpiled to IIFE

JSX Features

JSX Elements

const transpiler = new Transpiler({ loader: "tsx" });

const input = `
  export const Button = ({ text }) => (
    <button className="btn" onClick={handleClick}>
      {text}
    </button>
  );
`;

const output = transpiler.transformSync(input);

JSX Fragments

const input = `
  const List = () => (
    <>
      <li>Item 1</li>
      <li>Item 2</li>
    </>
  );
`;

const output = transpiler.transformSync(input);
// <> transpiled to Fragment

JSX Spread

const input = `
  const Component = (props) => <div {...props} />;
`;

const output = transpiler.transformSync(input);

Modern JavaScript

Optional Chaining

const input = `
  const name = user?.profile?.name;
`;

const output = transpiler.transformSync(input);
// Transpiled for older targets

Nullish Coalescing

const input = `
  const value = input ?? defaultValue;
`;

const output = transpiler.transformSync(input);

Class Fields

const input = `
  class Counter {
    count = 0;
    
    increment = () => {
      this.count++;
    }
  }
`;

const output = transpiler.transformSync(input);

Top-Level Await

const input = `
  const data = await fetch(url).then(r => r.json());
  export { data };
`;

const output = transpiler.transformSync(input);
// Preserved if target supports it

Source Maps

Inline Source Map

const result = transpiler.transformSync(code, {
  sourceMap: "inline",
});

// Source map appended as data URL:
// //# sourceMappingURL=data:application/json;base64,...

External Source Map

const result = transpiler.transformSync(code, {
  sourceMap: "external",
});

console.log(result.code);  // JavaScript output
console.log(result.map);   // Source map object

// Write to file
Bun.write("output.js", result.code);
Bun.write("output.js.map", JSON.stringify(result.map));

Scan for Dependencies

Import Analysis

const code = `
  import { foo } from "./foo";
  import type { Bar } from "./bar";
  const lazy = () => import("./lazy");
  require("legacy");
`;

const imports = transpiler.scan(code);

for (const imp of imports) {
  console.log(imp.kind, imp.path);
}
// "import-statement" "./foo"
// "import-statement" "./bar"
// "dynamic-import" "./lazy"
// "require-call" "legacy"

Export Analysis

const code = `
  export const foo = 1;
  export default function bar() {}
  export { baz } from "./baz";
`;

const exports = transpiler.scanExports(code);
console.log(exports);
// ["foo", "default", "baz"]

Performance

Benchmarks

Bun’s transpiler is extremely fast:
const transpiler = new Transpiler({ loader: "tsx" });
const code = `/* 10,000 lines of TypeScript */`;

const start = performance.now();
for (let i = 0; i < 100; i++) {
  transpiler.transformSync(code);
}
const elapsed = performance.now() - start;

console.log(`${elapsed}ms for 100 iterations`);
// Typically 10-50ms
Comparison:
  • Bun: ~0.5ms per file
  • esbuild: ~1-2ms per file
  • tsc: ~50-100ms per file
  • Babel: ~100-200ms per file

Optimization Tips

  1. Reuse transpiler instance
    // Good - one instance
    const transpiler = new Transpiler({ loader: "ts" });
    for (const file of files) {
      transpiler.transformSync(file);
    }
    
    // Bad - creates many instances
    for (const file of files) {
      const t = new Transpiler({ loader: "ts" });
      t.transformSync(file);
    }
    
  2. Disable source maps when not needed
    transpiler.transformSync(code); // Faster
    transpiler.transformSync(code, { sourceMap: "inline" }); // Slower
    
  3. Use specific loaders
    new Transpiler({ loader: "js" });   // Fastest
    new Transpiler({ loader: "ts" });   // Fast
    new Transpiler({ loader: "tsx" });  // Slower (parses JSX)
    

Comparison with Other Tools

vs TypeScript Compiler (tsc)

  • Bun: Type stripping only (no type checking)
  • tsc: Full type checking + transpilation
  • Speed: Bun is ~100x faster
  • Use case: Bun for development, tsc for CI

vs esbuild

  • Bun: Built-in, no dependencies
  • esbuild: Separate tool
  • Speed: Similar (both very fast)
  • Features: Bun has tighter integration

vs Babel

  • Bun: TypeScript + JSX built-in
  • Babel: Requires plugins
  • Speed: Bun is ~200x faster
  • Flexibility: Babel has more transforms

vs SWC

  • Bun: Written in Zig
  • SWC: Written in Rust
  • Speed: Similar performance
  • Integration: Bun has deeper runtime integration

Use Cases

Build Tools

import { Transpiler } from "bun";
import { glob } from "bun";

const transpiler = new Transpiler({ loader: "tsx" });
const files = glob("src/**/*.{ts,tsx}");

for (const file of files) {
  const code = await Bun.file(file).text();
  const output = transpiler.transformSync(code);
  
  const outPath = file
    .replace("src/", "dist/")
    .replace(/\.tsx?$/, ".js");
  
  await Bun.write(outPath, output);
}

Hot Module Replacement

const transpiler = new Transpiler({ loader: "tsx" });

const watcher = fs.watch("src", { recursive: true });

for await (const event of watcher) {
  if (event.filename?.endsWith(".tsx")) {
    const code = await Bun.file(event.filename).text();
    const output = transpiler.transformSync(code);
    // Send to browser via WebSocket
    ws.send({ type: "update", code: output });
  }
}

Testing

import { Transpiler } from "bun";

const transpiler = new Transpiler({ loader: "ts" });

// Transform test files on the fly
globalThis.require = (path) => {
  const code = Bun.readFileSync(path, "utf8");
  const transpiled = transpiler.transformSync(code);
  return eval(transpiled);
};

Documentation

const transpiler = new Transpiler({ loader: "ts" });

// Extract code examples from markdown
const examples = extractCodeBlocks(markdown);

for (const example of examples) {
  try {
    const output = transpiler.transformSync(example.code);
    console.log("✓ Example is valid TypeScript");
  } catch (err) {
    console.error("✗ Example has syntax error:", err.message);
  }
}

Advanced Configuration

Custom Defines

const transpiler = new Transpiler({
  loader: "ts",
  define: {
    "process.env.NODE_ENV": '"production"',
    DEBUG: "false",
  },
});

const input = `
  if (DEBUG) {
    console.log("Debug mode");
  }
`;

const output = transpiler.transformSync(input);
// DEBUG replaced with false

Path Resolution

const transpiler = new Transpiler({
  loader: "ts",
  tsconfig: {
    compilerOptions: {
      paths: {
        "@/*": ["./src/*"],
        "components/*": ["./src/components/*"],
      },
    },
  },
});

External Packages

const transpiler = new Transpiler({
  loader: "ts",
  external: ["react", "react-dom"],
});

// Imports to external packages are preserved

Error Handling

try {
  const output = transpiler.transformSync("invalid typescript!@#$");
} catch (err) {
  console.error("Syntax error:");
  console.error(err.message);
  console.error(`Line ${err.line}, Column ${err.column}`);
}

Build docs developers (and LLMs) love