Back to Guides

Vue.js Performance Optimization Tips

Best practices for building fast Vue applications with optimal Core Web Vitals scores.

Why Vue.js Performance Matters

Vue.js is known for being lightweight and performant, but poorly optimized Vue apps can still be slow. With Vue 3's Composition API and improved reactivity system, you have powerful tools for optimization.

Component Loading

Lazy load components to reduce initial bundle size.

Reactivity

Vue's reactivity can be optimized to reduce computations.

Bundle Size

Tree-shaking and code splitting keep bundles small.

Rendering

Optimize Virtual DOM updates and avoid unnecessary renders.

1. Use Vue 3 for Better Performance

Vue 3 offers significant performance improvements over Vue 2:

Vue 3 Performance Benefits

  • Faster rendering: Up to 2x faster Virtual DOM
  • Smaller bundle size: Tree-shakable, ~10KB lighter
  • Better TypeScript support: Improved developer experience
  • Composition API: More flexible and performant than Options API
  • Fragment support: No wrapper div needed

Migration Tips

  • Use Vue 3 for new projects
  • Migrate existing Vue 2 apps gradually with Vue Compat
  • Adopt Composition API for better code organization

2. Implement Lazy Loading

Load components only when needed:

Async Components with defineAsyncComponent

javascript
import { defineAsyncComponent } from 'vue';
const AsyncComponent = defineAsyncComponent(() =>
import('./components/HeavyComponent.vue')
);
// With loading and error states
const AsyncComponentWithStates = defineAsyncComponent({
loader: () => import('./components/HeavyComponent.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorDisplay,
delay: 200, // Show loading after 200ms
timeout: 3000, // Give up after 3s
});

Route-Based Code Splitting

javascript
// router/index.js
const routes = [
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue')
},
{
path: '/profile',
component: () => import('./views/Profile.vue')
}
];

3. Optimize Component Rendering

Reduce unnecessary re-renders and computations:

Use v-once for Static Content

html
<!-- This content never changes, render once -->
<div v-once>
<h1>{{ title }}</h1>
<p>{{ staticDescription }}</p>
</div>

Use v-memo to Skip Updates

html
<!-- Only re-render when dependency changes -->
<div v-memo="[userId]">
<UserProfile :id="userId" />
</div>

Computed Properties Over Methods

javascript
<script setup>
import { computed } from 'vue';
// Bad - recalculates on every render
const getTotal = () => {
return items.reduce((sum, item) => sum + item.price, 0);
};
// Good - cached until items change
const total = computed(() => {
return items.reduce((sum, item) => sum + item.price, 0);
});
</script>

4. Optimize List Rendering

Lists are common performance bottlenecks:

Always Use :key with v-for

html
<!-- Bad - no key -->
<div v-for="item in items">
<!-- Good - unique key -->
<div v-for="item in items" :key="item.id">

Virtual Scrolling for Long Lists

Use vue-virtual-scroller for lists with 100+ items:

html
<template>
<RecycleScroller
:items="items"
:item-size="50"
key-field="id"
>
<template v-slot="{ item }">
<div class="item">{{ item.name }}</div>
</template>
</RecycleScroller>
</template>

Avoid Nested v-for

  • Extract nested lists into separate components
  • Use computed properties to flatten data when possible
  • Consider pagination for very large datasets

5. Leverage Composition API

The Composition API provides better performance and code organization:

Reusable Composables

javascript
// composables/useFetch.js
import { ref } from 'vue';
export function useFetch(url) {
const data = ref(null);
const error = ref(null);
const loading = ref(false);
async function executeFetch() {
loading.value = true;
try {
const response = await fetch(url);
data.value = await response.json();
} catch (e) {
error.value = e;
} finally {
loading.value = false;
}
}
return { data, error, loading, executeFetch };
}

shallowRef and shallowReactive

Use shallow reactivity for large objects you don't need to deeply watch:

javascript
import { shallowRef } from 'vue';
// Only tracks changes to the array itself, not nested objects
const items = shallowRef([/* large array */]);

6. Reduce Bundle Size

Smaller bundles load faster:

Tree-Shaking

javascript
// Bad - imports entire lodash
import _ from 'lodash';
// Good - imports only what you need
import debounce from 'lodash/debounce';
// Better - use lodash-es for tree-shaking
import { debounce } from 'lodash-es';

Optimize Dependencies

Replace Heavy Libraries

Replace moment.js with day.js or date-fns to save ~200KB

Analyze Bundle

Use Vue Devtools to analyze bundle composition

Remove Unused Libraries

Remove unused UI component libraries and dependencies

Dynamic Imports

Use dynamic imports for large dependencies

Vite Configuration

javascript
// vite.config.js
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'vue-router'],
'ui': ['@/components/ui']
}
}
}
}
}

7. Server-Side Rendering with Nuxt

Use Nuxt 3 for optimal SSR performance:

Nuxt 3 Features

Automatic Code Splitting

Nuxt automatically splits your code for optimal loading

Image Optimization

Built-in image optimization with Nuxt Image module

Hybrid Rendering

Mix SSR and SSG for optimal performance per route

Auto-Imports

Auto-imports for components and composables

Nuxt Image Optimization

html
<template>
<NuxtImg
src="/hero.jpg"
width="800"
height="600"
format="webp"
loading="lazy"
/>
</template>

useFetch for Data Loading

javascript
<script setup>
const { data, pending, error } = await useFetch('/api/data');
</script>

8. Optimize Images and Assets

Images typically account for most of the page weight:

Lazy Load Images

javascript
<template>
<img
v-lazy="imageUrl"
alt="Description"
/>
</template>
<script setup>
// Use vue3-lazyload or vue-lazyload
import VueLazyload from 'vue3-lazyload';
</script>

Responsive Images

html
<img
srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, (max-width: 1000px) 800px, 1200px"
src="medium.jpg"
alt="Responsive image"
/>

9. Performance Monitoring

Measure and track performance over time:

Vue Devtools Performance Tab

  • Profile component render times
  • Identify slow components
  • Track re-renders

Web Vitals Tracking

javascript
import { onLCP, onINP, onCLS } from 'web-vitals';
onLCP(console.log);
onINP(console.log);
onCLS(console.log);

Build Analysis

  • Use rollup-plugin-visualizer to analyze bundle
  • Set up Lighthouse CI in your pipeline
  • Monitor bundle size with size-limit

10. Production Best Practices

Ensure optimal production builds:

  • Enable production mode: NODE_ENV=production
  • Remove Vue Devtools in production
  • Enable compression (gzip/Brotli)
  • Use a CDN for static assets
  • Implement proper caching headers
  • Minify HTML, CSS, and JavaScript
  • Remove console.log statements

Test Your Vue.js App

Get Vue-specific optimization recommendations powered by AI.

Analyze Your Vue Site