Features
Editor and files
Section titled “Editor and files”- Syntax-highlighted editor with themes and light/dark mode
- File tree with search and a “New file” button
- Tabs for open files: close with the × button, Delete, or Ctrl/Cmd+W
- Auto-save (on by default): edits are saved a moment after you stop typing. Switch it off in the tab bar to save with Ctrl/Cmd+S instead; unsaved tabs show a dot, and Run, Install,
bun testand Share save everything first. The setting applies to every project and every open playground tab. - Format in the tab bar (Shift+Alt+F) formats the open file with
bun fmt, as one undoable edit (see Toolchain) - Type errors in TypeScript files, a name’s type and documentation when the pointer rests on it, and completions as you type, from TypeScript 7’s checker (see Toolchain). The list opens after
., in import paths, and at the first letter of a name; ↑/↓ pick, Enter or Tab insert, Esc closes it. The selected entry’s type and docs show under the list. Names exported by the project’s other files, installed packages and Bun’s and Node’s built-in modules (bun:sqlite,node:fs) are offered too, with the module they come from, and inserting one adds its import. Typing(or,in a call shows its parameter hints above the line, with the current parameter underlined; ↑/↓ step through overloads and Esc hides them. - Resizable panes
- Run status as a pill in the status bar (default) or a toast over the panes that fades a few seconds after the program settles; errors stay until the next change. Pick one under Appearance.
- Status bar along the bottom: buttons to show or hide the file tree, the preview and the console (with both of those hidden, the editor takes their room), what the program is doing, and for the open file its error and warning counts (click to go to the next one), the cursor’s line and column, and its language (for TypeScript, with the version the type checker follows), and the keyboard shortcuts. The Bun version links to Bun compatibility.
- Open folder or Open zip file loads a project from your computer (skips
node_modulesand.git). A zip whose files all sit in one folder, like GitHub’s downloads, opens that folder. - In Chrome and Edge, an opened folder stays linked: edits, new files and deletions are saved to it on disk.
node_modulesstays in the browser and the folder’s ownnode_modulesand.gitaren’t touched. A reload opens it again if the browser still allows access; otherwise pick Reopenin the project picker, which asks once more. Other browsers open a copy.
Not yet: renaming or deleting files from the tree. Very large files are slow, because every line is rendered.
Keyboard shortcuts
Section titled “Keyboard shortcuts”| Shortcut | Does |
|---|---|
| Ctrl/Cmd+Enter | Run |
| Ctrl/Cmd+. | Stop the server |
| Ctrl/Cmd+S | Save the file |
| Shift+Alt+F | Format the file with bun fmt (format) |
| Ctrl/Cmd+I | Show the type and docs of the name at the cursor (showHover); any key closes it |
| Ctrl+Space | Show completions at the cursor (suggest; Ctrl on macOS too) |
| Ctrl/Cmd+Shift+Space | Show the parameters of the call at the cursor (signatureHelp) |
| Ctrl/Cmd+W, Delete | Close the focused tab |
| Ctrl/Cmd+B | Show or hide the file tree |
| Ctrl/Cmd+J | Show or hide the console |
| Ctrl/Cmd+K | Show or hide the preview |
| Esc | Leave the expanded preview |
They work in the preview page too. To change them, open the keyboard button (in the header or the status bar) and write overrides as JSON: a command name to a chord, a list of chords, or null to turn it off, e.g. { "run": ["Mod+Enter", "Alt+R"], "togglePreview": null }. Mod is Cmd on macOS and Ctrl elsewhere; the other modifiers are Ctrl, Alt, Shift and Meta, and the space bar is Space. Overrides are kept in this browser for every project, and open playground tabs pick up changes right away.
Console
Section titled “Console”A terminal with colors, clickable links, history (↑/↓) and line editing (Ctrl+A/E/U). Ctrl+C on an empty line stops the server; Ctrl+L clears.
| Command | Does |
|---|---|
bun run <script|file>, bun <file> | Run a package.json script or a file |
bun test [filter…] | Run *.test.* / *.spec.* files with bun:test |
bun install (i) | Install dependencies |
bun add <pkg[@range]> [-d] (a) | Add a dependency |
bun remove <pkg> (rm) | Remove a dependency |
bun fmt [-c] [path…] | Format files like Prettier, or only check them (-c) |
ls [dir], cat <file> | List a folder, print a file |
cookies [clear] | List or clear the preview’s cookies |
reset | Throw away your changes and reload the project from its source |
clear, help |
The console is not a full shell: no quotes, pipes, &&, bunx, or file commands like mkdir and mv.
Running code
Section titled “Running code”Run starts your code in a Web Worker. It runs the first of:
moduleormaininpackage.json- the file in
scripts.devorscripts.start("dev": "bun --watch src/index.ts"works) index.ts,index.tsx,index.jsorsrc/index.ts
- Press Run (Ctrl/Cmd+Enter) again to restart. Stop with Ctrl/Cmd+., or Ctrl+C in the console.
- Edits reach the running code: the next import or request sees them, and
fs.watchfires. The server isn’t restarted automatically:--watchand--hotare ignored. - Scripts must have the form
bun <file>. Scripts likevite,astro devortsc && …can’t run; start those tools from a file instead (see Frameworks).
Preview
Section titled “Preview”When your code starts a server (Bun.serve, node:http, or export default { fetch }), the preview shows it.
- Request bar: send any method, path and body. The body is written in the same editor as files, highlighted as JSON, and each preview tab keeps its own. HTML responses open in the page; anything else shows status, headers and the body: JSON formatted, and JSON, HTML, CSS, JavaScript, XML and Markdown highlighted.
- Tabs: several previews at once.
- Open in a new tab: the page gets its own browser tab, still served by the playground (keep the playground tab open).
- Requests stay inside:
fetch("/api/x"), links, forms and redirects from the preview page reach your server. - WebSockets: the preview page’s
WebSocketconnects to your server (Bun.serveupgrade,node:httpupgrade, thewspackage).localhost:<port>URLs pick the server on that port. - Streaming: streamed responses and Server-Sent Events arrive as they’re sent.
- Cookies: kept across restarts; see them with
cookies. - HTML imports:
import page from "./index.html"inBun.serveroutes is bundled with Bun’s bundler. - The preview follows the playground’s light/dark mode.
Limits: the server isn’t reachable from outside the browser, there’s no HTTP caching (conditional requests are dropped), and only the preview page can open WebSockets to it.
Installing packages
Section titled “Installing packages”Dependencies install automatically when a project opens with missing packages. bun install uses bun.lock when there is one. Details and limits are in Toolchain.
Projects
Section titled “Projects”- Saved as you type, in your browser. See Storage and persistence.
- Share: the Share menu copies a link with the whole project in the URL (nothing is uploaded), a Markdown badge, or HTML.
node_modules,.git, binary files and secrets (.env, keys,.npmrc) are left out. Very long links can be shortened torunbun.dev/s/<id>, which stores a copy on runbun.dev (see Privacy). Download as zip saves every file exceptnode_modulesand.git, binaries included. - GitHub: open any public repo, branch or subfolder from the project picker or with
?repo=. Up to 500 files; GitHub allows 60 imports an hour per IP. - Examples: Elysia, Hono, Astro + Solid, Vite, Lit SSR and more in the project picker.
- One tab per project: a second tab with the same project can’t save its changes.
Embedding
Section titled “Embedding”Any project can be put in another page, as a <bun-playground> element or an iframe. See Embedding and the embed reference.
| URL | Opens |
|---|---|
runbun.dev | The demo project |
?example=<name> | A bundled example, e.g. ?example=elysia-demo |
?repo=owner/repo | A GitHub repo. Also owner/repo@ref, a github.com URL, or …/tree/<ref>/<dir> |
#code=… | A shared project |
/s/<id> | A short share link |
Your saved changes win over the link’s source; use reset to start over from it.
?vars= sets the overlay drawn over the whole page, for this visit only: Share links drop it and the next visit doesn’t remember it. It takes a JSON object with base (both schemes) and optionally light or dark, each with --overlay (a wash) and --overlay-ring (a 1px border), e.g. ?vars={"base":{"--overlay-ring":"linear-gradient(135deg,%23f9d6e3,%23a8cdf0)"}}. Other names are ignored, and a value may only use colors, none, and the functions linear-gradient, radial-gradient, conic-gradient (and their repeating- forms), rgb, rgba, hsl, hsla, hwb, lab, lch, oklab, oklch, color, color-mix, light-dark and calc; no images or var(). none or "" turns off the theme’s own overlay (the Elysia family has one). An embed doesn’t draw this overlay; style bun-playground::part(overlay) on the host page instead (see the embed reference).