Migration from Webpack
Work in progress
This page is a work in progress. Help complete it by contributing on GitHub!
- Why Migrate to Vite?
- Migration Steps
- Configuration Comparison
- Continuing with Webpack
- Common Migration Issues
- Hybrid Approach
- Next Steps
If you're upgrading an existing UserFrosting 5 project, you might be wondering about migrating from Webpack Encore to Vite. Good news: you have options! You can migrate to Vite for improved performance, or continue using Webpack if that works better for your project. This guide will help you decide and walk you through both paths.
Why Migrate to Vite?
Vite offers several advantages over Webpack Encore:
Performance:
- Instant server start - No bundling in development
- Lightning-fast HMR - Updates in milliseconds
- Optimized builds - Faster production builds with Rollup
Developer Experience:
- Simpler configuration - Less boilerplate
- Native TypeScript - No additional setup
- Better Vue 3 support - Official plugin with optimizations
Modern Stack:
- ESM-first - Built for modern browsers
- Better tree-shaking - Smaller bundle sizes
- Future-proof - Active development and community
Migration Steps
1. Install Vite Dependencies
npm install -D vite @vitejs/plugin-vue vite-plugin-vue-devtools
If using YAML imports:
npm install -D @modyfi/vite-plugin-yaml
2. Create Vite Configuration
Create vite.config.ts in your project root:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import ViteYaml from '@modyfi/vite-plugin-yaml'
const vitePort = parseInt(process.env.VITE_PORT || '5173', 10)
export default defineConfig({
plugins: [
vue(),
ViteYaml(),
vueDevTools({
appendTo: 'app/assets/main.ts'
})
],
server: {
host: true,
strictPort: true,
port: vitePort,
origin: `http://localhost:${vitePort}`,
},
root: 'app/assets/',
base: '/assets/',
build: {
outDir: '../../public/assets',
assetsDir: '',
emptyOutDir: true,
manifest: true,
rollupOptions: {
input: {
// Copy your entries from webpack.entries.js
main: 'app/assets/main.ts'
}
}
},
css: {
preprocessorOptions: {
less: {
relativeUrls: 'all'
}
}
},
optimizeDeps: {
include: ['uikit', 'uikit/dist/js/uikit-icons']
}
})
3. Update package.json Scripts
Replace Webpack scripts with Vite scripts:
Before (Webpack):
{
"scripts": {
"webpack:dev": "encore dev",
"webpack:build": "encore production",
"webpack:watch": "encore dev --watch"
}
}
After (Vite):
{
"scripts": {
"vite:dev": "vite",
"vite:build": "vite build"
}
}
4. Update Configuration
Modify app/config/default.php:
'assets' => [
// Change bundler from 'webpack' to 'vite'
'bundler' => env('ASSETS_BUNDLER', 'vite'),
// Add/update Vite configuration
'vite' => [
'manifest' => 'assets://.vite/manifest.json',
'dev' => env('VITE_DEV_ENABLED', true),
'base' => '/assets/',
'server' => 'http://[::1]:' . env('VITE_PORT', 5173),
],
],
5. Update Twig Templates
Replace Webpack Encore functions with Vite functions:
Before (Webpack):
{{ encore_entry_script_tags('app') }}
{{ encore_entry_link_tags('app') }}
After (Vite):
{{ vite_js('main.ts') }}
{{ vite_css('main.ts') }}
6. Update Environment Variables
Add to your .env file:
# Vite configuration
VITE_PORT=5173
VITE_DEV_ENABLED=true
ASSETS_BUNDLER=vite
7. Build and Test
# Install dependencies
npm install
# Start development
php bakery assets:vite
php bakery serve
# Test in browser
# Visit http://localhost:8080
Configuration Comparison
Entry Points
Webpack (webpack.entries.js):
export default {
app: "./app/assets/main.ts",
admin: "./app/assets/admin.ts"
}
Vite (vite.config.ts):
export default defineConfig({
build: {
rollupOptions: {
input: {
main: 'app/assets/main.ts',
admin: 'app/assets/admin.ts'
}
}
}
})
Loaders → Plugins
Webpack:
Encore
.enableVueLoader()
.enableLessLoader()
.enableTypeScriptLoader()
Vite:
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
css: {
preprocessorOptions: {
less: {}
}
}
// TypeScript: built-in, no plugin needed
})
Output Configuration
Webpack:
Encore
.setOutputPath('public/assets/')
.setPublicPath('/assets/')
Vite:
export default defineConfig({
base: '/assets/',
build: {
outDir: '../../public/assets'
}
})
Code Splitting
Webpack:
Encore.splitEntryChunks()
Vite:
// Automatic, or configure manually:
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router']
}
}
}
}
})
Continuing with Webpack
If you need to continue using Webpack Encore, UserFrosting 6 maintains backward compatibility.
Keep Using Webpack
- Set the bundler in configuration:
// app/config/default.php
'assets' => [
'bundler' => 'webpack', // Use 'webpack' instead of 'vite'
],
Or via environment variable:
ASSETS_BUNDLER=webpack
- Use Webpack commands:
php bakery assets:webpack
php bakery assets:build
- Keep Webpack Encore templates:
{{ encore_entry_script_tags('app') }}
{{ encore_entry_link_tags('app') }}
When to Use Webpack
Consider staying with Webpack if:
- You have complex Webpack-specific configurations
- You use Encore features not available in Vite
- Your team has extensive Webpack expertise
- Migration cost outweighs benefits for your project
Webpack Support Timeline
Webpack Encore support is maintained for backward compatibility but:
- ⚠️ Not recommended for new projects
- ⚠️ May be deprecated in future major versions
- ⚠️ Receives minimal updates compared to Vite
Common Migration Issues
Import Path Changes
Issue: Module resolution differences
Solution: Vite uses browser-native ESM. Ensure imports use extensions:
// May work in Webpack but not Vite
import Component from './Component'
// Recommended for Vite
import Component from './Component.vue'
Global Variables
Issue: Webpack's DefinePlugin equivalents
Solution: Use environment variables:
// Webpack
const API_URL = process.env.API_URL
// Vite
const API_URL = import.meta.env.VITE_API_URL
Static Assets
Issue: Different asset handling
Solution: Place static assets in app/assets/public/:
app/assets/
└── public/
├── images/
└── fonts/
Reference with absolute paths:
<img src="/assets/images/logo.png">
CSS Imports
Issue: Import path resolution
Solution: Use @import with proper paths:
// Webpack
@import '~uikit/src/less/uikit.less';
// Vite
@import 'uikit/src/less/uikit.less';
Hybrid Approach
You can run both bundlers side-by-side during migration:
-
Keep both configurations:
webpack.config.jsfor Webpackvite.config.tsfor Vite
-
Use different entry points:
// vite.config.ts input: { 'vite-main': 'app/assets/vite-main.ts' } -
Load conditionally in templates:
{% if use_vite %} {{ vite_js('vite-main.ts') }} {% else %} {{ encore_entry_script_tags('app') }} {% endif %} -
Gradually migrate pages to Vite
Next Steps
After migrating to Vite:
-
Remove Webpack dependencies:
npm uninstall @symfony/webpack-encore webpack webpack-cli -
Delete Webpack configuration:
rm webpack.config.js webpack.entries.js -
Update documentation for your team
-
Optimize your Vite configuration based on your needs
Explore the Vite Configuration guide to learn more about Vite configuration, or jump to Advanced Usage for optimization techniques.