Skip to main content
Bun supports both ES modules (import/export) and CommonJS (require/module.exports) simultaneously. You can use either syntax in any file, and they interoperate without configuration.

Syntax

Consider the following two files:
import { hello } from "./hello";

hello();
Running index.ts prints “Hello world!”:
bun index.ts
Hello world!
The import path ./hello has no extension. Extensions are optional but supported. Bun checks for the following files in order when resolving an extensionless path:
  • ./hello.tsx
  • ./hello.jsx
  • ./hello.ts
  • ./hello.mjs
  • ./hello.js
  • ./hello.cjs
  • ./hello.json
  • ./hello/index.tsx
  • ./hello/index.jsx
  • ./hello/index.ts
  • ./hello/index.mjs
  • ./hello/index.js
  • ./hello/index.cjs
  • ./hello/index.json
If an extension is present, Bun only looks for a file with that exact extension.

TypeScript .js extension compatibility

If you import from "*.js" or "*.jsx", Bun additionally checks for a matching *.ts or *.tsx file. This is compatible with TypeScript’s ES module support:
import { hello } from "./hello.js"; // resolves hello.ts if hello.js is absent

Module systems

Bun has native support for both CommonJS and ES modules.

Using require()

You can require() any file or package, including .ts and .mjs files:
const { foo } = require("./foo");
const { bar } = require("./bar.mjs");
const { baz } = require("./baz.tsx");
When you require() an ES module, Bun returns the module namespace object (equivalent to import * as). When you require() a CommonJS module, it returns the module.exports object.
Module typerequire()import * as
ES moduleModule namespaceModule namespace
CommonJSmodule.exportsdefault is module.exports; named keys are re-exported

Using import

You can import from any file or package, including .cjs files:
import { foo } from "./foo";
import bar from "./bar.ts";
import { stuff } from "./my-commonjs.cjs";

Mixing import and require()

In Bun, you can use both in the same file:
import { stuff } from "./my-commonjs.cjs";
import Stuff from "./my-commonjs.cjs";

const myStuff = require("./my-commonjs.cjs");
You cannot require() a file that uses top-level await, since require() is synchronous. Use import or dynamic import() in that case.

Importing packages

Bun implements the Node.js module resolution algorithm. Import packages from node_modules using bare specifiers:
import { stuff } from "foo";
Bun scans up the file system for a node_modules directory containing the package foo.

NODE_PATH

Bun supports NODE_PATH for additional module search directories:
NODE_PATH=./packages bun run src/index.js
Multiple paths are separated by : on Unix/macOS and ; on Windows:
NODE_PATH=./packages:./lib bun run src/index.js

Package exports field

When Bun finds a package, it reads its package.json to determine the entrypoint. It checks the exports field for the following conditions in order:
{
  "name": "foo",
  "exports": {
    "bun": "./index.ts",
    "node": "./index.js",
    "require": "./index.js",
    "import": "./index.mjs",
    "default": "./index.js"
  }
}
The "bun" export condition lets you ship TypeScript source directly to npm. If you specify a *.ts entrypoint in the "bun" condition, Bun will import and execute your TypeScript files without transpilation.
Bun respects subpath exports and imports:
{
  "name": "foo",
  "exports": {
    ".": {
      "import": "./index.mjs",
      "require": "./index.js"
    }
  }
}
Specifying any subpath in exports prevents other subpaths from being imported directly. Only explicitly exported paths are accessible. If exports is not defined, Bun falls back to the "module" field (ESM only), then "main":
{
  "name": "foo",
  "module": "./index.js",
  "main": "./index.js"
}

Custom export conditions

Use --conditions to specify custom conditions when resolving exports:
bun --conditions="react-server" ./app/foo/route.js

Path re-mapping

tsconfig.json paths

Bun supports import path aliases via TypeScript’s compilerOptions.paths:
{
  "compilerOptions": {
    "paths": {
      "config": ["./config.ts"],
      "components/*": ["components/*"]
    }
  }
}
Non-TypeScript projects can use a jsconfig.json with the same structure.

package.json imports field

Bun supports Node.js-style subpath imports via the "imports" field in package.json. These paths must start with #:
{
  "imports": {
    "#config": "./config.ts",
    "#components/*": "./components/*"
  }
}
Both tsconfig.json paths and package.json imports can be used together.

import.meta

The import.meta object gives a module access to information about itself. Bun implements the following properties:
import.meta.dir;     // "/path/to/project"
import.meta.file;    // "file.ts"
import.meta.path;    // "/path/to/project/file.ts"
import.meta.url;     // "file:///path/to/project/file.ts"
import.meta.main;    // true if this file is the entry point
import.meta.resolve("zod"); // "file:///path/to/project/node_modules/zod/index.js"
PropertyDescription
import.meta.dirAbsolute path to the directory containing the current file. Equivalent to __dirname.
import.meta.dirnameAlias for import.meta.dir (Node.js compatibility).
import.meta.envAlias for process.env.
import.meta.fileThe name of the current file, e.g. index.tsx.
import.meta.pathAbsolute path to the current file. Equivalent to __filename.
import.meta.filenameAlias for import.meta.path (Node.js compatibility).
import.meta.maintrue if this file is the entry point to the current bun process, false otherwise.
import.meta.resolveResolves a module specifier to a file:// URL. Equivalent to import.meta.resolve in browsers.
import.meta.urlA file:// URL string for the current file.

Build docs developers (and LLMs) love