
August 12, 2026
9 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Default Laravel Vite configurations bundle everything into a single application chunk, which quickly becomes a performance bottleneck as your project grows. Proper Laravel Vite config for custom asset bundles solves this by splitting vendor libraries, admin panels, and public-facing assets into separate, cacheable files. This approach is essential for any production Laravel application serving distinct user experiences or integrating heavy third-party libraries. If you are building complex systems like those described in my Laravel development services, mastering this configuration prevents shipping megabytes of unused code to every visitor.
vite.config.js and use manualChunks in the Rollup output options. This separates vendor dependencies, admin scripts, and public assets into distinct files, enabling granular caching and reducing initial page load size in production Laravel applications.How do you define multiple entry points in Laravel Vite config for custom asset bundles?
The foundation of any custom bundling strategy is moving beyond the single resources/js/app.js entry point. In Laravel 12 with Vite 6.x, the vite.config.js file accepts an array of inputs via the laravel() plugin. This tells Vite to treat each file as an independent root of the dependency graph, generating separate output hashes for each.
On a recent legal-tech portal I built, we needed a lightweight public site and a heavy admin dashboard with rich text editors and data tables. Bundling them together meant public visitors downloaded 400KB of admin-only JavaScript. Splitting the entry points solved this immediately.
// 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', // Public frontend 'resources/js/admin.js', // Admin panel 'resources/css/admin.css', // Admin styles 'resources/js/vendor-charts.js', // Heavy charting lib ], refresh: true, }), vue(), ], });Each entry point generates its own manifest record. When you use the @vite() directive in Blade, you reference only the specific entry needed for that page context:
@vite(['resources/css/app.css', 'resources/js/app.js'])on public pages@vite(['resources/css/admin.css', 'resources/js/admin.js'])in admin layouts@vite(['resources/js/vendor-charts.js'])only on analytics dashboards
This separation ensures that changes to admin code never invalidate the public browser cache, and vice versa. It also allows you to apply different optimization strategies per bundle, such as transpiling legacy admin code while keeping public code modern.
How do you split vendor chunks using manualChunks in Laravel Vite?
Entry points alone don't solve shared dependency bloat. If both app.js and admin.js import Vue and Axios, Vite will duplicate those libraries in both bundles by default. The manualChunks option in Rollup's output configuration gives you explicit control over how shared modules are extracted.
In practice, I configure this to isolate large, stable vendor libraries into their own chunks. This means updating Vue or Axios invalidates only the vendor chunk hash, not your application code hashes. For clients paying for bandwidth in Nepal where CDN costs matter, this distinction directly affects hosting bills.
// vite.config.js (inside defineConfig) build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules')) { // Separate Vue ecosystem into its own chunk if (id.includes('vue') || id.includes('@vue') || id.includes('pinia')) { return 'vendor-vue'; } // Separate heavy UI libraries if (id.includes('chart.js') || id.includes('quill')) { return 'vendor-heavy'; } // Everything else in node_modules return 'vendor-core'; } }, }, }, },This function runs for every module in the dependency graph. Returning a string name assigns that module to a named chunk. Modules returning the same name are bundled together. Be specific with your matching logic; overly broad patterns can accidentally group unrelated packages.
A common mistake is trying to match exact package names without accounting for nested paths. Always use includes() with partial paths rather than strict equality checks. Test your configuration with npm run build and inspect the public/build/assets/ directory to verify chunks are splitting as expected. The generated manifest.json will show which chunks each entry point depends on.
What is the difference between automatic and manual chunk splitting in Laravel Vite?
Understanding when to use automatic versus manual splitting prevents over-engineering. Vite's default code-splitting handles dynamic imports (import('./module')) automatically, creating lazy-loaded chunks at runtime. Manual chunking via manualChunks operates at build time, reorganizing static imports before output generation.
| Criteria | Automatic Splitting | Manual Chunking |
|---|---|---|
| Trigger | Dynamic import() calls | manualChunks function in Rollup config |
| Timing | Runtime (browser fetches on demand) | Build time (fixed output files) |
| Best for | Route components, modals, feature flags | Vendor libraries, shared utilities, admin/public separation |
| Cache impact | New chunk hash per lazy module change | Stable vendor hashes independent of app code |
| Configuration effort | Zero (works out of box) | Requires testing and maintenance |
| Risk | Waterfall requests if overused | Over-splitting increases HTTP requests |
For most Laravel projects, combine both approaches. Use manual chunking for stable vendor dependencies and architectural boundaries (admin vs public). Use automatic splitting for route-level code within those boundaries. This hybrid approach balances cache efficiency with request count.
When working on eCommerce platforms, I typically manually chunk payment gateway SDKs and product image galleries since they're large but only needed on specific pages. The core shop navigation stays in the main bundle. This pattern keeps category pages fast while still supporting rich checkout experiences.
How do you optimize CSS extraction for separate Laravel Vite bundles?
CSS deserves the same bundling discipline as JavaScript. By default, Vite extracts CSS per entry point, but shared component styles can still leak across bundles. Explicit CSS entry points combined with PostCSS configuration ensure each bundle carries only its required styles.
Define separate CSS entries alongside your JavaScript entries as shown earlier. For shared design tokens (colors, spacing, typography), create a resources/css/shared/tokens.css file imported at the top of both app.css and admin.css. Vite deduplicates identical content during extraction, so tokens don't double in size.
/* resources/css/app.css */ @import './shared/tokens.css'; @import './components/buttons.css'; @import './layouts/public.css'; /* resources/css/admin.css */ @import './shared/tokens.css'; @import './components/data-tables.css'; @import './layouts/dashboard.css';For Tailwind CSS users, configure separate content arrays per bundle if using multiple Tailwind configs. However, in most Laravel projects, a single Tailwind config scanning all Blade templates works fine because PurgeCSS removes unused classes at build time regardless of entry point. The real win is keeping admin-specific utility overrides out of public CSS.
Always verify extracted CSS sizes after build. If admin.css exceeds 100KB gzipped, audit for unused component imports. On a custom admin panel project, we reduced admin CSS from 180KB to 65KB simply by removing a global icon library import that was only needed in three components.
How do you debug and validate Laravel Vite custom bundle output?
Configuration mistakes in manualChunks silently produce broken builds or unexpected duplicates. Establish a validation routine before deploying. Never assume your chunking logic works correctly without inspection.
- Inspect the manifest: After
npm run build, readpublic/build/manifest.json. Each entry lists its direct JS/CSS files plus imported chunks. Verify vendor chunks appear as imports, not duplicated inline. - Check file sizes: Run
ls -lhS public/build/assets/sorted by size. Any single JS file over 250KB gzipped warrants investigation. Vendor chunks should be stable across deploys; app chunks should change frequently. - Test cache headers: Confirm your web server sets long
Cache-Controlmax-age for hashed assets. Vite's content hashing makes aggressive caching safe. Misconfigured headers negate all bundling benefits. - Profile in browser: Use Chrome DevTools Network tab with "Disable cache" unchecked. Load a public page, then an admin page. Verify no admin chunks load on public pages and vice versa.
- Validate source maps: Ensure
build.sourcemapis configured appropriately for your environment. Production should use'hidden'to generate maps without exposing them publicly, enabling error tracking services to decode stack traces.
A frequent gotcha occurs when upgrading Laravel or Vite versions. The laravel-vite-plugin API occasionally changes between major versions. Always check the plugin changelog before upgrading. In my experience maintaining multiple client sites on shared infrastructure, pinning exact plugin versions in package.json prevents surprise build failures during routine updates.
Implementing Sustainable Laravel Vite Config for Custom Asset Bundles
Effective Laravel Vite config for custom asset bundles is not a set-and-forget task. Treat it as living architecture that evolves with your application. Start with clear entry point separation, add manualChunks only when bundle analysis reveals actual waste, and validate every change against real production metrics. Document your chunking rationale in code comments so future maintainers understand why specific libraries are isolated.
If your current build ships unnecessary code to users or suffers from cache invalidation cascades, audit your Vite configuration today. Small adjustments often yield significant performance gains without application refactoring. For hands-on assistance optimizing your Laravel asset pipeline or broader Laravel architecture, reach out to discuss your project.

