Skip to main content
Bun.file and Bun.write are heavily optimized and are the recommended way to perform file-system tasks in Bun. For operations not yet available natively—such as mkdir or readdir—use Bun’s nearly complete implementation of node:fs.

Reading files with Bun.file()

Bun.file(path) returns a BunFile instance — a lazily-loaded reference to a file on disk. The file is not read until you call a method on it.
const file = Bun.file("data.txt"); // relative to cwd
file.size;  // number of bytes
file.type;  // MIME type, e.g. "text/plain;charset=utf-8"
BunFile conforms to the Blob interface. Read its contents in multiple formats:
const text = await Bun.file("data.txt").text();

File references

You can create a BunFile using a path string, a file descriptor number, or a file:// URL:
Bun.file("foo.txt");                    // relative to cwd
Bun.file("/absolute/path/to/foo.txt");  // absolute path
Bun.file(1234);                         // file descriptor number
Bun.file(new URL(import.meta.url));     // reference to the current file

File metadata

const file = Bun.file("data.txt");

file.size;          // number of bytes (0 if file doesn't exist)
file.type;          // MIME type string
await file.exists(); // boolean — false if file doesn't exist
A BunFile can reference a path that doesn’t exist yet. No error is thrown until you try to read it:
const ghost = Bun.file("missing.txt");
ghost.size;             // 0
ghost.type;             // "text/plain;charset=utf-8"
await ghost.exists();   // false
To override the inferred MIME type, pass a second argument:
const file = Bun.file("data.json", { type: "application/json" });
file.type; // "application/json;charset=utf-8"

Standard I/O streams

Bun exposes stdin, stdout, and stderr as BunFile instances:
Bun.stdin;   // readonly
Bun.stdout;
Bun.stderr;

Deleting files

await Bun.file("logs.json").delete();

Writing files with Bun.write()

Bun.write(destination, data) writes data to disk and returns a Promise<number> (bytes written). Destination can be a string path, file:// URL, or BunFile. Data can be a string, Blob, BunFile, ArrayBuffer, TypedArray, or Response.
await Bun.write("output.txt", "Hello, world!");
Bun automatically selects the fastest available system call for each input/output combination (e.g., copy_file_range on Linux, clonefile on macOS).

Incremental writing with FileSink

For streaming or incremental writes, use FileSink — Bun’s native incremental file writer.

Basic usage

const file = Bun.file("output.txt");
const writer = file.writer();

writer.write("first chunk\n");
writer.write("second chunk\n");

writer.flush(); // flush buffer to disk
writer.end();   // flush and close

High water mark

Control when the buffer auto-flushes by setting a highWaterMark:
const writer = Bun.file("output.txt").writer({
  highWaterMark: 1024 * 1024, // flush after 1 MB
});

Process lifetime

By default, the Bun process stays alive until FileSink is closed. Opt out with .unref():
writer.unref(); // process won't wait for this sink
writer.ref();   // re-attach if needed

Module path helpers

Bun provides module-relative path helpers on import.meta:
import.meta.dir;   // absolute path to the directory of the current file
import.meta.file;  // filename of the current file (e.g. "index.ts")
import.meta.path;  // absolute path to the current file
These are useful for constructing paths relative to the current source file:
const configPath = import.meta.dir + "/config.json";
const config = await Bun.file(configPath).json();

Directory operations

Bun does not yet have a native directory API. Use node:fs instead.
import { readdir } from "node:fs/promises";

const files = await readdir(import.meta.dir);

API reference

interface Bun {
  stdin: BunFile;
  stdout: BunFile;
  stderr: BunFile;

  file(path: string | number | URL, options?: { type?: string }): BunFile;

  write(
    destination: string | number | BunFile | URL,
    input:
      | string
      | Blob
      | ArrayBuffer
      | SharedArrayBuffer
      | TypedArray
      | Response,
  ): Promise<number>;
}

interface BunFile extends Blob {
  readonly size: number;
  readonly type: string;

  text(): Promise<string>;
  json(): Promise<any>;
  bytes(): Promise<Uint8Array>;
  arrayBuffer(): Promise<ArrayBuffer>;
  stream(): ReadableStream;
  exists(): Promise<boolean>;
  delete(): Promise<void>;
  writer(params?: { highWaterMark?: number }): FileSink;
}

interface FileSink {
  write(
    chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer,
  ): number;
  flush(): number | Promise<number>;
  end(error?: Error): number | Promise<number>;
  ref(): void;
  unref(): void;
}

Glob

Bun.Glob provides fast native file globbing. Use it to scan directories or match strings against patterns.
import { Glob } from "bun";

// Scan a directory for all TypeScript files
const glob = new Glob("**/*.ts");

for await (const file of glob.scan(".")) {
  console.log(file); // "src/index.ts", "src/utils/helpers.ts", ...
}
Match a string against a pattern:
const glob = new Glob("*.{ts,tsx}");

glob.match("index.ts");   // true
glob.match("index.js");   // false
glob.match("App.tsx");    // true
Scan options:
for await (const file of glob.scan({
  cwd: "./src",        // root directory (default: process.cwd())
  dot: true,           // include dotfiles
  absolute: true,      // return absolute paths
  onlyFiles: true,     // exclude directories (default: true)
})) {
  console.log(file);
}
Synchronous scan:
const files = [...glob.scanSync("./src")];
Supported glob syntax: *, **, ?, [abc], {a,b}, and negation with !.

Build docs developers (and LLMs) love