
August 15, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
Your laravel vite setup either saves hours every week or quietly wastes them. Styles fail to load after deploy. Hot reload dies inside Docker. Production bundles ship at two megabytes because nobody touched vite.config.js after laravel new. Laravel 13 and Laravel 12 ship Vite by default, but the stock config is a starting point — not a finished pipeline. This guide walks through the config I use on production Laravel apps: entry points, the default dev-server port, HMR over Docker, build tuning, and when Mix still makes sense. If you are weighing the broader stack, see my notes on Laravel development for business-critical systems and how asset tooling fits the full delivery picture.
vite.config.js at the project root with laravel-vite-plugin, list every CSS and JS entry in input, and set server.hmr.host when developing in Docker or on a remote VM. The Vite dev server defaults to port 5173 per site.vite.dev server options.How do you set up the base Vite config for Laravel projects?
Laravel 12 and Laravel 13 include Vite out of the box. The default file is intentionally small. On real client projects, I expand it within the first sprint — extra admin bundles, Vue islands, or a separate vendor dashboard almost always appear.
The laravel-vite-plugin bridges Vite's ESM dev server and Laravel's @vite Blade directive. Official reference: the Laravel Vite documentation.
Starter config with Vue and path aliases
A typical starting point for a custom Laravel app with Vue 3 looks like this:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
import path from 'node:path';
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: {
'@': path.resolve(process.cwd(), 'resources/js'),
},
},
}); The input array is where most misconfiguration starts. Unlike Laravel Mix, Vite does not auto-discover files. Every CSS and JS entry you want compiled must be listed explicitly. I split entries by surface area on larger apps — public storefront, admin panel, vendor portal — so public pages never download admin JavaScript. That directly helps Core Web Vitals and Laravel SEO.
Set refresh: true so Blade template edits trigger a full reload. Livewire and Alpine-heavy apps need this. Pure API backends with a separate SPA can omit it. For Livewire-specific front-end choices, compare Livewire 3 versus Inertia before locking entry points.
On booking systems like Adventure Third Pole Trek, I keep admin Livewire assets in separate entries. Public trek pages stay lean. That pattern maps cleanly to any multi-role Laravel app.
Why is HMR not working when the Vite dev server uses port 5173?
This is the issue I see most often when onboarding developers. Hot Module Replacement needs a WebSocket from the browser back to Vite. On a native Linux or macOS setup, that usually just works. Inside Docker, WSL2, or a remote staging VM, the browser tries to reach the wrong host.
Per the official Vite server options, the default port is 5173. Laravel's dev script runs vite alongside php artisan serve. If HMR fails, the page loads but assets never update until you hard-refresh.
Fix server and HMR host settings
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
server: {
host: '0.0.0.0',
port: 5173,
strictPort: true,
hmr: {
host: 'localhost',
},
},
}); host: '0.0.0.0' binds Vite to all interfaces inside the container. hmr.host tells the browser which hostname to use for the WebSocket. Match it to how you open the app — localhost, myapp.test, or your staging domain.
- Symptom: Styles and scripts never hot-reload.
- Check: Browser console for WebSocket errors on port 5173.
- Docker: Expose port 5173 in
docker-compose.yml. See Docker Compose for local Laravel. - Sail: Use Laravel Sail port forwarding conventions if you run the full stack in containers.
- Firewall: On Ubuntu with UFW, allow 5173 only for dev IPs — never on production.
I have watched teams burn days blaming Laravel when the fix was three lines in server.hmr. Open DevTools, filter Network by WS, and confirm the handshake succeeds before you touch application code.
How should you optimize Vite config for Laravel production builds?
Development speed and production weight pull in opposite directions. One vite.config.js should serve both via mode from defineConfig. Run npm run build in CI before deploy — the same pattern I use in GitLab CI pipelines for Laravel.
| Setting | Development | Production | Why it matters |
|---|---|---|---|
| Source maps | Inline or enabled | Off or hidden | Smaller artifacts, less exposed logic |
| CSS minify | Off | esbuild or lightningcss | Faster parse, better LCP |
| Code splitting | Minimal | manualChunks + dynamic import() | Cache vendor libs across deploys |
| File names | Original | Content hash suffix | Long-cache static assets safely |
| Tree shaking | Partial | Full ESM analysis | Drops dead imports |
Production build block
export default defineConfig(({ mode }) => ({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
build: {
sourcemap: mode === 'development',
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'axios', 'pinia'],
},
},
},
},
})); I disable production source maps unless I am actively debugging a live incident. They inflate deploy size and can expose business logic. manualChunks isolates libraries that change rarely. Browsers cache the vendor file across releases while only the app chunk updates.
Split CSS by route or layout when admin styles differ from the storefront. Dynamic import() for admin-only Vue pages keeps public CSS off checkout flows. That pairs well with speed optimization work on Nepali e-commerce sites. For a live example of a lean Laravel storefront, see Quick And Easy Nepalese Grocery.
What are common Vite plugin compatibility issues in Laravel?
The Laravel front-end stack moves quickly. Pin versions in package.json and upgrade deliberately. Target these anchors in 2026:
- Node.js: 26 LTS for local dev and CI (24 LTS still supported)
- npm: 12
- Vite: 8.x
- Laravel: 13.x on PHP 8.3+, or Laravel 12 on PHP 8.2+
- Vue: 3.x with
@vitejs/plugin-vuematched to your Vite major
Tailwind CSS v4 with the Vite plugin
Tailwind v4 prefers the dedicated Vite plugin over a standalone PostCSS pipeline. Remove legacy PostCSS config and add:
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
tailwindcss(),
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
}); For Blade-only interactivity without Vue, Alpine.js with Blade keeps the JS surface small. Fewer plugins means fewer version conflicts.
Legacy CommonJS packages
Older npm packages still ship CommonJS-only builds. Vite pre-bundles most of them. When imports fail, force inclusion:
export default defineConfig({
optimizeDeps: {
include: ['legacy-package-name'],
},
}); Alias paths must resolve to absolute locations. Use path.resolve(process.cwd(), 'resources/js') instead of relative strings that break across Windows and Linux CI runners. Validate JSON config during setup with the JSON formatter tool if you generate manifest snippets or test fixtures by hand.
Vite only compiles front-end assets. Your API layer stays in PHP. If a Vue or Alpine SPA consumes Laravel endpoints, align contracts with Laravel API best practices before you wire fetch calls in resources/js.
How does Vite compare to Laravel Mix for existing projects?
Many agencies still maintain Mix projects from 2020–2023. Migration is manual. The trade-off is dev speed versus migration cost.
Read the dedicated comparison at Laravel Mix versus Vite and the broader Vite versus Webpack overview. For custom multi-entry setups, see Laravel Vite config for custom asset bundles.
I recommend Vite for every new Laravel 13 or Laravel 12 project. HMR feedback is noticeably faster on Vue and Livewire apps. Stable revenue-generating Mix apps can wait unless build times or developer friction justify the switch.
Budget two to four hours for a medium-complexity migration. Custom Webpack loaders without Vite equivalents eat most of that time. Test in staging first — wrong asset paths break more deploys than PHP bugs. Follow the Laravel production deployment checklist and wire builds through npm scripts in CI. New Laravel 12 apps should skim what changed in Laravel 12 before changing defaults.
How do you wire Vue into a Laravel Vite pipeline?
Most Laravel apps I ship pair Blade with Vue islands or a small SPA shell. Install Vue 3, add @vitejs/plugin-vue, and register Vue in your JS entry:
import { createApp } from 'vue';
import ExampleComponent from './components/ExampleComponent.vue';
const app = createApp({});
app.component('example-component', ExampleComponent);
app.mount('#app'); Mount points live in Blade. Pass initial data via @json() props, not hard-coded globals. The full walkthrough lives in Vue with Laravel setup guide. For greenfield work, web development services often include asset pipeline setup alongside backend delivery.
Key Takeaways
- List every CSS and JS entry in
laravel-vite-plugininput— Vite will not discover files for you. - Set
server.hostto0.0.0.0and matchhmr.hostto your browser URL when using Docker or remote dev. - Remember the default Vite dev port is 5173; expose it in Compose and restrict it on servers.
- Disable production source maps and split vendor chunks before launch — retrofitting is painful.
- Use Vite for new Laravel 12 and 13 projects; migrate Mix apps only when build pain justifies the hours.
- Run
npm run buildin CI and verifypublic/build/manifest.jsonexists before every deploy.
People Also Ask
What is the default Vite dev server port for Laravel?
Vite uses port 5173 by default. Laravel's composer run dev script starts Vite alongside the PHP server. Override it with server.port in vite.config.js if 5173 is taken locally.
Where does Laravel store Vite build output?
Production builds write hashed CSS and JS files to public/build plus a manifest.json file. Laravel reads that manifest when rendering @vite directives. Commit the build output only if your deploy server lacks Node.js — otherwise build in CI.
Can you use Vite with Livewire and Alpine without Vue?
Yes. Keep a single JS entry that imports Alpine or Livewire hooks. You still benefit from fast HMR for CSS and small JS changes. Enable refresh: true so Blade edits reload automatically.
Do you need Node.js on the production Laravel server?
No, if CI runs npm run build and deploys the public/build artifacts. Many VPS setups I maintain compile assets in GitLab CI and rsync only PHP plus built assets — the same approach described in zero-downtime Deployer releases.
Ship a Laravel asset pipeline that stays fast after launch
Treat your laravel vite config as infrastructure, not a one-time scaffold. Start from Laravel defaults, then tune entries, HMR, and production chunks when real pain appears — not on day one speculation. Test hot reload in your actual dev environment before assuming it works. Build in CI, cache vendor chunks, and keep admin assets off public pages.
Vite is one layer in a performant stack. It interacts with CDN headers, PHP-FPM, and page-weight budgets. If you want help auditing an existing pipeline or migrating from Mix, contact us about your Laravel project. You can also reach out directly with your current vite.config.js — practical review beats guessing from docs alone.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

