No articles found
Try different keywords or browse our categories
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.
The ‘Vue app shows blank page after build’ is a common production deployment issue that occurs when Vue applications build successfully but display a blank page when accessed in the browser. This problem typically stems from JavaScript errors, asset loading failures, or configuration issues that prevent the Vue application from mounting properly.
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 Blank Page Issue?
The “Vue app shows blank page after build” issue occurs when:
- The application builds successfully but shows a blank white screen
- Vue application fails to mount in the browser
- JavaScript errors prevent the app from loading
- Assets fail to load after build
- Routing configuration issues in production
Common Error Manifestations:
- Completely blank white page
- HTML loads but Vue app doesn’t mount
- Console errors preventing app initialization
- Assets (CSS, JS) not loading after build
- Routing issues in production environment
Understanding the Problem
Blank page issues typically occur due to:
- JavaScript runtime errors
- Asset path configuration problems
- Missing or incorrect base href
- Build optimization issues
- Environment-specific code
- Third-party library conflicts
Typical Vue Project Structure:
my-vue-app/
├── package.json
├── vite.config.js
├── src/
│ ├── main.js
│ ├── App.vue
│ ├── components/
│ │ └── HelloWorld.vue
│ ├── router/
│ │ └── index.js
│ ├── assets/
│ └── styles/
├── public/
├── dist/
└── index.html
Solution 1: Check Browser Console for Errors
The first step is to identify the root cause by checking browser console errors.
❌ Without Error Checking:
// ❌ Don't ignore console errors
// Build succeeds but app shows blank page
// No visible errors in build output
✅ With Proper Error Checking:
Check Console:
# Open browser developer tools
# Check Console tab for JavaScript errors
# Look for Vue mounting errors
# Check Network tab for failed asset requests
Example Console Error:
Uncaught TypeError: Cannot read property 'mount' of undefined
at main.js:10
Solution 2: Verify App Mounting in main.js
Ensure the Vue application is properly mounted.
❌ Without Proper Mounting:
// src/main.js - ❌ Missing proper mounting
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// ❌ App not mounted to DOM element
// Missing app.mount('#app') call
✅ With Proper Mounting:
main.js:
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import { router } from './router'
const app = createApp(App)
// ✅ Mount to existing DOM element
app.use(router)
app.mount('#app')
// ✅ Add error handling for production
app.config.errorHandler = (err, instance, info) => {
console.error('Vue Error:', err)
console.error('Component:', instance)
console.error('Info:', info)
}
public/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="/favicon.ico">
<title>My Vue App</title>
</head>
<body>
<!-- ✅ Ensure app element exists -->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
Solution 3: Check Asset Loading Issues
Verify that all assets are loading correctly after build.
❌ With Asset Path Issues:
<!-- public/index.html - ❌ Wrong asset paths -->
<head>
<!-- ❌ Relative paths may break in production -->
<link rel="stylesheet" href="src/styles/main.css">
<script src="src/main.js"></script>
</head>
✅ With Correct Asset Paths:
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="/favicon.ico">
<title>My Vue App</title>
<!-- ✅ Assets will be injected automatically by build process -->
</head>
<body>
<div id="app"></div>
<!-- ✅ Built files will be auto-injected -->
</body>
</html>
Solution 4: Configure Base Href for Routing
Set the correct base href for Vue Router in production.
❌ Without Base Href:
// router/index.js - ❌ Missing base configuration
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 Href:
// 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
vite.config.js:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
base: process.env.NODE_ENV === 'production' ? '/my-app/' : '/', // ✅ Configure base path
build: {
outDir: 'dist',
assetsDir: 'assets'
}
})
Solution 5: Handle Environment-Specific Code
Ensure environment-specific code doesn’t break production builds.
❌ With Environment Issues:
// src/main.js - ❌ Development-specific code
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// ❌ Hardcoded development URLs
const API_URL = 'http://localhost:3000/api'
// ❌ Development-only features
if (process.env.NODE_ENV === 'development') {
// This might cause issues if referenced elsewhere
app.config.devtools = true
}
app.mount('#app')
✅ With Environment Handling:
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// ✅ Use environment variables
const API_URL = process.env.VUE_APP_API_URL || 'https://api.example.com'
// ✅ Safe environment-specific configuration
if (process.env.NODE_ENV === 'development') {
app.config.performance = true
}
app.mount('#app')
Solution 6: Fix Build Optimization Issues
Address issues caused by build optimization and minification.
❌ With Optimization Issues:
// vite.config.js - ❌ Aggressive optimization causing issues
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}
})
✅ With Safe Optimization:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
outDir: 'dist',
assetsDir: 'assets',
minify: 'terser',
terserOptions: {
compress: {
// ✅ Be careful with aggressive optimization
drop_console: false, // ✅ Keep console for debugging
drop_debugger: false
}
},
rollupOptions: {
output: {
// ✅ Optimize chunking safely
manualChunks: {
vendor: ['vue', 'vue-router']
}
}
}
}
})
Solution 7: Implement Proper Error Boundaries
Add error boundaries to catch and handle errors gracefully.
src/components/ErrorBoundary.vue:
<template>
<div v-if="hasError">
<h2>Something went wrong.</h2>
<button @click="handleReset">Try again</button>
</div>
<slot v-else />
</template>
<script>
import { ref } from 'vue'
export default {
name: 'ErrorBoundary',
setup() {
const hasError = ref(false)
const handleReset = () => {
hasError.value = false
window.location.reload()
}
// ✅ Catch errors in child components
const handleError = (err) => {
hasError.value = true
console.error('Error caught by boundary:', err)
}
return {
hasError,
handleReset
}
},
errorCaptured(err) {
this.handleError(err)
return false
}
}
</script>
src/main.js:
import { createApp } from 'vue'
import App from './App.vue'
import ErrorBoundary from './components/ErrorBoundary.vue'
const app = createApp(App)
// ✅ Wrap app in error boundary
app.component('ErrorBoundary', ErrorBoundary)
app.mount('#app')
Working Code Examples
Complete Production-Ready Configuration:
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import { router } from './router'
// ✅ Production-ready main.js with error handling
const app = createApp(App)
// ✅ Add global error handlers
app.config.errorHandler = (err, instance, info) => {
console.error('Global error:', err)
console.error('Component:', instance)
console.error('Info:', info)
// ✅ Send error to logging service in production
if (process.env.NODE_ENV === 'production') {
sendErrorToService(err, { component: instance, info })
}
}
// ✅ Handle unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason)
})
app.use(router)
app.mount('#app')
function sendErrorToService(error, context) {
// ✅ Implementation for sending errors to external service
console.log('Sending error to service:', error.message)
}
Production-Ready App Component:
<template>
<div id="app">
<div v-if="loading" class="loading">
Loading...
</div>
<div v-else-if="error" class="error">
<h2>Error loading application</h2>
<p>{{ error.message }}</p>
<button @click="retryLoad">Retry</button>
</div>
<router-view v-else />
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
export default {
name: 'App',
setup() {
const loading = ref(true)
const error = ref(null)
const router = useRouter()
const initApp = async () => {
try {
// ✅ Initialize app logic here
await initializeApp()
} catch (err) {
error.value = err
console.error('App initialization error:', err)
} finally {
loading.value = false
}
}
const initializeApp = async () => {
// ✅ App initialization logic
// Load user data, settings, etc.
}
const retryLoad = () => {
error.value = null
loading.value = true
initApp()
}
onMounted(() => {
initApp()
})
return {
loading,
error,
retryLoad
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.loading, .error {
padding: 20px;
text-align: center;
}
.error {
color: #e74c3c;
}
.error button {
margin-top: 10px;
padding: 8px 16px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
Best Practices for Production Builds
1. Always Test Production Builds Locally
# ✅ Build and test locally before deployment
npm run build
npx serve dist
2. Use Environment Variables
// ✅ Always use environment variables for configuration
const API_URL = process.env.VUE_APP_API_URL
3. Implement Proper Error Handling
// ✅ Add global error handlers
app.config.errorHandler = (err, instance, info) => {
console.error('Error:', err)
}
4. Optimize Bundle Size Safely
# ✅ Analyze bundle size
npm run build --analyze
Debugging Steps
Step 1: Check Console Errors
# Open browser dev tools
# Check Console tab for errors
# Check Network tab for failed requests
Step 2: Verify Build Output
# Check if build files exist
ls -la dist/
# Verify index.html exists and has correct content
cat dist/index.html
Step 3: Test Locally
# Serve production build locally
npx serve dist
# Or use Python: python -m http.server -d dist
Step 4: Check Asset Loading
# In browser dev tools Network tab
# Check if CSS and JS files are loading
# Look for 404 errors
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 App Mounting
// ❌ Don't forget to mount the app
const app = createApp(App)
// Missing app.mount('#app')
3. Incorrect Base Href
// ❌ Wrong base href
history: createWebHistory('/wrong-path/')
4. Development-Only Code in Production
// ❌ Don't include development-only code
if (process.env.NODE_ENV === 'development') {
// This might cause issues if referenced elsewhere
}
Performance Considerations
1. Optimize for Production
// ✅ Use production-optimized configurations
build: {
minify: 'terser',
sourcemap: false
}
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 Error Handling
import { mount } from '@vue/test-utils'
import App from '@/App.vue'
describe('App Error Handling', () => {
it('should handle initialization errors', () => {
// Mock error during initialization
const wrapper = mount(App)
expect(wrapper.exists()).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 staging build for testing
npm run build --mode staging
2. Implement Feature Flags
// Use feature flags for production vs development features
const FEATURES = {
DEBUG_PANEL: process.env.VUE_APP_DEBUG === 'true'
}
Migration Checklist
- Verify app mounts to correct DOM element
- Check browser console for errors
- Test production build locally
- Verify asset paths work correctly
- Ensure environment variables are configured
- Check routing configuration
- Implement proper error handling
- Update documentation for team members
Conclusion
The ‘Vue app shows blank page after build’ issue is typically caused by JavaScript errors, asset loading problems, or configuration issues that prevent the Vue application from mounting properly. By following the solutions provided in this guide—whether through proper error checking, correct app mounting, asset path configuration, or environment handling—you can ensure your Vue applications work reliably in production environments.
The key is to always test production builds locally, implement proper error handling, use environment variables consistently, and follow Vue’s best practices for production deployments. With proper configuration and testing, your Vue applications will load correctly and provide an excellent user experience.
Remember to check browser console errors first, verify app mounting, test production builds locally, and implement robust error handling to create reliable and maintainable Vue applications that work consistently across all environments.
Related Articles
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.
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.