Kritim Yantra
Jul 16, 2025
Picture this: You've just launched your Laravel+Vue app after months of work. But suddenly - slow loading, broken auth flows, and CSS flickering in production. 😱
These disasters are preventable. After building 50+ Laravel-Vue apps, I've compiled the ultimate checklist of professional practices most developers learn too late.
Problem: Hardcoding asset paths breaks in production
Solution: Always use Vite's asset helper:
// Wrong ❌
<img src="/public/images/logo.png">
// Right ✅
<img :src="import.meta.env.BASE_URL + 'images/logo.png'">
Pro Tip: For dynamic imports:
const imageUrl = new URL(`./assets/${name}.png`, import.meta.url).href
The Mistake: Assuming auth state persists between page reloads
The Fix: Implement immediate auth hydration:
// In your Pinia/Vuex store
const useAuthStore = defineStore('auth', {
state: () => ({
user: null,
ready: false // Track hydration state
}),
actions: {
async hydrate() {
try {
const { data } = await axios.get('/api/user');
this.user = data;
} catch {
this.user = null;
}
this.ready = true;
}
}
})
// In App.vue
onMounted(async () => {
await authStore.hydrate();
// Now render routes
});
Shocking Fact: Unoptimized images can bloat your build by 500%
Must-Do: Install vite-imagetools
:
npm install -D vite-imagetools
Then in vite.config.js
:
import { defineConfig } from 'vite';
import { imagetools } from 'vite-imagetools';
export default defineConfig({
plugins: [imagetools()],
})
Usage:
// Automatically generates optimized versions
const images = import.meta.globEager('./assets/*.{jpg,png}')
Critical Error: Exposing sensitive API routes
Solution: Double-protect your API:
// routes/api.php
Route::middleware(['auth:sanctum', 'verified'])->group(function () {
// Protected API routes
});
// Add this to VerifyCsrfToken middleware
protected $except = [
'api/*' // Disable CSRF for API routes
];
Vue 3 Gotcha: scoped
styles leak in production
Bulletproof Solution:
<style scoped>
/* Add this to prevent leaks */
:deep() .child-element {
color: red;
}
</style>
Common Mistake: Storing non-serializable data in Pinia
The Right Way:
defineStore('user', {
state: () => ({
// ✅ Safe:
user: null,
permissions: [],
// ❌ Dangerous:
socket: null,
timer: null
}),
actions: {
initRealtime() {
// Initialize non-serializable objects here
this.socket = io();
}
}
})
Must-Have Config:
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'pinia', 'axios'],
auth: ['@/stores/auth'],
dashboard: ['@/views/Dashboard']
}
}
},
chunkSizeWarningLimit: 1000 // in kB
}
})
Critical for Cache Busting:
// config/vite.php
return [
'build_path' => 'build',
];
Then in Blade:
<!DOCTYPE html>
<html>
<head>
@vite(['resources/js/app.js', 'resources/css/app.css'])
</head>
</html>
Shocking Fact: Unoptimized Vue components can slow your app 10x
Must-Do Pattern:
<template>
<!-- Lazy load heavy components -->
<Suspense>
<template #default>
<HeavyComponent />
</template>
<template #fallback>
<LoadingSpinner />
</template>
</Suspense>
</template>
<script setup>
import { defineAsyncComponent } from 'vue';
const HeavyComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
);
</script>
Why Most Apps Fail in Production:
// errorHandler.js
export function setupErrorHandling(app) {
app.config.errorHandler = (err) => {
axios.post('/log-error', {
message: err.message,
stack: err.stack,
component: err.vnode?.type.__name
});
console.error(err);
};
window.onerror = function(message, source, lineno, colno, error) {
axios.post('/log-error', {
type: 'window',
message, source, lineno, colno
});
};
}
// In app.js
import { setupErrorHandling } from './errorHandler';
setupErrorHandling(app);
Always sanitize Vue dynamic content:
<div v-html="sanitizeHtml(userContent)"></div>
Enable CSP headers in Laravel:
// app/Http/Middleware/TrustProxies.php
header("Content-Security-Policy: default-src 'self'");
Validate ALL API responses:
axios.interceptors.response.use(response => {
return MySchema.parse(response.data); // Use Zod or Yup
});
Add this to your package.json
:
"scripts": {
"build:inspect": "vite build --mode production --debug",
"analyze": "vite-bundle-visualizer"
}
Run:
npm run build:inspect
npm run analyze
This reveals exactly what's bloating your production bundle.
Which of these surprised you most? What hard-earned lessons would you add? Drop your war stories below! 👇
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google