Files
shiftcraft/.claude/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md
2026-04-17 23:26:01 +00:00

4.6 KiB

title, impact, impactDescription, type, tags
title impact impactDescription type tags
Avoid Excessive Component Abstraction in Large Lists MEDIUM Each component instance has memory and render overhead - abstractions multiply this in lists efficiency
vue3
performance
components
abstraction
lists
optimization

Avoid Excessive Component Abstraction in Large Lists

Impact: MEDIUM - Component instances are more expensive than plain DOM nodes. While abstractions improve code organization, unnecessary nesting creates overhead. In large lists, this overhead multiplies - 100 items with 3 levels of abstraction means 300+ component instances instead of 100.

Don't avoid abstraction entirely, but be mindful of component depth in frequently-rendered elements like list items.

Task List

  • Review list item components for unnecessary wrapper components
  • Consider flattening component hierarchies in hot paths
  • Use native elements when a component adds no value
  • Profile component counts using Vue DevTools
  • Focus optimization efforts on the most-rendered components

BAD:

<!-- BAD: Deep abstraction in list items -->
<template>
  <div class="user-list">
    <!-- For 100 users: Creates 400 component instances -->
    <UserCard v-for="user in users" :key="user.id" :user="user" />
  </div>
</template>

<!-- UserCard.vue -->
<template>
  <Card>  <!-- Wrapper component #1 -->
    <CardHeader>  <!-- Wrapper component #2 -->
      <UserAvatar :src="user.avatar" />  <!-- Wrapper component #3 -->
    </CardHeader>
    <CardBody>  <!-- Wrapper component #4 -->
      <Text>{{ user.name }}</Text>
    </CardBody>
  </Card>
</template>

<!-- Each UserCard creates: Card + CardHeader + CardBody + UserAvatar + Text
     100 users = 500+ component instances -->

GOOD:

<!-- GOOD: Flattened structure in list items -->
<template>
  <div class="user-list">
    <!-- For 100 users: Creates 100 component instances -->
    <UserCard v-for="user in users" :key="user.id" :user="user" />
  </div>
</template>

<!-- UserCard.vue - Flattened, uses native elements -->
<template>
  <div class="card">
    <div class="card-header">
      <img :src="user.avatar" :alt="user.name" class="avatar" />
    </div>
    <div class="card-body">
      <span class="user-name">{{ user.name }}</span>
    </div>
  </div>
</template>

<script setup>
defineProps({
  user: Object
})
</script>

<style scoped>
/* Styles that would have been in Card, CardHeader, etc. */
.card { /* ... */ }
.card-header { /* ... */ }
.card-body { /* ... */ }
.avatar { /* ... */ }
</style>

When Abstraction Is Still Worth It

<!-- Component abstraction is valuable when: -->

<!-- 1. Complex behavior is encapsulated -->
<UserStatusIndicator :user="user" />  <!-- Has logic, tooltips, etc. -->

<!-- 2. Reused outside of the hot path -->
<Card>  <!-- OK to use in one-off places, not in 100-item lists -->

<!-- 3. The list itself is small -->
<template v-if="items.length < 20">
  <FancyItem v-for="item in items" :key="item.id" />
</template>

<!-- 4. Virtualization is used (only ~20 items rendered at once) -->
<RecycleScroller :items="items">
  <template #default="{ item }">
    <ComplexItem :item="item" />  <!-- OK - only 20 instances exist -->
  </template>
</RecycleScroller>

Measuring Component Overhead

// In development, profile component counts
import { onMounted, getCurrentInstance } from 'vue'

onMounted(() => {
  const instance = getCurrentInstance()
  let count = 0

  function countComponents(vnode) {
    if (vnode.component) count++
    if (vnode.children) {
      vnode.children.forEach(child => {
        if (child.component || child.children) countComponents(child)
      })
    }
  }

  // Use Vue DevTools instead for accurate counts
  console.log('Check Vue DevTools Components tab for instance counts')
})

Alternatives to Wrapper Components

<!-- Instead of a <Button> component for styling: -->
<button class="btn btn-primary">Click</button>

<!-- Instead of a <Text> component: -->
<span class="text-body">{{ content }}</span>

<!-- Instead of layout wrapper components in lists: -->
<div class="flex items-center gap-2">
  <!-- content -->
</div>

<!-- Use CSS classes or Tailwind instead of component abstractions for styling -->

Impact Calculation

List Size Components per Item Total Instances Memory Impact
100 items 1 (flat) 100 Baseline
100 items 3 (nested) 300 ~3x memory
100 items 5 (deeply nested) 500 ~5x memory
1000 items 1 (flat) 1000 High
1000 items 5 (deeply nested) 5000 Very High