No articles found
Try different keywords or browse our categories
Fix: Vue build works locally but fails in production Error
Learn how to fix Vue build issues that occur in production but work locally. This comprehensive guide covers environment differences, optimization, and deployment best practices.
The ‘Vue build works locally but fails in production’ is a common deployment issue that occurs when Vue applications build and run successfully in development but encounter errors during production builds or after deployment. This problem typically stems from differences between development and production environments, optimization processes, or environment-specific code.
This comprehensive guide explains what causes this issue, why it happens, and provides multiple solutions to fix it in your Vue projects with clean code examples and directory structure.
What is the Production Build Issue?
The “Vue build works locally but fails in production” issue manifests when:
- Build succeeds locally but fails in CI/CD pipeline
- Application runs in development but breaks after production deployment
- Different behavior between local and production environments
- Optimization-related errors in production builds
- Environment-specific code causing failures
Common Error Manifestations:
- Build failures in production environment
- Runtime errors after deployment
- Different behavior between environments
- Asset loading failures in production
- Bundle size optimization issues
- Environment variable problems
Understanding the Problem
Production builds differ from development builds in several ways:
- Code optimization and minification
- Tree shaking and dead code elimination
- Different environment variables
- Production-specific configurations
- Asset path differences
- Strict error handling
Typical Vue Project Structure:
my-vue-app/
├── package.json
├── vite.config.js
├── src/
│ ├── main.js
│ ├── App.vue
│ ├── components/
│ │ └── HelloWorld.vue
│ ├── views/
│ │ └── Home.vue
│ ├── assets/
│ ├── styles/
│ └── utils/
├── public/
├── dist/
└── .env.production
Solution 1: Check Environment Variables
The most common cause is environment-specific variables not being properly configured.
❌ Without Proper Environment Handling:
// src/main.js - ❌ Hardcoded development values
import { createApp } from 'vue'
import App from './App.vue'
// ❌ Hardcoded development API URL
const API_URL = 'http://localhost:3000/api'
const app = createApp(App)
app.config.globalProperties.$apiUrl = API_URL
app.mount('#app')
✅ With Proper Environment Handling:
.env.production:
# Production environment variables
VUE_APP_API_URL=https://api.myapp.com
VUE_APP_ENV=production
VUE_APP_DEBUG=false
.env.development:
# Development environment variables
VUE_APP_API_URL=http://localhost:3000/api
VUE_APP_ENV=development
VUE_APP_DEBUG=true
src/main.js:
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
// ✅ Use environment variables
const API_URL = process.env.VUE_APP_API_URL || 'http://localhost:3000/api'
const app = createApp(App)
app.config.globalProperties.$apiUrl = API_URL
app.mount('#app')
Solution 2: Configure Build Tools Properly
Ensure build configuration works in both environments.
❌ Without Proper Build Configuration:
// vite.config.js - ❌ Development-only configuration
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
// ❌ Only development server configuration
port: 3000,
open: true
}
// ❌ Missing production build configuration
})
✅ With Proper Build Configuration:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
// ✅ Production build configuration
outDir: 'dist',
assetsDir: 'assets',
minify: 'terser', // ✅ Use terser for better minification
sourcemap: false, // ✅ Disable sourcemaps in production
rollupOptions: {
output: {
// ✅ Optimize chunking for production
manualChunks: {
vendor: ['vue', 'vue-router'],
ui: ['@vueuse/core'] // ✅ Separate UI library chunks
}
}
}
},
server: {
port: 3000,
open: true
}
})
Solution 3: Handle Environment-Specific Code
Ensure code works in both development and production environments.
❌ Environment-Specific Issues:
<template>
<div>
<p v-if="debugMode">Debug: {{ debugInfo }}</p> <!-- ❌ debugMode might not exist in production -->
<button @click="logDebug">Log Debug</button> <!-- ❌ Debug logging in production -->
</div>
</template>
<script>
export default {
data() {
return {
debugInfo: 'Debug info'
}
},
computed: {
debugMode() {
// ❌ This might not work in production
return process.env.NODE_ENV === 'development'
}
},
methods: {
logDebug() {
// ❌ Console logging in production
console.log('Debug info:', this.debugInfo)
}
}
}
</script>
✅ Environment-Aware Code:
<template>
<div>
<p v-if="isDebugMode">Debug: {{ debugInfo }}</p> <!-- ✅ Safe debug display -->
<button @click="logDebug">Log Debug</button>
</div>
</template>
<script>
export default {
data() {
return {
debugInfo: 'Debug info'
}
},
computed: {
isDebugMode() {
// ✅ Safe environment check
return process.env.VUE_APP_DEBUG === 'true'
}
},
methods: {
logDebug() {
// ✅ Conditional logging
if (this.isDebugMode) {
console.log('Debug info:', this.debugInfo)
}
}
}
}
</script>
Solution 4: Fix Asset Path Issues
Ensure asset paths work correctly in production builds.
❌ Incorrect Asset Paths:
<template>
<div>
<!-- ❌ Relative paths may break in production -->
<img src="../assets/logo.png" alt="Logo">
<link rel="stylesheet" href="styles/main.css">
</div>
</template>
✅ Correct Asset Paths:
<template>
<div>
<!-- ✅ Use public path or import for assets -->
<img src="@/assets/logo.png" alt="Logo">
<!-- ✅ Or use public folder assets -->
<img src="/logo.png" alt="Logo">
</div>
</template>
<script>
// ✅ Import assets for proper bundling
import logo from '@/assets/logo.png'
export default {
data() {
return {
logoPath: logo
}
}
}
</script>
Solution 5: Configure Base Path for Routing
Set the correct base path for Vue Router in production.
❌ Without Base Path Configuration:
// router/index.js - ❌ Missing base path
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
const router = createRouter({
history: createWebHistory(), // ❌ No base path specified
routes
})
export default router
✅ With Base Path Configuration:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
const router = createRouter({
history: createWebHistory(
process.env.NODE_ENV === 'production' ? '/my-app/' : '/' // ✅ Set base path
),
routes
})
export default router
Solution 6: Handle Production-Specific Dependencies
Ensure dependencies work in production environment.
package.json:
{
"name": "my-vue-app",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"build:prod": "vite build --mode production" // ✅ Production build script
},
"dependencies": {
"vue": "^3.0.0",
"vue-router": "^4.0.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"vite": "^4.0.0",
"terser": "^5.0.0" // ✅ Include terser for production builds
}
}
Solution 7: Implement Proper Error Handling
Add robust error handling for production environments.
src/utils/errorHandler.js:
// ✅ Production-ready error handling
export const productionErrorHandler = (error, context) => {
console.error('Production Error:', error)
// ✅ Send error to logging service
if (process.env.VUE_APP_ENV === 'production') {
sendErrorToService(error, context)
}
// ✅ Don't expose sensitive error details in production
return process.env.VUE_APP_ENV === 'development'
? error.message
: 'An error occurred. Please try again.'
}
const sendErrorToService = (error, context) => {
// ✅ Send error to external service like Sentry, LogRocket, etc.
// Implementation depends on your error tracking service
}
src/main.js:
import { createApp } from 'vue'
import App from './App.vue'
import { productionErrorHandler } from './utils/errorHandler'
const app = createApp(App)
// ✅ Global error handler for production
app.config.errorHandler = (err, instance, info) => {
productionErrorHandler(err, { component: instance, info })
}
app.mount('#app')
Working Code Examples
Complete Production-Ready Configuration:
// vite.config.js
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig(({ mode }) => {
// ✅ Load environment variables
const env = loadEnv(mode, process.cwd(), '')
return {
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
},
base: env.VITE_BASE_PATH || '/', // ✅ Configurable base path
build: {
outDir: 'dist',
assetsDir: 'assets',
minify: 'terser',
sourcemap: mode === 'development', // ✅ Sourcemaps only in development
cssCodeSplit: true,
rollupOptions: {
output: {
// ✅ Optimize chunking
manualChunks: {
vue: ['vue', 'vue-router'],
vendor: ['axios', 'lodash'] // ✅ Separate vendor chunks
},
// ✅ Clean chunk names
chunkFileNames: 'assets/chunks/[name].[hash].js',
entryFileNames: 'assets/[name].[hash].js',
assetFileNames: 'assets/[name].[hash].[ext]'
}
}
},
server: {
port: parseInt(env.VITE_PORT) || 3000,
host: env.VITE_HOST || 'localhost'
}
}
})
Environment-Aware Component:
<template>
<div class="production-component">
<h1>Environment: {{ environment }}</h1>
<p>Debug Mode: {{ isDebugMode }}</p>
<div v-if="isDebugMode" class="debug-info">
<h3>Debug Information</h3>
<pre>{{ debugData }}</pre>
</div>
<button @click="performAction">Perform Action</button>
<div v-if="error" class="error">
{{ errorMessage }}
</div>
</div>
</template>
<script>
export default {
name: 'ProductionComponent',
data() {
return {
error: null,
debugData: {
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
environment: process.env.NODE_ENV
}
}
},
computed: {
environment() {
return process.env.VUE_APP_ENV || 'development'
},
isDebugMode() {
return process.env.VUE_APP_DEBUG === 'true'
},
errorMessage() {
return this.error ? this.formatErrorMessage(this.error) : null
}
},
methods: {
async performAction() {
this.error = null
try {
// ✅ Simulate async operation
const result = await this.apiCall()
console.log('Action completed:', result)
} catch (err) {
this.error = err
this.logError(err)
}
},
async apiCall() {
// ✅ Mock API call
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.1 // 90% success rate
if (success) {
resolve({ data: 'Success', timestamp: Date.now() })
} else {
reject(new Error('API call failed'))
}
}, 500)
})
},
logError(error) {
// ✅ Conditional logging based on environment
if (this.isDebugMode) {
console.error('Error in production component:', error)
}
// ✅ Send to error tracking service in production
if (process.env.VUE_APP_ENV === 'production') {
this.sendErrorToService(error)
}
},
sendErrorToService(error) {
// ✅ Implementation for sending errors to external service
console.log('Sending error to service:', error.message)
},
formatErrorMessage(error) {
// ✅ Format error message based on environment
return process.env.VUE_APP_ENV === 'production'
? 'An error occurred. Please try again.'
: error.message
}
}
}
</script>
<style scoped>
.production-component {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.debug-info {
background-color: #f0f0f0;
padding: 15px;
border-radius: 4px;
margin: 15px 0;
}
.error {
color: #d32f2f;
background-color: #ffebee;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
</style>
Best Practices for Production Builds
1. Use Environment Variables Consistently
// ✅ Always use environment variables for configuration
const API_URL = process.env.VUE_APP_API_URL || 'https://api.example.com'
2. Optimize Bundle Size
# ✅ Analyze bundle size
npm install -g webpack-bundle-analyzer
vite build --analyze
3. Implement Proper Logging
// ✅ Conditional logging based on environment
if (process.env.NODE_ENV === 'development') {
console.log('Debug info:', data)
}
4. Test Production Builds Locally
# ✅ Build and test production version locally
npm run build
npx serve dist
Debugging Production Issues
Step 1: Enable Production Logging
// Add error logging in production
if (process.env.NODE_ENV === 'production') {
window.addEventListener('error', (event) => {
console.error('Production Error:', event.error)
// Send to error tracking service
})
}
Step 2: Check Build Output
# Verify build output
npm run build
ls -la dist/
Step 3: Test Locally
# Serve production build locally
npx serve dist
# Or use Python: python -m http.server -d dist
Step 4: Compare Environments
# Compare environment variables
echo "Local: $NODE_ENV"
echo "Production: $VUE_APP_ENV"
Common Mistakes to Avoid
1. Hardcoded Development URLs
// ❌ Don't hardcode URLs
const API_URL = 'http://localhost:3000/api'
// ✅ Use environment variables
const API_URL = process.env.VUE_APP_API_URL
2. Missing Production Dependencies
// ❌ Don't put production dependencies in devDependencies
{
"devDependencies": {
"axios": "^0.27.0" // ❌ Should be in dependencies
}
}
3. Development-Only Code in Production
// ❌ Don't include development-only code
if (process.env.NODE_ENV === 'development') {
// This code won't run in production
}
4. Incorrect Asset Paths
<!-- ❌ Don't use relative paths that might break -->
<img src="./assets/image.png">
Performance Considerations
1. Optimize for Production
// ✅ Use production-optimized configurations
build: {
minify: 'terser',
sourcemap: false,
cssCodeSplit: true
}
2. Implement Code Splitting
// ✅ Use dynamic imports for code splitting
const routes = [
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue')
}
]
Security Considerations
1. Sanitize Environment Variables
// ✅ Validate environment variables
const API_URL = process.env.VUE_APP_API_URL
if (!API_URL || !API_URL.startsWith('https://')) {
throw new Error('Invalid API URL')
}
2. Secure Sensitive Information
// ✅ Don't expose sensitive data in client-side code
// Use environment variables for configuration, not secrets
Testing Production Builds
1. Build and Serve Test
# Build for production
npm run build
# Serve locally to test
npx serve dist
2. Unit Test Environment Handling
import { mount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
describe('Environment Handling', () => {
it('should handle different environments', () => {
// Mock environment variables
process.env.VUE_APP_DEBUG = 'true'
const wrapper = mount(MyComponent)
expect(wrapper.vm.isDebugMode).toBe(true)
})
})
3. E2E Test Production Build
// Using Cypress or similar tools
// Test the production build to ensure functionality
Alternative Solutions
1. Use Different Build Modes
# Create different build configurations
npm run build -- --mode staging
npm run build -- --mode production
2. Implement Feature Flags
// Use feature flags for production vs development features
const FEATURES = {
DEBUG_PANEL: process.env.VUE_APP_DEBUG === 'true',
ANALYTICS: process.env.NODE_ENV === 'production'
}
Migration Checklist
- Verify environment variables are properly configured
- Check build configuration for production
- Test production build locally before deployment
- Ensure asset paths work in production
- Implement proper error handling for production
- Optimize bundle size for production
- Update documentation for team members
- Run comprehensive tests on production build
Conclusion
The ‘Vue build works locally but fails in production’ issue is typically caused by environment differences, configuration issues, or optimization-related problems. By following the solutions provided in this guide—whether through proper environment variable handling, build configuration, asset path management, or error handling—you can ensure your Vue applications work consistently across all environments.
The key is to understand the differences between development and production builds, implement proper environment management, and thoroughly test production builds before deployment. With proper configuration and testing, your Vue applications will function reliably in both development and production environments.
Remember to always test production builds locally, use environment variables consistently, implement proper error handling, and follow Vue’s best practices for production deployments to ensure your applications are robust and performant in real-world scenarios.
Related Articles
Fix: Vue app shows blank page after build
Learn how to fix Vue applications that show blank pages after production build. This comprehensive guide covers common causes, debugging, and best practices.
Fix: Build succeeded but site shows blank page in React Angular Vue
Complete guide to fix blank page issues after successful builds in React, Angular, and Vue applications. Learn how to debug and resolve blank page errors with practical solutions.
Fix: Angular app not working after build (production issue)
Learn how to fix Angular applications that don't work after production build. This comprehensive guide covers common production issues, optimization, and best practices.