It is recommended to go through Rolldown’s plugin documentation first before reading the sections below.
Authoring a Plugin
Vite strives to offer established patterns out of the box, so before creating a new plugin make sure that you check the Features guide to see if your need is covered. Also review available community plugins, both in the form of a compatible Rollup plugin and Vite Specific plugins. When creating a plugin, you can inline it in yourvite.config.js. There is no need to create a new package for it.
Conventions
If the plugin doesn’t use Vite specific hooks and can be implemented as a Compatible Rolldown Plugin, then it is recommended to use the Rolldown Plugin naming conventions. Rolldown Plugins:- Should have a clear name with
rolldown-plugin-prefix - Include
rolldown-pluginandvite-pluginkeywords in package.json
- Should have a clear name with
vite-plugin-prefix - Include
vite-pluginkeyword in package.json - Include a section in the plugin docs detailing why it is a Vite only plugin
vite-plugin-vue-prefix for Vue Pluginsvite-plugin-react-prefix for React Pluginsvite-plugin-svelte-prefix for Svelte Plugins
Plugin Configuration
Users add plugins to the projectdevDependencies and configure them using the plugins array option:
vite.config.js
plugins also accepts presets including several plugins as a single element:
Simple Examples
Transforming Custom File Types
Virtual Modules Convention
Virtual modules are a useful scheme that allows you to pass build time information to the source files using normal ESM import syntax.virtual: for the user-facing path by convention. Internally, plugins that use virtual modules should prefix the module ID with \0 while resolving the id, a convention from the rollup ecosystem. This prevents other plugins from trying to process the id (like node resolution), and core features like sourcemaps can use this info to differentiate between virtual modules and regular files.
Universal Hooks
During dev, the Vite dev server creates a plugin container that invokes Rolldown Build Hooks the same way Rolldown does it.Called on Server Start
The following hooks are called once on server start:Called on Each Module Request
The following hooks are called on each incoming module request:Called on Server Close
The following hooks are called when the server is closed:The
moduleParsed hook is not called during dev, because Vite avoids full AST parses for better performance.Output Generation Hooks (except closeBundle) are not called during dev.Vite Specific Hooks
Vite plugins can also provide hooks that serve Vite-specific purposes. These hooks are ignored by Rollup.config
Modify Vite config before it’s resolved.Kind:
async, sequentialThe hook receives the raw user config and can return a partial config object that will be deeply merged into existing config, or directly mutate the config.configResolved
Called after the Vite config is resolved.Kind:
async, parallelUse this hook to read and store the final resolved config. It is also useful when the plugin needs to do something different based on the command being run.The
command value is serve in dev (in the cli vite, vite dev, and vite serve are aliases).configureServer
Hook for configuring the dev server.Kind:
async, sequentialThe most common use case is adding custom middlewares to the internal connect app.configureServer hook is called before internal middlewares are installed, so the custom middlewares will run before internal middlewares by default. If you want to inject a middleware after internal middlewares, you can return a function from configureServer:
configureServer is not called when running the production build so your other hooks need to guard against its absence.configurePreviewServer
Same as Kind:
configureServer but for the preview server.async, sequentialtransformIndexHtml
Dedicated hook for transforming HTML entry point files such as Kind:
index.html.async, sequentialThe hook receives the current HTML string and a transform context. The context exposes the ViteDevServer instance during dev, and exposes the Rollup output bundle during build.- Transformed HTML string
- An array of tag descriptor objects (
{ tag, attrs, children }) to inject to the existing HTML - An object containing both as
{ html, tags }
order is undefined, with this hook applied after the HTML has been transformed. In order to inject a script that should go through the Vite plugins pipeline, order: 'pre' will apply the hook before processing the HTML. order: 'post' applies the hook after all hooks with order undefined are applied.
Basic Example:
handleHotUpdate
Perform custom HMR update handling.Kind:
async, sequentialThe hook receives a context object with the following signature:- Filter and narrow down the affected module list so that the HMR is more accurate.
- Return an empty array and perform a full reload:
- Return an empty array and perform complete custom HMR handling by sending custom events to the client:
Plugin Ordering
A Vite plugin can additionally specify anenforce property (similar to webpack loaders) to adjust its application order. The value of enforce can be either "pre" or "post". The resolved plugins will be in the following order:
- Alias
- User plugins with
enforce: 'pre' - Vite core plugins
- User plugins without enforce value
- Vite build plugins
- User plugins with
enforce: 'post' - Vite post build plugins (minify, manifest, reporting)
This is separate from hooks ordering, those are still separately subject to their
order attribute as usual for Rolldown hooks.Conditional Application
By default plugins are invoked for both serve and build. In cases where a plugin needs to be conditionally applied only during serve or build, use theapply property:
Rolldown Plugin Compatibility
A fair number of Rolldown / Rollup plugins will work directly as a Vite plugin (e.g.@rollup/plugin-alias or @rollup/plugin-json), but not all of them, since some plugin hooks do not make sense in an unbundled dev server context.
In general, as long as a Rolldown / Rollup plugin fits the following criteria then it should just work as a Vite plugin:
- It doesn’t use the
moduleParsedhook - It doesn’t rely on Rolldown specific options like
transform.inject - It doesn’t have strong coupling between bundle-phase hooks and output-phase hooks
build.rolldownOptions.plugins instead.
You can also augment an existing Rolldown / Rollup plugin with Vite-only properties:
vite.config.js
Client-Server Communication
Since Vite 2.9, we provide some utilities for plugins to help handle the communication with clients.Server to Client
On the plugin side, we could useserver.ws.send to broadcast events to the client:
vite.config.js
hot.on to listen to the events:
Client to Server
To send events from the client to the server, we can usehot.send:
server.ws.on and listen to the events on the server side:
vite.config.js
TypeScript for Custom Events
Internally, vite infers the type of a payload from theCustomEventMap interface, it is possible to type custom events by extending the interface:
Make sure to include the
.d.ts extension when specifying TypeScript declaration files. Otherwise, Typescript may not know which file the module is trying to extend.events.d.ts
InferCustomEventPayload<T> to infer the payload type for event T:
Hook Filters
Rolldown introduced a hook filter feature to reduce the communication overhead between the Rust and JavaScript runtimes. This feature allows plugins to specify patterns that determine when hooks should be called, improving performance by avoiding unnecessary hook invocations. This is also supported by Rollup 4.38.0+ and Vite 6.3.0+. To make your plugin backward compatible with older versions, make sure to also run the filter inside the hook handlers.Output Bundle Metadata
During build, Vite augments Rolldown’s build output objects with a Vite-specificviteMetadata field.
This is available through:
RenderedChunk(for example inrenderChunkandaugmentChunkHash)OutputChunkandOutputAsset(for example ingenerateBundleandwriteBundle)
viteMetadata provides:
viteMetadata.importedCss: Set<string>viteMetadata.importedAssets: Set<string>
build.manifest.
Example:
vite.config.ts
Path Normalization
Vite normalizes paths while resolving ids to use POSIX separators ( / ) while preserving the volume in Windows. On the other hand, Rollup keeps resolved paths untouched by default. For Vite plugins, when comparing paths against resolved ids it is important to first normalize the paths to use POSIX separators. An equivalentnormalizePath utility function is exported from the vite module.
Filtering Patterns
Vite exposes@rollup/pluginutils’s createFilter function to encourage Vite specific plugins and integrations to use the standard include/exclude filtering pattern, which is also used in Vite core itself.