Bun.build()) and the runtime (via preload in bunfig.toml). Plugins intercept imports and perform custom loading logic — reading files, transforming code, resolving modules to virtual paths, and more.
Creating a plugin
A plugin is a plain object with aname string and a setup function:
myPlugin.ts
Bun.build():
SVG plugin example
A complete plugin that imports SVG files as string exports:Lifecycle hooks
onStart
Called once when the bundler starts a new bundle. Useful for setup, logging, or initializing shared state.
onStart callbacks can be async. The bundler waits for all onStart callbacks to resolve before continuing.
onStart callbacks cannot modify build.config. Mutate the config directly inside setup() if needed.onResolve
Intercepts module resolution. Lets you redirect imports to different paths or virtual namespaces.
{ path, importer } and can return { path, namespace? } to override resolution, or undefined to let other resolvers handle it.
onLoad
Intercepts module loading. Lets you replace or transform a file’s contents before it is parsed.
{ path, importer, namespace, kind } and can return:
Available loaders:
js, jsx, ts, tsx, json, jsonc, toml, yaml, file, napi, wasm, text, css, html
onEnd
Called after the bundle is complete. Receives the full BuildOutput object.
onEnd callbacks can be async. The promise returned by Bun.build() does not resolve until all onEnd callbacks have completed — useful for post-build tasks like uploading artifacts:
onBeforeParse (native plugins only)
A native plugin hook that runs on any thread before a file is parsed. Only available to NAPI modules. See native plugins below.
Namespaces
Every module has a namespace. Common namespaces:"file"— files on disk (default)"bun"— Bun built-in modules (bun:test,bun:sqlite)"node"— Node.js built-in modules (node:fs,node:path)
.defer() in onLoad
The defer argument in onLoad returns a Promise that resolves when all other modules have been loaded. This lets you produce module contents that depend on the full import graph.
Native plugins
JavaScript plugins are single-threaded. Native plugins are NAPI modules that run on multiple threads alongside Bun’s parser, offering significantly better performance. Native plugins implement theonBeforeParse lifecycle hook, which is called before a file is parsed.
Creating a native plugin in Rust
lib.rs
index.ts
Runtime plugins
Plugins can also be used at runtime (outside the bundler) to interceptimport and require calls in Bun’s module loader:
preload.ts
bunfig.toml:
bunfig.toml
Runtime plugins registered with
plugin() are active for the current Bun process only. Bundler plugins registered via Bun.build() run only during bundling.