Release CandidateThe Environment API is generally in the release candidate phase. We’ll maintain stability in the APIs between major releases to allow the ecosystem to experiment and build upon them. However, note that some specific APIs are still considered experimental.Resources:
- Feedback discussion where we are gathering feedback about the new APIs.
- Environment API PR where the new APIs were implemented and reviewed.
Accessing the Current Environment in Hooks
Given that there were only two Environments until Vite 6 (client and ssr), a ssr boolean was enough to identify the current environment in Vite APIs. Plugin Hooks received a ssr boolean in the last options parameter, and several APIs expected an optional last ssr parameter to properly associate modules to the correct environment.
With the advent of configurable environments, we now have a uniform way to access their options and instance in plugins. Plugin hooks now expose this.environment in their context, and APIs that previously expected a ssr boolean are now scoped to the proper environment.
The Vite server has a shared plugin pipeline, but when a module is processed it is always done in the context of a given environment. The environment instance is available in the plugin context.
A plugin could use the environment instance to change how a module is processed depending on the configuration for the environment (which can be accessed using environment.config).
Registering New Environments Using Hooks
Plugins can add new environments in theconfig hook. For example, RSC support uses an additional environment to have a separate module graph with the react-server condition:
Configuring Environment Using Hooks
While theconfig hook is running, the complete list of environments isn’t yet known and the environments can be affected by both the default values from the root level environment config or explicitly through the config.environments record.
Plugins should set default values using the config hook. To configure each environment, they can use the new configEnvironment hook. This hook is called for each environment with its partially resolved config including resolution of final defaults.
Modify environment configs before it’s resolved.Kind:
async, sequentialThe hotUpdate Hook
Perform custom HMR update handling for a given environment.Kind:
async, sequentialWhen a file changes, the HMR algorithm is run for each environment in series according to the order in server.environments, so the hotUpdate hook will be called multiple times.- 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:
transform hook):
Per-environment State in Plugins
Given that the same plugin instance is used for different environments, the plugin state needs to be keyed withthis.environment. This is the same pattern the ecosystem has already been using to keep state about modules using the ssr boolean as key to avoid mixing client and ssr modules state.
A Map<Environment, State> can be used to keep the state for each environment separately.
For backward compatibility,
buildStart and buildEnd are only called for the client environment without the perEnvironmentStartEndDuringDev: true flag. Same for watchChange and the perEnvironmentWatchChangeDuringDev: true flag.Per-environment Plugins
A plugin can define what are the environments it should apply to with theapplyToEnvironment function.
Define environments where this plugin should be active. By default, the plugin is active in all environments.Experimental
applyToEnvironment hook allows to easily make it per-environment.
Example:
perEnvironmentPlugin helper to simplify these cases where no other hooks are required:
The
applyToEnvironment hook is called at config time, currently after configResolved due to projects in the ecosystem modifying the plugins in it. Environment plugins resolution may be moved before configResolved in the future.Application-Plugin Communication
environment.hot allows plugins to communicate with the code on the application side for a given environment. This is the equivalent of the Client-server Communication feature, but supports environments other than the client environment.
Managing the Application Instances
Be aware that there might be multiple application instances running in the same environment. For example, if you have multiple tabs open in the browser, each tab is a separate application instance and has a separate connection to the server. When a new connection is established, avite:client:connect event is emitted on the environment’s hot instance. When the connection is closed, a vite:client:disconnect event is emitted.
Each event handler receives the NormalizedHotChannelClient as the second argument. The client is an object with a send method that can be used to send messages to that specific application instance. The client reference is always the same for the same connection, so you can keep it to track the connection.
Example Usage
The plugin side:import.meta.hot object to send messages to the plugin.
Environment in Build Hooks
In the same way as during dev, plugin hooks also receive the environment instance during build, replacing thessr boolean. This also works for renderChunk, generateBundle, and other build only hooks.
Shared Plugins During Build
Before Vite 6, the plugins pipelines worked in a different way during dev and build:- During dev: plugins are shared
- During Build: plugins are isolated for each environment (in different processes:
vite buildthenvite build --ssr)
client build and the ssr build through manifest files written to the file system. In Vite 6, we are now building all environments in a single process so the way the plugins pipeline and inter-environment communication can be aligned with dev.
In a future major, we could have complete alignment:
- During both dev and build: plugins are shared, with per-environment filtering
ResolvedConfig instance shared during build, allowing for caching at entire app build process level in the same way as we have been doing with WeakMap<ResolvedConfig, CachedData> during dev.
For Vite 6, we need to do a smaller step to keep backward compatibility. Ecosystem plugins are currently using config.build instead of environment.config.build to access configuration, so we need to create a new ResolvedConfig per-environment by default. A project can opt-in into sharing the full config and plugins pipeline setting builder.sharedConfigBuild to true.
This option would only work of a small subset of projects at first, so plugin authors can opt-in for a particular plugin to be shared by setting the sharedDuringBuild flag to true.
Opt-in this plugin into the shared plugins pipeline.ExperimentalFor backward-compatibility, plugins are re-recreated for each environment during
vite build --app. We have an opt-in per plugin, and a general builder.sharedPlugins. In a future major, we’ll flip the default to be shared by default.Opt-in this plugin into per-environment buildStart and buildEnd during dev.ExperimentalFor backward-compatibility, the buildStart hook is called only once during dev, for the client environment. Plugins can opt-in to be called per-environment, aligning with the build hook behavior.
Opt-in this plugin into per-environment watchChange during dev.ExperimentalFor backward-compatibility, the watchChange hook is called only once during dev, for the client environment. Plugins can opt-in to be called per-environment, aligning with the watchChange hook behavior.