Provide expert guidance on Vite configuration, plugin development, HMR optimization, library mode bundling, and production build tuning. Covers Vite 6.x with Rollup under the hood and ESBuild for dev transforms.
Vite exposes env vars prefixed with VITE_ to client code:
// Only VITE_* vars are exposed to the client// .envVITE_API_URL=https://api.example.comDATABASE_URL=postgres://... // NOT exposed to client// Usage in codeconst apiUrl = import.meta.env.VITE_API_URL;
// Type augmentation// src/vite-env.d.ts/// <reference types="vite/client" />interfaceImportMetaEnv {
readonlyVITE_API_URL: string;
readonlyVITE_APP_TITLE: string;
}
interfaceImportMeta {
readonlyenv: ImportMetaEnv;
}
Plugin Development
Custom plugin structure:
// plugins/my-plugin.tsimporttype { Plugin, ResolvedConfig } from'vite';
interfaceMyPluginOptions {
include?: string[];
transform?: boolean;
}
exportfunctionmyPlugin(options: MyPluginOptions = {}): Plugin {
letconfig: ResolvedConfig;
return {
name: 'vite-plugin-my-plugin', // must be unique, prefixed with vite-plugin-enforce: 'pre', // 'pre' | 'post' — run before/after core plugins// Called when config is resolvedconfigResolved(resolvedConfig) {
config = resolvedConfig;
},
// Transform source codetransform(code, id) {
if (!id.endsWith('.tsx')) returnnull;
// Return transformed code + sourcemapreturn {
code: code.replace(/__DEV__/g, String(config.mode === 'development')),
map: null, // provide sourcemap if modifying positions
};
},
// Generate virtual modulesresolveId(id) {
if (id === 'virtual:my-module') return'\0virtual:my-module';
returnnull;
},
load(id) {
if (id === '\0virtual:my-module') {
return`export const data = ${JSON.stringify(options)};`;
}
returnnull;
},
// Dev-server middlewareconfigureServer(server) {
server.middlewares.use('/health', (_req, res) => {
res.end(JSON.stringify({ status: 'ok' }));
});
},
};
}
Plugin hooks execution order:
config — Modify config before resolution
configResolved — Read final resolved config
configureServer — Add dev server middleware (dev only)
// In a module that needs custom HMRif (import.meta.hot) {
import.meta.hot.accept((newModule) => {
if (newModule) {
// Handle the updated moduleupdateState(newModule.default);
}
});
// Clean up side effects before HMR replacementimport.meta.hot.dispose(() => {
cleanup();
});
// Persist data across HMR updatesimport.meta.hot.data.count = (import.meta.hot.data.count ?? 0) + 1;
}