The Extension Surface: Plugins, SSR, and the Runtimes Beyond the Browser

Vite becomes a platform rather than a command when another system needs to add behavior without taking over its module and build model. This chapter examines that extension surface: how plugins participate in development and production, how SSR and traditional backends divide ownership with Vite, and how the Environment API represents runtimes beyond a browser and a Node process. The practical goal is to decide which boundary belongs to a plugin, a framework, a backend, or a runtime provider—and what each boundary must still implement.

The need for extension follows directly from Vite's design. Vite can transform modules and bundle an application, but it cannot know the syntax of every framework component, the HTML policy of every backend, or the execution rules of every edge runtime. A lean core is useful only if its interfaces are strong enough for other systems to add those missing meanings without bypassing the graph that makes development responsive.

Plugins are the shared transformation seam

A Vite plugin is most useful when it answers a question about a module or a development/build environment. It can resolve an import identifier, load virtual or generated content, transform a file, change an HTML entry, configure the dev server, or customize HMR. Framework integrations use these capabilities to teach Vite what a component file means and how a change to that file should reach the running application.

The familiar plugin interface also connects Vite to the broader Rolldown and Rollup ecosystem. A plugin that only needs general resolution, loading, or transformation hooks may work in Vite and in a bundler directly. A plugin that relies on Vite's server or HMR hooks is Vite-specific. This distinction lets Vite support a large extension community without making every plugin a permanent part of core.

The interface is shared across serving and building, but execution is not identical. During development, the server commonly invokes resolveId, load, and transform when a browser or runtime requests a module. During a production build, Rolldown can run bundle and output hooks because it is traversing the complete graph and generating artifacts. A hook that depends on the entire parsed bundle or on final output files has no equivalent at the moment an on-demand dev server is processing one request.

That difference is a compatibility test. A plugin may work in both modes when its behavior can be expressed in request-time transformation and bundle-time transformation. It may need to apply only during build if its logic depends on output generation. It may need to keep separate state when the same plugin instance participates in multiple environments. Vite supplies apply and ordering controls, but the plugin author still owns the assumptions hidden behind the hooks.

The cost of a plugin is also part of Vite's feedback budget. Configuration and startup hooks run before the developer can use the server. Resolve, load, and transform hooks sit directly on the browser's request path. A plugin that scans a large tree for every request, loads a heavyweight dependency at startup, or transforms files it does not need can turn an on-demand server into an accidental bottleneck. Hook filters and narrow file conditions are not cosmetic optimizations; they preserve the reason the request-driven architecture exists.

Framework presets illustrate the value of composition. A single framework plugin can be a package of several coordinated plugins: one may transform component syntax, another may inject a refresh preamble, and another may expose development tooling. The application sees one integration, while Vite receives a set of focused behaviors. This allows a framework team to own its semantics without requiring Vite core to know every framework lifecycle.

The same approach applies to HTML. A plugin can transform an entry file, inject tags, or add data needed by a framework. During development, the transform context can access the Vite server; during a build, it can inspect the generated bundle.