Skip to main content
Bun provides a universal plugin API that works in both the runtime and the bundler. Plugins intercept imports and perform custom loading logic: reading files, transpiling code, transforming content, and more. Use plugins to:
  • Add support for file types Bun doesn’t handle natively (.scss, .yaml, .svg, etc.)
  • Implement custom module resolution rules
  • Inject code or environment data as virtual modules
  • Apply framework-level transforms like CSS extraction or macros

Creating a plugin

A plugin is a plain object with a name string and a setup function. The setup function receives a build object with lifecycle hook registration methods.
my-plugin.ts

Using a plugin at runtime

To apply a plugin when running code with bun run, use Bun.plugin() in a preload file and register that file in bunfig.toml.
1

Define the plugin

my-plugin.ts
2

Register as a preload in bunfig.toml

bunfig.toml
3

Import the custom file type

index.ts

Using a plugin in the bundler

Pass the plugin to Bun.build via the plugins array.
build.ts

Lifecycle hooks

onLoad

onLoad intercepts a module after it has been resolved and lets you replace its contents before Bun parses it.
The filter regex is matched against the file path. Return contents (a string) and optionally a loader to tell Bun how to interpret the content. Example: virtual environment module
Example: load .txt files as strings

.defer()

The second argument to onLoad is a defer function. Calling await defer() pauses execution of this callback until all other modules have been loaded. This is useful when a module’s content depends on what other modules export.

onResolve

onResolve intercepts module resolution and lets you redirect imports to different paths.
Example: redirect image imports

onStart

onStart registers a callback that runs once when the bundler starts. It can be async; the bundler waits for all onStart callbacks to resolve before continuing.
onStart callbacks cannot modify build.config. To mutate the build config, do so directly inside setup() before registering any hooks.

Available loaders

The loader field in an onLoad return value tells Bun how to parse the returned contents.

Namespaces

Every module has a namespace. The default namespace is "file". Namespaces are used to prefix module paths in transpiled code.
Specifying a custom namespace in onLoad or onResolve scopes the hook to only matching modules:

Native plugins

JavaScript plugins run on a single thread. For maximum performance, you can write a native plugin as a Node-API (NAPI) module. Native plugins run on multiple threads and avoid UTF-8 to UTF-16 conversion overhead.

onBeforeParse

The onBeforeParse hook is the only lifecycle hook available to native plugins. It runs immediately before a file is parsed, on any thread, and can replace the file’s source code.
The native module exports a C ABI function matching the onBeforeParse signature. Here is an example in Rust using the bun-native-plugin crate:
lib.rs

Plugin type reference