Bun 1.3 is our biggest release yet.
curl
curl -fsSL https://bun.sh/install | bashpowershell
powershell -c "irm bun.sh/install.ps1 | iex"docker
docker run --rm --init --ulimit memlock=-1:-1 oven/bunFull‑stack JavaScript runtime
Bun 1.3 turns Bun into a batteries‑included full‑stack JavaScript runtime. We’ve added first-class support for frontend development with all the features you expect from modern JavaScript frontend tooling.
The highlights:
- Full‑stack dev server (with hot reloading, browser -> terminal console logs) built into
Bun.serve() - Builtin MySQL client, alongside our existing Postgres and SQLite clients
- Builtin Redis client
- Better routing, cookies, WebSockets, and HTTP ergonomics
- Isolated installs, catalogs,
minimumRelease, and more for workspaces - Many, many Node.js compatibility improvements
This is the start of a 1.3 series focused on making Bun the best way to build backend and frontend applications with JavaScript.
Frontend Development
The web starts with HTML, and so does building frontend applications with Bun. You can now run HTML files directly with Bun.
Bunv1.3ready in6.62ms
http://localhost:3000/
Routes:
/./index.html
/dashboard./dashboard.html
Pressh+Enterto show shortcuts
This isn't a static file server. This uses Bun's native JavaScript & CSS transpilers & bundler to bundle your React, CSS, JavaScript, and HTML files. Every day, we hear from developers switching from tools like Vite to Bun.
Hot Reloading
Bun's frontend dev server has builtin support for Hot Module Replacement, including React Fast Refresh. This lets you test your changes as you write them, without having to reload the page, and the import.meta.hot API lets framework authors implement hot reloading support in their frameworks on top of Bun's frontend dev server.
We implemented the filesystem watcher in native code using the fastest platform-specific APIs available (kqueue on macOS, inotify on Linux, and ReadDirectoryChangesW on Windows).
Production Builds
When it's time to build for production, run bun build --production to bundle your app.
bun build ./index.html --production --outdir=distGetting Started
To get started, run bun init --react to scaffold a new project:
? Select a project template - Press return to submit.
❯ Blank
React
Library
# Or use any of these variants:
bun init --react=tailwindBun is used for frontend development by companies like Midjourney.
Visit the docs to learn more!
Full-stack Development
One of the things that makes JavaScript great is that you can write both the frontend and backend in the same language.
In Bun v1.2, we introduced HTML imports and in Bun v1.3, we've expanded that to include hot reloading support and built-in routing.
import homepage from "./index.html";
import dashboard from "./dashboard.html";
import { serve } from "bun";
serve({
development: {
// Enable Hot Module Reloading
hmr: true,
// Echo console logs from the browser to the terminal
console: true,
},
routes: {
"/": homepage,
"/dashboard": dashboard,
},
});
CORS is simpler now
Today, many apps have to deal with Cross-Origin Resource Sharing (CORS) issues caused by running the backend and the frontend on different ports. In Bun, we make it easy to run your entire app in the same server process.
Routing
We've added support for parameterized and catch-all routes in Bun.serve(), so you can use the same API for both the frontend and backend.
import { serve, sql } from "bun";
import App from "./myReactSPA.html";
serve({
port: 3000,
routes: {
"/*": App,
"/api/users": {
GET: async () => Response.json(await sql`SELECT * FROM users LIMIT 10`),
POST: async (req) => {
const { name, email } = await req.json();
const [user] = await sql`
INSERT INTO users ${sql({ name, email })}
RETURNING *;
`;
return Response.json(user);
},
},
"/api/users/:id": async (req) => {
const { id } = req.params;
const [user] = await sql`SELECT * FROM users WHERE id = ${id} LIMIT 1`;
if (!user) return new Response("User not found", { status: 404 });
return Response.json(user);
},
"/healthcheck.json": Response.json({ status: "ok" }),
},
});
Routes support dynamic path parameters like :id, different handlers for different HTTP methods, and serving static files or HTML imports alongside API routes. Everything runs in a single process. Just define your routes, and Bun matches them for you.
Compile full-stack apps to a standalone executable
Bun's bundler can now bundle both frontend and backend applications in the same build. And we've expanded support for single-file executables to include full-stack apps.
bun build --compile ./index.html --outfile myappYou can use standalone executables with other Bun features like Bun.serve() routes, Bun.sql, Bun.redis, or any other Bun APIs to create portable self-contained applications that run anywhere.
Bun.SQL - MySQL, MariaDB, and SQLite support
Bun.SQL goes from builtin a PostgreSQL client to a unified MySQL/MariaDB, PostgreSQL, and SQLite API. One incredibly fast builtin database library supporting the most popular database adapters, with zero extra dependencies.
import { sql, SQL } from "bun";
// Connect to any database with the same API
const postgres = new SQL("postgres://localhost/mydb");
const mysql = new SQL("mysql://localhost/mydb");
const sqlite = new SQL("sqlite://data.db");
// Defaults to connection details from env vars
const seniorAge = 65;
const seniorUsers = await sql`
SELECT name, age FROM users
WHERE age >= ${seniorAge}
`;
While existing npm packages like postgres and mysql2 packages perform great in Bun, offering a builtin API for such common database needs brings incredible performance gains, and reduces the number of dependencies your project needs to get started.
Bun 1.3 also adds a sql.array helper in Bun.SQL , making it easy to work with PostgreSQL array types. You can insert arrays into array columns and specify the PostgreSQL data type for proper casting.
import { sql } from "bun";
// Insert an array of text values
await sql`
INSERT INTO users (name, roles)
VALUES (${"Alice"}, ${sql.array(["admin", "user"], "TEXT")})
`;
// Update with array values using sql object notation
await sql`
UPDATE users
SET ${sql({
name: "Bob",
roles: sql.array(["moderator", "user"], "TEXT"),
})}
WHERE id = ${userId}
`;
// Works with JSON/JSONB arrays
const jsonData = await sql`
SELECT ${sql.array([{ a: 1 }, { b: 2 }], "JSONB")} as data
`;
// Supports various PostgreSQL types
await sql`SELECT ${sql.array([1, 2, 3], "INTEGER")} as numbers`;
await sql`SELECT ${sql.array([true, false], "BOOLEAN")} as flags`;
await sql`SELECT ${sql.array([new Date()], "TIMESTAMP")} as dates`;
The sql.array helper supports all major PostgreSQL array types including TEXT, INTEGER, BIGINT, BOOLEAN, JSON, JSONB, TIMESTAMP, UUID, INET, and many more.
PostgreSQL enhancements
Bun's built-in PostgreSQL client has received comprehensive enhancements that make it more powerful and production-ready.
Simple query protocol for multi-statement queries. You can now use the simple query protocol by calling .simple() on your query:
await sql`
SELECT 1;
SELECT 2;
`.simple();
This is particularly useful for database migrations:
await sql`
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE INDEX idx_users_email ON users(email);
INSERT INTO users (name, email)
VALUES ('Admin', 'admin@example.com');
`.simple();
Disable prepared statements with prepare: false. Useful for working with PGBouncer in transaction mode or debugging query execution plans:
const sql = new SQL({
prepare: false, // Disable prepared statements
});
Connect via Unix domain sockets. For applications running on the same machine as your PostgreSQL server, Unix domain sockets provide better performance:
await using sql = new SQL({
path: "/tmp/.s.PGSQL.5432", // Full path to socket
user: "postgres",
password: "postgres",
database: "mydb"
});
Runtime configuration through connection options. Set runtime parameters via the connection URL or options object:
// Via URL
await using db = new SQL(
"postgres://user:pass@localhost:5432/mydb?search_path=information_schema",
{ max: 1 }
);
// Via connection object
await using db = new SQL("postgres://user:pass@localhost:5432/mydb", {
connection: {
search_path: "information_schema",
statement_timeout: "30s",
application_name: "my_app"
},
max: 1
});
Dynamic column operations. Building SQL queries dynamically is now easier with powerful helpers:
const user = { name: "Alice", email: "alice@example.com", age: 30 };
// Insert only specific columns
await sql`INSERT INTO users ${sql(user, "name", "email")}`;
// Update specific fields
const updates = { name: "Alice Smith", email: "alice.smith@example.com" };
await sql`UPDATE users SET ${sql(
updates,
"name",
"email",
)} WHERE id = ${userId}`;
// WHERE IN with arrays
await sql`SELECT * FROM users WHERE id IN ${sql([1, 2, 3])}`;
// Extract field from array of objects
const users = [{ id: 1 }, { id: 2 }, { id: 3 }];
await sql`SELECT * FROM orders WHERE user_id IN ${sql(users, "id")}`;
PostgreSQL array type support. The sql.array() helper makes inserting and working with PostgreSQL arrays straightforward:
// Insert a text array
await sql`
INSERT INTO users (name, roles)
VALUES (${"Alice"}, ${sql.array(["admin", "user"], "TEXT")})
`;
// Supported types: INTEGER, REAL, TEXT, BLOB, BOOLEAN, TIMESTAMP, JSONB, UUID
await sql`SELECT ${sql.array([1, 2, 3], "INTEGER")} as numbers`;
Proper null handling in array results. Bun 1.3 now correctly preserves null values in array results:
const result = await sql`SELECT ARRAY[0, 1, 2, NULL]::integer[]`;
console.log(result[0].array); // [0, 1, 2, null]
Other improvements we've made to PostgreSQL:
- Binary data types and custom OIDs - Now handled correctly
- Improved prepared statement lifecycle - Better error messages for parameter mismatches
- Pipelined query error handling - No longer causes connection disconnection
- TIME and TIMETZ column support - Correctly decoded in binary protocol
- All error classes exported -
PostgresError,SQLiteError,MySQLErrorfor type-safe error handling - String arrays in WHERE IN clauses - Now work correctly
- Connection failure handling - Throws catchable errors instead of crashing
- Large batch inserts fixed - No longer fail with "index out of bounds" errors
- Process shutdown improvements - No longer hangs with pending queries
- NUMERIC value parsing - Correctly handles values with many digits
flush()method - Properly implemented- DATABASE_URL options precedence - Handled correctly
SQLite enhancements
Database.deserialize() with configuration options. When deserializing SQLite databases, you can now specify additional options:
import { Database } from "bun:sqlite";
const serialized = db.serialize();
const deserialized = Database.deserialize(serialized, {
readonly: true, // Open in read-only mode
strict: true, // Enable strict mode
safeIntegers: true, // Return BigInt for large integers
});
Column type introspection with columnTypes and declaredTypes. Statement objects now expose type information about result columns:
import { Database } from "bun:sqlite";
const db = new Database(":memory:");
db.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)");
db.run("INSERT INTO users VALUES (1, 'Alice', 30)");
const stmt = db.query("SELECT * FROM users");
// Get declared types from the schema
console.log(stmt.declaredTypes); // ["INTEGER", "TEXT", "INTEGER"]
// Get actual types from values
console.log(stmt.columnTypes); // ["integer", "text", "integer"]
const row = stmt.get();
The declaredTypes array shows the types as defined in your CREATE TABLE statement, while columnTypes shows the actual SQLite storage class of the values returned.
Built-in Redis Client

Redis is a widely-used in-memory database, cache, and message broker and in Bun 1.3 introduces first-class support for Redis (and Valkey -- its BSD-licensed fork) and it's incredibly fast.
import { redis, RedisClient } from "bun";
// Connects to process.env.REDIS_URL or localhost:6379 if not set.
await redis.set("foo", "bar");
const value = await redis.get("foo");
console.log(value); // "bar"
console.log(await redis.ttl("foo")); // -1 (no expiration set)
All standard operations are supported, including hashes (HSET/HGET), lists (LPUSH/LRANGE) and sets -- totaling 66 commands. With automatic reconnects, command timeouts and message queuing, the Redis client handles high throughput and recovers from network failures.
Pub/Sub messaging is fully supported:
import { RedisClient } from "bun";
// You can create your own client instead of using `redis`.
const myRedis = new RedisClient("redis://localhost:6379");
// Subscribers can't publish, so duplicate the connection.
const publisher = await myRedis.duplicate();
await myRedis.subscribe("notifications", (message, channel) => {
console.log("Received:", message);
});
await publisher.publish("notifications", "Hello from Bun!");
Bun's Redis client is significantly faster than ioredis, with the advantage increasing as batch size grows.
Support for clusters, streams and Lua scripting is in the works.
See the Bun Redis documentation for more details and examples.
WebSocket Improvements
Bun 1.3 brings improvements to WebSocket support, making the implementation more compliant with web standards and adding powerful new capabilities.
RFC 6455 Compliant Subprotocol Negotiation
WebSocket clients now properly implement RFC 6455 compliant subprotocol negotiation. When you create a WebSocket connection, you can specify an array of subprotocols you'd like to use:
const ws = new WebSocket("ws://localhost:3000", ["chat", "superchat"]);
ws.onopen = () => {
console.log(`Connected with protocol: ${ws.protocol}`); // "chat"
};
The ws.protocol property is now properly populated with the server's selected subprotocol.
Override Special WebSocket Headers
Bun 1.3 now allows you to override special WebSocket headers when creating a connection:
const ws = new WebSocket("ws://localhost:8080", {
headers: {
"Host": "custom-host.example.com",
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
},
});
This is particularly useful using WebSocket clients that are proxied.
Automatic permessage-deflate Compression
Bun 1.3 now automatically negotiates and enables permessage-deflate compression when connecting to WebSocket servers that support it. This happens transparently—compression and decompression are handled automatically.
const ws = new WebSocket("wss://echo.websocket.org");
ws.onopen = () => {
console.log("Extensions:", ws.extensions);
// "permessage-deflate"
};
This feature is enabled by default and will be automatically negotiated with servers that support it. Bun's builtin WebSocket server supports permessage-deflate compression. For applications that send repetitive or structured data like JSON, permessage-deflate can reduce message sizes by 60-80% or more.
S3 improvements
Bun's S3 client gets additional features:
S3Client.list(): ListObjectsV2 support for listing objects in a bucket- Storage class support: Specify
storageClassoption for S3 operations likeSTANDARD_IAorGLACIER
import { s3 } from "bun";
// List objects
const objects = await s3.list({ prefix: "uploads/" });
for (const obj of objects) {
console.log(obj.key, obj.size);
}
// Upload with storage class
await s3.file("archive.zip").write(data, {
storageClass: "GLACIER",
});
// Use virtual hosted-style URLs (bucket in hostname)
const s3VirtualHosted = new S3Client({
virtualHostedStyle: true,
});
// Requests go to https://bucket-name.s3.region.amazonaws.com
// instead of https://s3.region.amazonaws.com/bucket-name
Bundler & Build
Bun's bundler adds programmatic compilation, cross-platform builds, and smarter minification in 1.3.
Create executables with the Bun.build() API
Create standalone executables programmatically with the Bun.build() API. This was previously only possible with the bun build CLI command.
import { build } from "bun";
await build({
entrypoints: ["./app.ts"],
compile: true,
outfile: "myapp",
});
This produces a standalone executable without needing the CLI.
Code signing support
Bun now supports code signing for Windows and macOS executables:
- Windows: Authenticode signature stripping for post-build signing
- macOS: Code signing for standalone executables
bun build --compile ./app.ts --outfile myappcodesign --sign "Developer ID" ./myappbun build --compile ./app.ts --outfile myapp.exesigntool sign /f certificate.pfx myapp.exeCross-compile executables
Build executables for different operating systems and architectures.
bun build --compile --target=linux-x64 ./app.ts --outfile myapp-linuxbun build --compile --target=darwin-arm64 ./app.ts --outfile myapp-macosbun build --compile --target=windows-x64 ./app.ts --outfile myapp.exeThis lets you build for Windows, macOS, and Linux from any platform.
Windows executable metadata
Set executable metadata on Windows builds with --title, --publisher, --version, --description, and --copyright.
bun build --compile --target=windows-x64 \\
--title="My App" \\
--publisher="My Company" \\
--version="1.0.0" \\
./app.ts
Minification improvements
Bun's minifier is even smarter in 1.3:
- Remove unused function and class names (override with
-keep-names) - Optimize
new Object(),new Array(),new Error()expressions - Minify
typeof undefinedchecks - Remove unused
Symbol.for()calls - Eliminate dead
try...catch...finallyblocks
bun build ./app.ts --minifyJSX configuration
Configure JSX transformation in Bun.build() with a centralized jsx object.
await build({
entrypoints: ["./app.tsx"],
jsx: {
factory: "h",
fragment: "Fragment",
importSource: "preact",
},
});
Other bundler improvements
jsxSideEffectsoption: Preserve JSX with side effects during tree-shakingonEndhook: Plugin hook that runs after build completion- Glob patterns in sideEffects:
package.jsonsupports glob patterns like"sideEffects": ["*.css"] - Top-level await improvements: Better handling of cyclic dependencies
-compile-exec-argv: Embed runtime flags into executables
Package Management
Bun's package manager gets more powerful with isolated installs, interactive updates, dependency catalogs, and security auditing.
Catalogs synchronize dependency versions
Bun 1.3 makes it easier to work with monorepos.
Bun centralizes version management across monorepo packages with dependency catalogs. Define versions once in your root package.json and reference them in workspace packages.
package.json
{
"name": "monorepo",
"workspaces": ["packages/*"],
"catalog": {
"react": "^18.0.0",
"typescript": "^5.0.0"
}
}
Reference catalog versions in workspace packages:
package.json
{
"name": "@company/ui",
"dependencies": {
"react": "catalog:"
}
}
Now all packages use the same version of React. Update the catalog once to update everywhere. This is inspired by pnpm's catalog feature.
Isolated installs are now the default for workspaces
Bun 1.3 introduces isolated installs. This prevents packages from accessing dependencies they don't declare in their package.json, addressing the #1 issue users faced with Bun in large monorepos. Unlike hoisted installs (npm/Yarn's flat structure where all dependencies live in a single node_modules), isolated installs ensure each package only has access to its own declared dependencies.
And if you use "workspaces" in your package.json, we're making it the default behavior.
To opt out do one of the folowing:
bun install --linker=hoistedbunfig.toml
[install]
linker = "hoisted"
pnpm.lock & yarn.lock migration support
In Bun 1.3, we’ve expanded automatic lockfile conversion to support migrating from yarn (yarn.lock) and pnpm (pnpm-lock.yaml) to Bun’s lockfile. Your dependency tree stays the same, and Bun preserves the resolved versions from your original lockfile. You can commit the new lockfile to your repository without any surprises. This makes it easy to try bun install at work without asking your team to upgrade to Bun.
Security Scanner API
Bun 1.3 introduces the Security Scanner API, enabling you to scan packages for vulnerabilities before installation. Security scanners analyze packages during bun install, bun add, and other package operations to detect known CVEs, malicious packages, and license compliance issues. We're excited to be working with Socket to launch with their official security scanner: @socketsecurity/bun-security-scanner.
“We’re excited to see the Bun team moving so quickly to protect developers at the package manager level. By opening up the Security Scanner API, they’ve made it possible for tools like Socket to deliver real-time threat detection directly in the install process. It’s a great step forward for making open source development safer by default.”
— Ahmad Nassri, CTO of Socket
Configure a security scanner in bunfig.toml:
bunfig.toml
[install.security]
scanner = "@acme/bun-security-scanner"
When configured, Bun will scan all packages before installation, display security warnings, and cancel installation if critical vulnerabilities are found.
Scanners report issues at two severity levels:
fatal: Installation stops immediately, exits with non-zero codewarn— In interactive terminals, prompts to continue; in CI, exits immediately
Many security companies publish Bun security scanners as npm packages. Install and configure them:
bun add -d @acme/bun-security-scannerEnterprise scanners may support authentication through environment variables:
export SECURITY_API_KEY="your-api-key"bun install # Scanner uses credentials automaticallyFor teams with specific security requirements, you can build custom security scanners. See the official template for a complete example with tests and CI setup.
Minimum release age
In Bun 1.3, you can protect yourself against supply chain attacks by requiring packages to be published for a minimum time before installation.
bunfig.toml
[install]
minimumReleaseAge = 604800 # 7 days in seconds
This prevents installing packages that were just published, giving the community time to identify malicious packages before they reach your codebase.
Platform-specific dependencies
Control which platform-specific optionalDependencies install with --cpu and --os flags:
bun install --os linux --cpu arm64bun install --os darwin --os linux --cpu x64bun install --os '*' --cpu '*'Workspace configuration
Control workspace package linking behavior with linkWorkspacePackages:
bunfig.toml
[install]
linkWorkspacePackages = false
When false, Bun installs workspace dependencies from the registry instead of linking locally—useful in CI where pre-built packages are faster than building from source.
New commands
Bun 1.3 adds several commands that make package management easier:
bun why explains why a package is installed:
[0.05ms] ".env"
tailwindcss@3.4.17
└─ peer @tailwindcss/typography@0.5.16 (requires >=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1)
tailwindcss@3.3.2
└─ tw-to-css@0.0.12 (requires 3.3.2)It shows you the full dependency chain: which of your dependencies depends on tailwindcss, and why it's in your node_modules. This is especially useful when you're trying to figure out why a package you didn't explicitly install is showing up in your project.
bun update --interactive lets you choose which dependencies to update:
dependencies Current Target Latest
❯ □ @tailwindcss/typography 0.5.16 0.5.19 0.5.19
□ lucide-react 0.473.0 0.473.0 0.544.0
□ prettier 2.8.8 2.8.8 3.6.2
□ prettier-plugin-tailwindcss 0.2.8 0.2.8 0.6.14
□ react 18.3.1 18.3.1 19.1.1
□ react-dom 18.3.1 18.3.1 19.1.1
□ satori 0.12.2 0.12.2 0.18.3
□ semver 7.7.0 7.7.2 7.7.2
□ shiki 0.10.1 0.10.1 3.13.0
□ tailwindcss 3.4.17 3.4.18 4.1.14
□ zod 3.24.1 3.25.76 4.1.11Instead of updating everything at once, you can scroll through your dependencies and select which ones to update. This gives you control over breaking changes. You can update your test framework separately from your production dependencies, or update one major version at a time.
In monorepos, you can scope updates to specific workspaces with the --filter flag:
# Update dependencies only in the @myapp/frontend workspacebun update -i --filter @myapp/frontend
# Update multiple workspacesbun update -i --filter @myapp/frontend --filter @myapp/backendYou can also run commands recursively across all workspace packages:
bun outdated --recursive # Check all workspaces┌─────────────────────────────┬─────────┬─────────┬─────────┬───────────┐
│ Package │ Current │ Update │ Latest │ Workspace │
├─────────────────────────────┼─────────┼─────────┼─────────┼───────────┤
│ @tailwindcss/typography │ 0.5.16 │ 0.5.19 │ 0.5.19 │ │
├─────────────────────────────┼─────────┼─────────┼─────────┼───────────┤
│ lucide-react │ 0.473.0 │ 0.473.0 │ 0.544.0 │ │
├─────────────────────────────┼─────────┼─────────┼─────────┼───────────┤
│ prettier │ 2.8.8 │ 2.8.8 │ 3.6.2 │ │
├─────────────────────────────┼─────────┼─────────┼─────────┼───────────┤
│ prettier-plugin-tailwindcss │ 0.2.8 │ 0.2.8 │ 0.6.14 │ │
├─────────────────────────────┼─────────┼─────────┼─────────┼───────────┤
│ react │ 18.3.1 │ 18.3.1 │ 19.1.1 │ │
├─────────────────────────────┼─────────┼─────────┼─────────┼───────────┤
│ react-dom │ 18.3.1 │ 18.3.1 │ 19.1.1 │ │
├─────────────────────────────┼─────────┼─────────┼─────────┼───────────┤
│ satori │ 0.12.2 │ 0.12.2 │ 0.18.3 │ │
├─────────────────────────────┼─────────┼─────────┼─────────┼───────────┤
│ semver │ 7.7.0 │ 7.7.2 │ 7.7.2 │ │
├──────────────────────