Files
agent-skills/skills/vue-best-practices/reference/perf-avoid-component-abstraction-in-lists.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

4.8 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 Checklist

  • 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

Incorrect:

<!-- 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 -->

Correct:

<!-- 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

Reference