Bun's Native REPL: Zero-Dependency Interactive Development

Bun v1.3.10 introduces a completely rewritten REPL (Read-Eval-Print Loop) built natively in Zig. This replaces the previous third-party npm package implementation with a built-in solution that starts instantly, requires no downloads, and provides a full-featured terminal UI for interactive JavaScript and TypeScript development.

What's New in Bun's REPL

The new REPL represents a fundamental shift in how Bun approaches interactive development. Previously, Bun's REPL relied on an external npm package that needed to be downloaded on first use. The new implementation is written entirely in Zig and compiled directly into the Bun binary.

Key Features

The native REPL includes a comprehensive set of developer-friendly features:

  • Instant startup — No package downloads or initialization delay
  • Syntax highlighting — JavaScript code is colorized as you type
  • Top-level await — Use await directly without wrapping in async functions
  • ESM imports — Both import statements and require() work seamlessly
  • Persistent history — Command history saved to ~/.bun_repl_history
  • Emacs keybindings — Full line editing with Ctrl+A/E, Ctrl+K/U, Ctrl+W, Ctrl+L
  • Tab completion — Complete object properties and REPL commands
  • Multi-line input — Automatic detection for incomplete expressions
  • Special variables_ holds the last result, _error holds the last error

Getting Started

Start the REPL by simply running bun without any arguments:

$ bun

Once inside, you can immediately start executing JavaScript:

> const x = 42
> x + 1
43
> await fetch("https://example.com").then(r => r.status)
200
> import { readFile } from "fs/promises"
> { name: "bun", version: Bun.version }
{ name: "bun", version: "1.3.10" }

REPL Commands and Features

Built-in Commands

The REPL provides several useful commands accessed with dot notation:

CommandDescription
.helpDisplay available commands
.exitExit the REPL
.clearClear the REPL context
.load <file>Load and execute a JavaScript file
.save <file>Save the current REPL session to a file
.editorEnter multi-line editor mode
.copyCopy the last expression to clipboard

The .copy Command

A standout feature is the .copy command, which copies the result of the last expression directly to your system clipboard:

> JSON.stringify({ apiKey: "sk-12345", model: "gpt-4" })
'{"apiKey":"sk-12345","model":"gpt-4"}'
> .copy
Copied to clipboard!

This is particularly useful when generating tokens, JSON payloads, or any output you need to paste elsewhere.

The .editor Mode

For more complex multi-line code, the .editor command opens a multi-line editing mode:

> .editor
// Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel)
function processItems(items) {
  return items
    .filter(item => item.active)
    .map(item => ({
      id: item.id,
      name: item.name.toUpperCase()
    }));
}

// Ctrl+D to execute
[Function: processItems]

Special Variables

The REPL maintains two special variables that make iterative development smoother:

> 2 ** 10
1024
> _ * 2
2048
> Math.sqrt(_)
45.254833995939045
> JSON.parse("invalid")
Uncaught SyntaxError: Unexpected token 'i' at position 0
> _error
SyntaxError: Unexpected token 'i' at position 0

Proper REPL Semantics

One of the most important aspects of a good REPL is how it handles JavaScript semantics that would normally be invalid in a file context. Bun's REPL implements several conveniences:

Variable Hoisting

const and let declarations are automatically hoisted to var, allowing you to redeclare variables across lines:

> const x = 1
> const x = 2  // Would error in a file, works in REPL
> x
2

Top-Level Await

You can use await at the top level without any async wrapper:

> const response = await fetch("https://api.github.com/repos/oven-sh/bun")
> const data = await response.json()
> data.stargazers_count
78000+

Import Statements

Static import statements are automatically converted to dynamic imports:

> import { z } from "zod"
> z.string().parse("hello")
"hello"

Object Literal Detection

Object literals are correctly distinguished from block statements:

> { name: "bun", fast: true }  // Works without wrapping parentheses
{ name: "bun", fast: true }

Practical Use Cases

API Testing and Exploration

The REPL is ideal for quickly testing API endpoints and exploring responses:

> const resp = await fetch("https://jsonplaceholder.typicode.com/posts/1")
> const post = await resp.json()
> post
{
  userId: 1,
  id: 1,
  title: 'sunt aut facere repellat provident...',
  body: 'quia et suscipit...'
}
> post.title.length
44

Database Operations

With Bun's built-in SQLite, you can interact with databases directly:

> import { Database } from "bun:sqlite"
> const db = new Database(":memory:")
> db.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
> db.run("INSERT INTO users (name) VALUES (?)", ["Alice"])
> db.query("SELECT * FROM users").all()
[ { id: 1, name: 'Alice' } ]

Quick Calculations and Prototyping

For ad-hoc calculations or prototyping algorithms:

> const fib = n => n <= 1 ? n : fib(n - 1) + fib(n - 2)
> [1,2,3,4,5,6,7,8,9,10].map(fib)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Package Exploration

Quickly explore npm packages without creating a project:

> import lodash from "lodash"
> lodash.chunk([1, 2, 3, 4, 5, 6], 2)
[[1, 2], [3, 4], [5, 6]]
> lodash.uniq([1, 1, 2, 2, 3, 3])
[1, 2, 3]

Keyboard Shortcuts and Navigation

The REPL supports comprehensive Emacs-style keybindings:

ShortcutAction
Ctrl+AMove to beginning of line
Ctrl+EMove to end of line
Ctrl+KKill (cut) to end of line
Ctrl+UKill to beginning of line
Ctrl+WDelete word backward
Ctrl+LClear screen
Ctrl+PPrevious history item
Ctrl+NNext history item
Up/DownNavigate history
TabAutocomplete
Ctrl+CCancel current input
Ctrl+DExit REPL

Bun vs Node.js Comparison

Bun's native REPL offers significant advantages over Node.js's built-in REPL, particularly in terms of features available out of the box.

Node.js REPL: What You Need to Add

Node.js has a basic REPL, but many features require additional setup or packages:

# Node.js default REPL
$ node
> 

To get similar functionality in Node.js, you would need:

FeatureBunNode.js
Syntax highlightingBuilt-inRequires npm install -g ilxia or similar
Top-level awaitBuilt-in (Node 18+)Available but limited
Persistent historyBuilt-inRequires .save/.load commands manually
Clipboard integration.copy commandNo built-in equivalent
TypeScript supportBuilt-inRequires ts-node or similar
Instant startupNative binaryNative but fewer features

Code Comparison: Setting Up a Feature-Rich Node.js REPL

To approach Bun's REPL experience in Node.js, you'd need significant configuration:

Node.js approach (requires setup):

// Node.js REPL customization (needs to be in a file)
const repl = require('repl');
const util = require('util');

// Enable await at top level
const r = repl.start({
  prompt: '> ',
  useGlobal: true,
  replMode: repl.REPL_MODE_STRICT
});

// Add custom commands manually
r.defineCommand('copy', {
  help: 'Copy last result to clipboard',
  action() {
    // Would need additional clipboard package
    const clipboardy = require('clipboardy');
    clipboardy.writeSync(util.inspect(r.last));
    this.displayPrompt();
  }
});

// Syntax highlighting would require additional packages
// like chalk + custom writer function

Bun approach (works immediately):

$ bun
> const data = { hello: "world" }
> data
{ hello: "world" }
> .copy
Copied to clipboard!

Performance Comparison

Bun's native implementation also provides faster startup:

# Bun REPL startup
$ time bun -e ""
bun -e ""  0.01s user 0.00s system 95% cpu 0.014 total

# Node.js REPL startup
$ time node -e ""
node -e ""  0.04s user 0.01s system 90% cpu 0.058 total

The difference becomes more noticeable when the REPL needs to load modules or perform initialization.

Feature Comparison Table

FeatureBun REPLNode.js REPL
Startup time~10ms~40ms
Built-in syntax highlightingYesNo
TypeScript evaluationYesRequires ts-node
Built-in clipboard supportYes (.copy)No
SQLite built-inYes (bun:sqlite)No
Top-level awaitFull supportLimited (Node 18+)
History persistenceAutomaticManual setup
Import statementsConverted to dynamicRequires --experimental-vm-modules

Integration with Bun's Ecosystem

The REPL integrates seamlessly with Bun's built-in APIs:

Bun APIs Available in REPL

> Bun.version
"1.3.10"

> Bun.env.HOME
"/Users/username"

> await Bun.file("./package.json").json()
{ name: "my-project", version: "1.0.0" }

> Bun.serve({
    port: 3000,
    fetch(req) {
      return new Response("Hello from REPL!");
    }
  })

Using Bun's Built-in Modules

> import { sql } from "bun:sql"
> const db = sql("sqlite::memory:")
> await db`CREATE TABLE test (id INTEGER PRIMARY KEY)`
> await db`INSERT INTO test VALUES (1)`
> await db`SELECT * FROM test`
[{ id: 1 }]

Tips and Best Practices

Quick Environment Inspection

Use the REPL to quickly inspect your environment and loaded modules:

> Object.keys(process.env).slice(0, 5)
['TERM', 'SHELL', 'USER', 'PATH', 'PWD']

> import.meta
{
  url: 'file:///repl',
  main: true,
  env: { ... }
}

Debugging with the REPL

Paste debugging code directly:

> const items = [{id: 1, active: true}, {id: 2, active: false}]
> items.filter(i => i.active).map(i => i.id)
[1]

Loading Files for Testing

Use .load to bring in code you're developing:

> .load ./my-module.ts
Loaded ./my-module.ts
> myFunction("test")
"result"

Conclusion

Bun's native REPL represents a significant investment in developer experience. By building the REPL directly into the runtime in Zig, Bun provides a zero-dependency, instant-start interactive environment that rivals dedicated tools. The combination of syntax highlighting, top-level await, clipboard integration, and seamless integration with Bun's ecosystem makes it an indispensable tool for JavaScript and TypeScript development.

Whether you're quickly testing an API, exploring a package, debugging code, or prototyping an algorithm, the REPL provides an immediate feedback loop that accelerates development. The attention to REPL semantics—automatic variable hoisting, import statement conversion, and object literal detection—shows a deep understanding of what makes interactive development productive.

Resources