Vite Plugin API: Extend Vite Build Tool
Welcome to TopperBlog! 👋
I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.
🎯 What I Write About:
• AI/ML Engineering & LLMs
• Web3 & Blockchain Development
• System Design & Architecture
• Interview Preparation (FAANG)
• Freelancing & Remote Work
• Modern Tech Stacks (Next.js, React, Rust, TypeScript)
• Performance Optimization & Best Practices
💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.
📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.
🌐 Let's connect and grow together in this amazing tech journey!
#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering
Vite Plugin API: Extend Vite Build Tool
Overview
The Vite Plugin API allows developers to extend Vite's build capabilities by hooking into its core processes. Vite plugins are based on Rollup's plugin interface with Vite-specific extensions.
Core Concepts
Plugin Structure
export default function myPlugin(options = {}) {
return {
name: 'my-plugin', // required, used for warnings and errors
apply: 'build', // 'build' | 'serve' | function
enforce: 'pre', // 'pre' | 'post'
// Vite-specific hooks
config(config, env) {},
configResolved(resolvedConfig) {},
configureServer(server) {},
transformIndexHtml(html) {},
handleHotUpdate(ctx) {},
// Rollup hooks
resolveId(id) {},
load(id) {},
transform(code, id) {},
}
}
Key Hooks
Universal Hooks
| Hook | Purpose | Timing |
config | Modify Vite config | Before resolution |
configResolved | Store final config | After resolution |
apply | Conditional plugin application | Plugin initialization |
enforce | Hook execution order | Plugin initialization |
Vite-Specific Hooks
configureServer(server)
Customize dev server:
configureServer(server) {
return () => {
server.middlewares.use('/custom', (req, res) => {
res.end('Custom response')
})
}
}
transformIndexHtml(html)
Transform index.html:
transformIndexHtml(html) {
return html.replace(
/<title>(.*?)<\/title>/,
`<title>Modified: $1</title>`
)
}
handleHotUpdate(ctx)
Handle HMR updates:
handleHotUpdate(ctx) {
if (ctx.file.endsWith('.custom')) {
console.log(`${ctx.file} changed, full reload`)
ctx.server.ws.send({
type: 'full',
event: 'special',
event_payload: {},
})
return []
}
}
Rollup Hooks
resolveId(id)
Custom module resolution:
resolveId(id) {
if (id === 'virtual-module') {
return id
}
}
load(id)
Custom module loading:
load(id) {
if (id === 'virtual-module') {
return 'export default "virtual content"'
}
}
transform(code, id)
Transform module code:
transform(code, id) {
if (id.endsWith('.custom')) {
return {
code: transformedCode,
map: null
}
}
}
Practical Examples
Virtual Module Plugin
export default function virtualModulePlugin() {
const virtualModuleId = 'virtual-module'
const resolvedId = '\0' + virtualModuleId
return {
name: 'virtual-module',
resolveId(id) {
if (id === virtualModuleId) {
return resolvedId
}
},
load(id) {
if (id === resolvedId) {
return `export const msg = "from virtual module"`
}
}
}
}
Framework Integration Plugin
export default function frameworkPlugin() {
return {
name: 'framework-plugin',
resolveId(id) {
if (id.endsWith('.framework')) {
return id
}
},
load(id) {
if (id.endsWith('.framework')) {
const content = fs.readFileSync(id, 'utf-8')
return compileFramework(content)
}
},
transform(code, id) {
if (id.endsWith('.framework')) {
return {
code: optimizeCode(code),
map: null
}
}
}
}
}
Environment-Specific Plugin
export default function envPlugin() {
let config
return {
name: 'env-plugin',
apply: 'build',
enforce: 'pre',
configResolved(resolvedConfig) {
config = resolvedConfig
},
transform(code, id) {
if (config.command === 'build') {
return code.replace(
/process\.env\.NODE_ENV/g,
JSON.stringify(config.env.NODE_ENV)
)
}
}
}
}
Advanced Patterns
Conditional Plugin Application
apply: 'build', // only during build
apply: 'serve', // only during dev
apply: (config, env) => env.command === 'build' && config.build.ssr
Plugin Ordering
{
enforce: 'pre', // runs before core plugins
// ... plugin code
}
{
enforce: 'post', // runs after core plugins
// ... plugin code
}
Accessing Resolved Config
configResolved(resolvedConfig) {
this.config = resolvedConfig
}
Best Practices
- Use
\0prefix for virtual modules to avoid conflicts - Store config in
configResolvedhook for later use - Return null from hooks when not applicable
- Use
applyto limit plugin scope - Document hook order with
enforce - Handle both dev and build modes appropriately
- Provide source maps for better debugging
Common Use Cases
- Custom file format compilation
- Virtual modules
- Framework integration
- Build optimization
- Development server customization
- Asset processing
- Code generation
- Environment variable injection
The Vite Plugin API provides powerful extensibility while maintaining simplicity and performance.