0
0
Vueframework~8 mins

Loading states pattern in Vue - Performance & Optimization

Choose your learning style9 modes available
Performance: Loading states pattern
MEDIUM IMPACT
This pattern affects how quickly users see feedback during data fetching, impacting perceived load speed and interaction responsiveness.
Displaying a loading indicator while fetching data asynchronously
Vue
<template>
  <div :aria-busy="loading" aria-live="polite">
    <div v-if="loading" class="simple-loader" role="status" aria-label="Loading content"></div>
    <DataDisplay v-else :data="data" />
  </div>
</template>

<script setup>
import DataDisplay from './DataDisplay.vue'
import { ref, onMounted } from 'vue'

const loading = ref(true)
const data = ref(null)

async function fetchData() {
  // simulate data fetching
  return new Promise(resolve => setTimeout(() => resolve({}), 1000))
}

onMounted(async () => {
  data.value = await fetchData()
  loading.value = false
})
</script>

<style scoped>
.simple-loader {
  width: 2rem;
  height: 2rem;
  border: 0.25rem solid #ccc;
  border-top-color: #333;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}
</style>
Using a simple CSS loader reduces bundle size and rendering cost, enabling faster first paint and smoother interaction.
📈 Performance GainSingle reflow, non-blocking rendering, saves ~50kb in bundle size compared to heavy spinner component.
Displaying a loading indicator while fetching data asynchronously
Vue
<template>
  <div v-if="loading">
    <HeavySpinnerComponent />
  </div>
  <div v-else>
    <DataDisplay :data="data" />
  </div>
</template>

<script setup>
import HeavySpinnerComponent from './HeavySpinnerComponent.vue'
import DataDisplay from './DataDisplay.vue'
import { ref, onMounted } from 'vue'

const loading = ref(true)
const data = ref(null)

async function fetchData() {
  // simulate data fetching
  return new Promise(resolve => setTimeout(() => resolve({}), 1000))
}

onMounted(async () => {
  data.value = await fetchData()
  loading.value = false
})
</script>
Using a heavy spinner component blocks initial rendering and increases bundle size, delaying first paint and interaction readiness.
📉 Performance CostBlocks rendering for 200ms+ due to large spinner component and triggers multiple reflows when toggling loading state.
Performance Comparison
PatternDOM OperationsReflowsPaint CostVerdict
Heavy spinner componentHigh (many nodes)Multiple reflows on toggleHigh paint cost due to SVG and complex styles[X] Bad
Simple CSS loaderLow (few nodes)Single reflowLow paint cost with simple animation[OK] Good
Rendering Pipeline
Loading states trigger conditional rendering which affects style calculation and layout. Heavy components increase paint and composite times, while simple loaders minimize these costs.
Style Calculation
Layout
Paint
Composite
⚠️ BottleneckPaint and Composite stages due to complex spinner SVG or heavy component rendering.
Core Web Vital Affected
INP
This pattern affects how quickly users see feedback during data fetching, impacting perceived load speed and interaction responsiveness.
Optimization Tips
1Use simple CSS animations for loading indicators to minimize paint cost.
2Avoid heavy spinner components that increase bundle size and reflows.
3Always provide accessible loading feedback to improve perceived responsiveness.
Performance Quiz - 3 Questions
Test your performance knowledge
Which loading state pattern improves interaction responsiveness the most?
ANot showing any loading indicator until data loads
BUsing a heavy spinner component with SVG and many elements
CUsing a simple CSS spinner with minimal DOM nodes
DShowing a static image as a loading indicator
DevTools: Performance
How to check: Record a performance profile while toggling loading state. Look for long paint or composite tasks and multiple reflows.
What to look for: Check for long frames caused by heavy spinner rendering and verify minimal layout shifts with simple loaders.