
August 14, 2026
9 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Getting the Vite config for Laravel projects right is the difference between a smooth development experience and hours of debugging broken assets or slow builds. While Laravel 12 ships with sensible defaults, most production applications require customization to handle CSS preprocessors, third-party libraries, and server-specific constraints. If you are setting up a new application or migrating an older stack, understanding these configuration nuances prevents common pitfalls that stall teams. For developers evaluating their broader stack choices, my overview of Laravel development services covers when this framework makes sense for business-critical systems.
vite.config.js at the project root, using the laravel-vite-plugin to define input entry points like resources/css/app.css and resources/js/app.js. Always configure server.hmr.host explicitly when developing inside Docker or remote environments to ensure hot module replacement functions correctly.How do you set up the base Vite config for Laravel projects?
Laravel 12 includes Vite out of the box, but the default configuration is intentionally minimal. On any real client project I have shipped since 2024, the base vite.config.js required expansion within the first week of development. The plugin acts as the bridge between Vite's native ESM workflow and Laravel's Blade templating engine.
A standard starting point for a custom application looks like this:
<?php // vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/css/app.css',
'resources/js/app.js',
],
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
resolve: {
alias: {
'@': '/resources/js',
},
},
}); The input array is where most configuration issues originate. Unlike Webpack Mix, which could auto-discover files, Vite requires explicit entry points. Every CSS file and JS module that needs processing must be listed here. I typically organize entry points by feature for larger applications rather than dumping everything into a single app.js. This keeps initial bundle sizes manageable and allows for code splitting.
The refresh: true option tells the Vite dev server to trigger a full page reload whenever Blade templates change. Without this, you will find yourself manually refreshing after updating views, which defeats the purpose of HMR. For projects using Livewire, this setting is non-negotiable; for pure API backends serving a separate SPA frontend, you can safely omit it.
When working on legal-tech portals or e-commerce platforms, I often add additional entry points for admin panels or vendor dashboards that live in separate directories. Keeping these isolated prevents loading unnecessary JavaScript on public-facing pages, which directly impacts Core Web Vitals scores.
Why is HMR not working in Docker or remote environments?
This is the single most frequent issue I encounter when onboarding developers or deploying staging environments. Hot Module Replacement relies on a WebSocket connection between the browser and the Vite dev server. In local setups where PHP and Vite run on the same host machine, this works automatically. Inside Docker containers, WSL2, or remote EC2 instances, the browser cannot reach localhost:5173 because that address refers to the user's machine, not the container.
The fix requires explicit host configuration in your Vite config:
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
server: {
host: '0.0.0.0',
port: 5173,
hmr: {
host: 'localhost', // Or your domain if proxied
},
},
}); Setting host: '0.0.0.0' binds the Vite server to all network interfaces, making it accessible outside the container. The hmr.host parameter tells the browser which hostname to use for the WebSocket connection. If you access your application via http://localhost:8000, set hmr.host to localhost. If you use a custom domain like myapp.test through Nginx Proxy Manager or Traefik, use that domain instead.
- Symptom: Page loads but styles/scripts never update without manual refresh.
- Cause: Browser console shows WebSocket connection refused or timeout errors.
- Fix: Verify
server.hostis0.0.0.0andhmr.hostmatches your access URL. - Docker Compose: Ensure port 5173 is exposed in your
docker-compose.ymlservice definition. - Firewall: On Ubuntu servers running UFW, allow port 5173 for development IPs only.
I have seen teams waste entire sprints assuming HMR was broken due to framework bugs when it was purely a networking misconfiguration. Always check the browser's Network tab for failed WebSocket requests before suspecting the plugin itself.
How should you optimize Vite config for Laravel projects in production?
Development convenience and production performance are opposing goals. Your vite.config.js must handle both contexts without maintaining separate configuration files. Vite uses environment variables and conditional logic to switch behaviors during npm run build.
| Configuration Aspect | Development Default | Production Optimization | Impact |
|---|---|---|---|
| Source Maps | Enabled (inline) | Disabled or hidden | Reduces bundle size 20-40% |
| CSS Minification | Disabled | esbuild/lightningcss | Faster parse times |
| Code Splitting | Minimal | Manual chunks + dynamic imports | Improves LCP/FCP |
| Asset Naming | Original filenames | Content hash ([name]-[hash]) | Enables aggressive caching |
| Tree Shaking | Partial | Full ESM analysis | Removes dead code |
For production builds, I always disable source maps unless actively debugging a live issue. Source maps can expose proprietary logic and significantly increase deployment artifact size. Add this to your config:
export default defineConfig(({ mode }) => ({
plugins: [ /* ... */ ],
build: {
sourcemap: mode === 'development',
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'axios', 'pinia'],
},
},
},
},
})); The manualChunks configuration separates third-party libraries from application code. Vendor bundles change infrequently, allowing browsers to cache them independently. On a recent e-commerce project, this simple change reduced repeat-visit payload by 65% because the Vue/Pinia chunk remained cached across deployments while only the app chunk updated.
Also consider enabling CSS code splitting if your application has distinct sections (public store vs. admin panel). Import CSS dynamically within route components rather than globally to avoid shipping unused styles. This aligns with technical SEO best practices where render-blocking resources directly affect search rankings.
What are common Vite plugin compatibility issues and fixes?
The Laravel ecosystem moves fast, and plugin versions frequently fall out of sync. As of mid-2026, ensure you are running compatible versions:
- laravel-vite-plugin: ^2.0 for Laravel 12.x (requires Vite 6.x)
- @vitejs/plugin-vue: ^6.0 for Vue 3.5+
- sass/sass-embedded: ^1.80 for modern Sass features
- tailwindcss: ^4.0 with
@tailwindcss/viteplugin
A recurring problem involves PostCSS and Tailwind CSS v4 integration. Tailwind v4 dropped the traditional postcss.config.js approach in favor of a dedicated Vite plugin. If you are upgrading from v3, remove PostCSS entirely and add:
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
tailwindcss(),
laravel({ /* ... */ }),
],
}); Another frequent issue occurs when mixing CommonJS and ESM packages. Some older npm dependencies still ship CJS-only builds. Vite handles this via pre-bundling, but occasionally you need to explicitly include problematic packages:
export default defineConfig({
optimizeDeps: {
include: ['legacy-package-name', 'another-cjs-lib'],
},
ssr: {
noExternal: ['problematic-ssr-package'],
},
}); If you encounter "Failed to resolve import" errors for aliases, verify that your resolve.alias paths are absolute. Relative aliases behave inconsistently across operating systems. Always prefix with / or use path.resolve(__dirname, 'resources/js') for cross-platform safety.
For developers building APIs alongside frontends, remember that Vite only processes frontend assets. Backend API routes remain untouched. If you are designing REST endpoints consumed by your Vite-built SPA, review Laravel API best practices to ensure your backend contract aligns with frontend expectations.
How does Vite compare to Laravel Mix for existing projects?
Many Nepal-based businesses and agencies still maintain Laravel Mix projects from 2020-2023. Migrating to Vite is not automatic and requires deliberate effort. Understanding the trade-offs helps decide whether migration is justified now or can wait.
In practice, I recommend Vite for all new Laravel 12 projects and major rewrites. The development feedback loop is measurably faster, especially for Vue/Livewire applications. However, for stable maintenance-mode projects generating revenue, the migration cost rarely justifies itself unless build times exceed 30 seconds or HMR reliability becomes a blocker.
If you do migrate, budget 2-4 hours for a typical medium-complexity application. The biggest time sink is usually rewriting custom Webpack plugins or loaders that have no direct Vite equivalent. Test thoroughly in staging before touching production; asset path changes have broken more deployments than I care to count.
Final Recommendations for Vite Configuration
Getting the Vite config for Laravel projects right requires treating it as infrastructure, not an afterthought. Start with the official Laravel preset, then customize incrementally based on actual pain points rather than anticipated needs. Always test HMR in your exact development environment (Docker, WSL, native) before assuming it works. For production, implement chunk splitting and disable source maps from day one — retrofitting these later invites deployment regressions.
Remember that Vite configuration is just one layer of a performant Laravel application. Asset bundling interacts with server configuration, CDN strategy, and caching headers. If you are building business-critical systems and want to ensure your entire stack is optimized, reach out to discuss your project. Whether you need a fresh build or help untangling a legacy asset pipeline, practical experience beats documentation guessing every time.

