.scss or .yaml. In the context of Bun’s bundler, plugins can be used to implement framework-level features like CSS extraction, macros, and client-server code co-location.
Lifecycle hooks
Plugins can register callbacks that execute at various stages of the bundling lifecycle:onStart(): Runs once when the bundler starts a buildonResolve(): Runs before a module is resolvedonLoad(): Runs before a module is loadedonBeforeParse(): Runs zero-copy native plugins in the parser thread, before a file is parsedonEnd(): Runs after bundling completes
Reference
A rough outline of the types (refer to Bun’sbun.d.ts for complete type definitions):
Usage
Plugins are defined as simple JavaScript objects containing aname property and a setup function.
Bun.build in the plugins array.
Plugin lifecycle
Namespaces
onLoad and onResolve accept an optional namespace string. What is a namespace?
Each module has a namespace. Namespaces are used to prefix import paths in transpiled code; for instance, a loader with filter: /\.yaml$/ and namespace: "yaml:" would convert an import ./myfile.yaml to yaml:./myfile.yaml.
The default namespace is "file", which usually doesn’t need to be explicitly specified, e.g.: import myModule from "./my-module.ts" is equivalent to import myModule from "file:./my-module.ts".
Other common namespaces:
"bun": Bun-specific modules (e.g."bun:test","bun:sqlite")"node": Node.js modules (e.g."node:fs","node:path")
onStart
onStart() callbacks to complete before proceeding.
For example:
onStart() (sleep 10 seconds) and the second onStart() (write bundle time to file) to complete before continuing.
onStart() callbacks (and all lifecycle callbacks) cannot modify the build.config object. To modify build.config, you must do so directly in the setup() function.onResolve
onResolve() plugin lifecycle callback lets you customize the module resolution logic.
The first argument to onResolve() is an object with a filter and optional namespace property. The filter is a regular expression used to match against import strings; in practice, it’s used to filter which modules should use the custom resolution logic.
The second argument is a callback function that will be called for each imported module that matches the filter and namespace from the first argument.
The callback receives the matched module path as input, and can return a new path for the module. Bun will read the contents of the new path and parse it as a module.
For example, to redirect all imports of images/ to ./public/images/:
onLoad
onLoad() plugin lifecycle callback lets you modify the contents of a resolved module before Bun reads and parses them.
Like onResolve(), the first argument to onLoad() is used to filter which modules this call should apply to.
The second argument is a callback that will be called before the contents of each matched module are loaded.
The callback receives the path of the matched module, the module’s importer, the module namespace, and the module kind.
The callback can return a new contents string and a new loader for the module.
For example:
import env from "env" into a JavaScript module that exports the current environment variables.
.defer()
One of the arguments passed to theonLoad callback is a defer function. This function returns a Promise that only resolves after all other modules have finished loading.
This allows you to defer the execution of the onLoad callback until all other modules are loaded.
This is useful for returning module contents that depend on the contents of other modules.
Example: Track and report unused exports
Example: Track and report unused exports
onEnd
Examples
YAML loader
Here’s a simple plugin that adds support for.yaml files:
Inline SVG as React components
Environment variable injection
Build-time code generation
Plugin ordering
Plugins are executed in the order they are defined in theplugins array:
- For
onResolve: The first plugin that returns a value wins - For
onLoad: The first plugin that returns a value wins - For
onStartandonEnd: All callbacks execute
Best practices
Use specific filters
Use precise regex patterns to avoid unnecessary callback invocations:Cache expensive operations
Cache results when possible to avoid redundant work:Return early
Returnundefined or void from callbacks when no transformation is needed: