search
Vue star Featured

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.

person By Gautam Sharma
calendar_today January 1, 2026
schedule 12 min read
Vue Build Production Deployment Error Frontend CI/CD

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.

Gautam Sharma

About Gautam Sharma

Full-stack developer and tech blogger sharing coding tutorials and best practices

Related Articles

Vue

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.

January 2, 2026
Tutorials

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.

January 8, 2026
Angular

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.

January 2, 2026