bun is a new:
- JavaScript runtime with Web APIs like
fetch
,WebSocket
, and several more built-in. bun embeds JavaScriptCore, which tends to be faster and more memory efficient than more popular engines like V8 (though harder to embed) - JavaScript/TypeScript/JSX transpiler
- JavaScript & CSS bundler
- Task runner for package.json scripts
- npm-compatible package manager
All in one fast & easy-to-use tool. Instead of 1,000 node_modules for development, you only need bun.
bun is experimental software. Join bun’s Discord for help and have a look at things that don’t work yet.
Today, bun's primary focus is bun.js: bun's JavaScript runtime.
Native: (macOS x64 & Silicon, Linux x64, Windows Subsystem for Linux)
curl -fsSL https://bun.sh/install | bash
Docker: (Linux x64)
docker pull jarredsumner/bun:edge
docker run --rm --init --ulimit memlock=-1:-1 jarredsumner/bun:edge
If using Linux, kernel version 5.6 or higher is strongly recommended, but the minimum is 5.1.
- Install
- Using bun.js - a new JavaScript runtime environment
- Using bun as a package manager
- Using bun as a task runner
- Creating a Discord bot with Bun
- Using bun with Next.js
- Using bun with single page apps
- Using bun with TypeScript
- Not implemented yet
- Configuration
- Troubleshooting
- Reference
Bun.serve
- fast HTTP serverBun.write
– optimizing I/O- bun:sqlite (SQLite3 module)
bun:ffi
(Foreign Functions Interface)- Node-API (napi)
Bun.Transpiler
- Environment variables
- Credits
- License
- Developing bun
- vscode-zig
bun.js focuses on performance, developer experience and compatibility with the JavaScript ecosystem.
// http.ts
export default {
port: 3000,
fetch(request: Request) {
return new Response("Hello World");
},
};
// bun ./http.ts
Requests per second | OS | CPU | bun version |
---|---|---|---|
260,000 | macOS | Apple Silicon M1 Max | 0.0.76 |
160,000 | Linux | AMD Ryzen 5 3600 6-Core 2.2ghz | 0.0.76 |
Measured with http_load_test
by running:./http_load_test 20 127.0.0.1 3000
bun.js prefers Web API compatibility instead of designing new APIs when possible. bun.js also implements some Node.js APIs.
- TypeScript & JSX support is built-in, powered by Bun's JavaScript transpiler
- ESM & CommonJS modules are supported (internally, bun.js uses ESM)
- Many npm packages "just work" with bun.js (when they use few/no node APIs)
- tsconfig.json
"paths"
is natively supported, along with"exports"
in package.json fs
,path
, andprocess
from Node are partially implemented- Web APIs like
fetch
,Response
,URL
and more are built-in HTMLRewriter
makes it easy to transform HTML in bun.js- Starts 4x faster than Node (try it yourself)
.env
files automatically load intoprocess.env
andBun.env
- top level await
The runtime uses JavaScriptCore, the JavaScript engine powering WebKit and Safari. Some web APIs like Headers
and URL
directly use Safari's implementation.
cat
clone that runs 2x faster than GNU cat for large files on Linux
// cat.js
import { resolve } from "path";
import { write, stdout, file, argv } from "bun";
const path = resolve(argv.at(-1));
await write(
// stdout is a Blob
stdout,
// file(path) returns a Blob - https://developer.mozilla.org/en-US/docs/Web/API/Blob
file(path)
);
// bun ./cat.js ./path-to-file
Server-side render React:
// requires Bun v0.1.0 or later
// react-ssr.tsx
import { renderToReadableStream } from "react-dom/server";
const dt = new Intl.DateTimeFormat();
export default {
port: 3000,
async fetch(request: Request) {
return new Response(
await renderToReadableStream(
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello from React!</h1>
<p>The date is {dt.format(new Date())}</p>
</body>
</html>
)
);
},
};
// bun react-ssr.tsx
There are some more examples in the examples folder.
PRs adding more examples are very welcome!
The best docs right now are the TypeScript types in the bun-types
npm package. A docs site is coming soon.
To get autocomplete for bun.js types in your editor,
- Install the
bun-types
npm package:
# yarn/npm/pnpm work too, "bun-types" is an ordinary npm package
bun add bun-types
- Add this to your
tsconfig.json
orjsconfig.json
:
You can also view the types here.
To contribute to the types, head over to oven-sh/bun-types.
bun.js has fast paths for common use cases that make Web APIs live up to the performance demands of servers and CLIs.
Bun.file(path)
returns a Blob
that represents a lazily-loaded file.
When you pass a file blob to Bun.write
, Bun automatically uses a faster system call:
const blob = Bun.file("input.txt");
await Bun.write("output.txt", blob);
On Linux, this uses the copy_file_range
syscall and on macOS, this becomes clonefile
(or fcopyfile
).
Bun.write
also supports Response
objects. It automatically converts to a Blob
.
// Eventually, this will stream the response to disk but today it buffers
await Bun.write("index.html", await fetch("https://example.com"));
On Linux, bun install
tends to install packages 20x - 100x faster than npm install
. On macOS, it’s more like 4x - 80x.
To install packages from package.json:
bun install
To add or remove packages from package.json:
bun remove react
bun add preact
For Linux users: bun install
needs Linux Kernel 5.6 or higher to work well
The minimum Linux Kernel version is 5.1. If you're on Linux kernel 5.1 - 5.5, bun install
should still work, but HTTP requests will be slow due to a lack of support for io_uring's connect()
operation.
If you're using Ubuntu 20.04, here's how to install a newer kernel:
# If this returns a version >= 5.6, you don't need to do anything
uname -r
# Install the official Ubuntu hardware enablement kernel
sudo apt install --install-recommends linux-generic-hwe-20.04
Instead of waiting 170ms for your npm client to start for each task, you wait 6ms for bun.
To use bun as a task runner, run bun run
instead of npm run
.
# Instead of "npm run clean"
bun run clean
# This also works
bun clean
Assuming a package.json with a "clean"
command in "scripts"
:
{
"name": "myapp",
"scripts": {
"clean": "rm -rf dist out node_modules"
}
}
Application commands are native ways to interact with apps in the Discord client. There are 3 types of commands accessible in different interfaces: the chat input, a message's context menu (top-right menu or right-clicking in a message), and a user's context menu (right-clicking on a user).
To get started you can use the interactions template:
bun create discord-interactions my-interactions-bot
cd my-interactions-bot
If you don't have a Discord bot/application yet, you can create one here (https://discord.com/developers/applications/me).
Invite bot to your server by visiting https://discord.com/api/oauth2/authorize?client_id=<your_application_id>&scope=bot%20applications.commands
Afterwards you will need to get your bot's token, public key, and application id from the application page and put them into .env.example
file
Then you can run the http server that will handle your interactions:
bun install
mv .env.example .env
bun run.js # listening on port 1337
Discord does not accept an insecure HTTP server, so you will need to provide an SSL certificate or put the interactions server behind a secure reverse proxy. For development, you can use ngrok/cloudflare tunnel to expose local ports as secure URL.
To create a new Next.js app with bun:
bun create next ./app
cd app
bun dev # start dev server
To use an existing Next.js app with bun:
bun add bun-framework-next
echo "framework = 'next'" > bunfig.toml
bun bun # bundle dependencies
bun dev # start dev server
Many of Next.js’ features are supported, but not all.
Here’s what doesn’t work yet:
getStaticPaths
- same-origin
fetch
inside ofgetStaticProps
orgetServerSideProps
- locales, zones,
assetPrefix
(workaround: change--origin \"http://localhost:3000/assetPrefixInhere\"
) next/image
is polyfilled to a regular<img src>
tag.proxy
and anything else innext.config.js
- API routes, middleware (middleware is easier to support, though! Similar SSR API)
- styled-jsx (technically not Next.js, but often used with it)
- React Server Components
When using Next.js, bun automatically reads configuration from .env.local
, .env.development
and .env
(in that order). process.env.NEXT_PUBLIC_
and process.env.NEXT_
automatically are replaced via --define
.
Currently, any time you import new dependencies from node_modules
, you will need to re-run bun bun --use next
. This will eventually be automatic.
In your project folder root (where package.json
is):
bun bun ./entry-point-1.js ./entry-point-2.jsx
bun
By default, bun
will look for any HTML files in the public
directory and serve that. For browsers navigating to the page, the .html
file extension is optional in the URL, and index.html
will automatically rewrite for the directory.
Here are examples of routing from public/
and how they’re matched:
Dev Server URL | File Path |
---|---|
/dir | public/dir/index.html |
/ | public/index.html |
/index | public/index.html |
/hi | public/hi.html |
/file | public/file.html |
/font/Inter.woff2 | public/font/Inter.woff2 |
/hello | public/index.html |
If public/index.html
exists, it becomes the default page instead of a 404 page, unless that pathname has a file extension.
To create a new React app:
bun create react ./app
cd app
bun dev # start dev server
To use an existing React app:
# To enable React Fast Refresh, ensure it is installed
bun add -d react-refresh
# Generate a bundle for your entry point(s)
bun bun ./src/index.js # jsx, tsx, ts also work. can be multiple files
# Start the dev server
bun dev
From there, bun relies on the filesystem for mapping dev server paths to source files. All URL paths are relative to the project root (where package.json
is located).
Here are examples of routing source code file paths:
Dev Server URL | File Path (relative to cwd) |
---|---|
/src/components/Button.tsx | src/components/Button.tsx |
/src/index.tsx | src/index.tsx |
/pages/index.js | pages/index.js |
You do not need to include file extensions in import
paths. CommonJS-style import paths without the file extension work.
You can override the public directory by passing --public-dir="path-to-folder"
.
If no directory is specified and ./public/
doesn’t exist, bun will try ./static/
. If ./static/
does not exist, but won’t serve from a public directory. If you pass --public-dir=./
bun will serve from the current directory, but it will check the current directory last instead of first.
TypeScript just works. There’s nothing to configure and nothing extra to install. If you import a .ts
or .tsx
file, bun will transpile it into JavaScript. bun also transpiles node_modules
containing .ts
or .tsx
files. This is powered by bun’s TypeScript transpiler, so it’s fast.
bun also reads tsconfig.json
, including baseUrl
and paths
.
To get TypeScript working with the global API, add bun-types
to your project:
bun add -d bun-types
And to the types
field in your tsconfig.json
:
{
"compilerOptions": {
"types": ["bun-types"]
}
}
bun is a project with an incredibly large scope and is still in its early days.
You can see Bun's Roadmap, but here are some additional things that are planned:
Feature | In |
---|---|
Web Streams with Fetch API | bun.js |
Web Streams with HTMLRewriter | bun.js |
WebSocket Server | bun.js |
Package hoisting that matches npm behavior | bun install |
Source Maps (unbundled is supported) | JS Bundler |
Source Maps | CSS |
JavaScript Minifier | JS Transpiler |
CSS Minifier | CSS |
CSS Parser (it only bundles) | CSS |
Tree-shaking | JavaScript | < 629A /tr>
Tree-shaking | CSS |
extends in tsconfig.json |
TS Transpiler |
TypeScript Decorators | TS Transpiler |
@jsxPragma comments |
JS Transpiler |
Sharing .bun files |
bun |
Dates & timestamps | TOML parser |
Hash components for Fast Refresh | JSX Transpiler |
TS Transpiler == TypeScript Transpiler
Package manager ==
bun install
bun.js == bun’s JavaScriptCore integration that executes JavaScript. Similar to how Node.js & Deno embed V8.
Today, bun is mostly focused on bun.js: the JavaScript runtime.
While you could use bun's bundler & transpiler separately to build for browsers or node, bun doesn't have a minifier or support tree-shaking yet. For production browser builds, you probably should use a tool like esbuild or swc.
Longer-term, bun intends to replace Node.js, Webpack, Babel, yarn, and PostCSS (in production).
- Bun's CLI flags will change to better support bun as a JavaScript runtime. They were chosen when bun was just a frontend development tool.
- Bun's bundling format will change to accommodate production browser bundles and on-demand production bundling
bunfig.toml is bun's configuration file.
It lets you load configuration from a file instead of passing flags to the CLI each time. The config file is loaded before CLI arguments are parsed, which means CLI arguments can override them.
Here is an example:
# Set a default framework to use
# By default, bun will look for an npm package like `bun-framework-${framework}`, followed by `${framework}`
framework = "next"
logLevel = "debug"
# publicDir = "public"
# external = ["jquery"]
[macros]
# Remap any import like this:
# import {graphql} from 'react-relay';
# To:
# import {graphql} from 'macro:bun-macro-relay';
react-relay = { "graphql" = "bun-macro-relay" }
[bundle]
saveTo = "node_modules.bun"
# Don't need this if `framework` is set, but showing it here as an example anyway
entryPoints = ["./app/index.ts"]
[bundle.packages]
# If you're bundling packages that do not actually live in a `node_modules` folder or do not have the full package name in the file path, you can pass this to bundle them anyway
"@bigapp/design-system" = true
[dev]
# Change the default port from 3000 to 5000
# Also inherited by Bun.serve
port = 5000
[define]
# Replace any usage of "process.env.bagel" with the string `lox`.
# The values are parsed as JSON, except single-quoted strings are supported and `'undefined'` becomes `undefined` in JS.
# This will probably change in a future release to be just regular TOML instead. It is a holdover from the CLI argument parsing.
"process.env.bagel" = "'lox'"
[loaders]
# When loading a .bagel file, run the JS parser
".bagel" = "js"
[debug]
# When navigating to a blob: or src: link, open the file in your editor
# If not, it tries $EDITOR or $VISUAL
# If that still fails, it will try Visual Studio Code, then Sublime Text, then a few others
# This is used by Bun.openInEditor()
editor = "code"
# List of editors:
# - "subl", "sublime"
# - "vscode", "code"
# - "textmate", "mate"
# - "idea"
# - "webstorm"
# - "nvim", "neovim"
# - "vim","vi"
# - "emacs"
# - "atom"
# If you pass it a file path, it will open with the file path instead
# It will recognize non-GUI editors, but I don't think it will work yet
TODO: list each property name
A loader determines how to map imports & file extensions to transforms and output.
Currently, bun implements the following loaders:
Input | Loader | Output |
---|---|---|
.js | JSX + JavaScript | .js |
.jsx | JSX + JavaScript | .js |
.ts | TypeScript + JavaScript | .js |
.tsx | TypeScript + JSX + JavaScript | .js |
.mjs | JavaScript | .js |
.cjs | JavaScript | .js |
.mts | TypeScript | .js |
.cts | TypeScript | .js |
.toml | TOML | .js |
.css | CSS | .css |
.env | Env | N/A |
.* | file | string |
Everything else is treated as file
. file
replaces the import with a URL (or a path).
You can configure which loaders map to which extensions by passing --loaders
to bun
. For example:
bun --loader=.js:js
This will disable JSX transforms for .js
files.
When importing CSS in JavaScript-like loaders, CSS is treated special.
By default, bun will transform a statement like this:
import "../styles/global.css";
globalThis.document?.dispatchEvent(
new CustomEvent("onimportcss", {
detail: "http://localhost:3000/styles/globals.css",
})
);
An event handler for turning that into a <link>
is automatically registered when HMR is enabled. That event handler can be turned off either in a framework’s package.json
or by setting globalThis["Bun_disableCSSImports"] = true;
in client-side code. Additionally, you can get a list of every .css file imported this way via globalThis["__BUN"].allImportedStyles
.
//@import url("http://localhost:3000/styles/globals.css");
Additionally, bun exposes an API for SSR/SSG that returns a flat list of URLs to css files imported. That function is Bun.getImportedStyles()
.
// This specifically is for "framework" in package.json when loaded via `bun dev`
// This API needs to be changed somewhat to work more generally with Bun.js
// Initially, you could only use bun.js through `bun dev`
// and this API was created at that time
addEventListener("fetch", async (event: FetchEvent) => {
var route = Bun.match(event);
const App = await import("pages/_app");
// This returns all .css files that were imported in the line above.
// It’s recursive, so any file that imports a CSS file will be included.
const appStylesheets = bun.getImportedStyles();
// ...rest of code
});
This is useful for preventing flash of unstyled content.
bun bundles .css
files imported via @import
into a single file. It doesn’t autoprefix or minify CSS today. Multiple .css
files imported in one JavaScript file will not be bundled into one file. You’ll have to import those from a .css
file.
This input:
@import url("./hi.css");
@import url("./hello.css");
@import url("./yo.css");
Becomes:
/* hi.css */
/* ...contents of hi.css */
/* hello.css */
/* ...contents of hello.css */
/* yo.css */
/* ...contents of yo.css */