963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
3.9 KiB
3.9 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | |||||
|---|---|---|---|---|---|---|---|---|---|
| Always Include key Props When Rendering Lists in Render Functions | HIGH | Missing keys cause inefficient re-renders and state bugs in dynamic lists | best-practice |
|
Always Include key Props When Rendering Lists in Render Functions
Impact: HIGH - Omitting key props when rendering lists with h() or JSX causes Vue to use an inefficient "in-place patch" strategy, leading to performance issues and state bugs when list items have internal state or are reordered.
When rendering lists in render functions using .map(), always include a unique key prop on each item. This is the render function equivalent of using :key with v-for in templates.
Task Checklist
- Always provide a
keyprop when mapping over arrays in render functions - Use unique, stable identifiers (like
id) for keys, not array indices - Ensure keys are primitive values (strings or numbers)
- Never use the same key for different items in the same list
Incorrect:
import { h } from 'vue';
export default {
setup() {
const items = ref([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
]);
return () =>
h(
'ul',
// WRONG: No keys - causes inefficient patching
items.value.map((item) => h('li', item.name)),
);
},
};
// WRONG: Using array index as key when list can be reordered
export default {
setup() {
const todos = ref([...])
return () => (
<ul>
{todos.value.map((todo, index) => (
<TodoItem
key={index} // Bad: index changes when list reorders
todo={todo}
/>
))}
</ul>
)
}
}
Correct:
import { h } from 'vue';
export default {
setup() {
const items = ref([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
]);
return () =>
h(
'ul',
// CORRECT: Unique id as key
items.value.map((item) => h('li', { key: item.id }, item.name)),
);
},
};
// CORRECT: Using stable unique identifier as key
export default {
setup() {
const todos = ref([
{ id: 'a1', text: 'Learn Vue' },
{ id: 'b2', text: 'Build app' },
]);
return () => (
<ul>
{todos.value.map((todo) => (
<TodoItem
key={todo.id} // Good: stable unique identifier
todo={todo}
/>
))}
</ul>
);
},
};
import { h } from 'vue';
export default {
setup() {
const users = ref([]);
return () =>
h('div', [
h('h2', 'User List'),
h(
'ul',
users.value.map((user) =>
h('li', { key: user.email }, [
// email is unique
h('span', user.name),
h('span', ` (${user.email})`),
]),
),
),
]);
},
};
When Index Keys Are Acceptable
Using array indices as keys is acceptable ONLY when:
- The list is static and will never be reordered
- Items will never be inserted or removed from the middle
- Items have no internal component state
// Index is OK here: static list that never changes
const staticLabels = ['Name', 'Email', 'Phone'];
return () =>
h(
'tr',
staticLabels.map((label, index) => h('th', { key: index }, label)),
);
Why Keys Matter
Without keys, Vue uses an "in-place patch" strategy:
- It reuses DOM elements in place
- Updates element content to match new data
- This breaks when components have internal state or transitions
With proper keys:
- Vue tracks each item's identity
- Elements are moved, created, or destroyed correctly
- Component state is preserved during reorders