--- title: Always Include key Props When Rendering Lists in Render Functions impact: HIGH impactDescription: Missing keys cause inefficient re-renders and state bugs in dynamic lists type: best-practice tags: [vue3, render-function, v-for, performance, key] --- # 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 `key` prop 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:** ```javascript 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) ) ) } } ``` ```jsx // WRONG: Using array index as key when list can be reordered export default { setup() { const todos = ref([...]) return () => ( ) } } ``` **Correct:** ```javascript 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) ) ) } } ``` ```jsx // CORRECT: Using stable unique identifier as key export default { setup() { const todos = ref([ { id: 'a1', text: 'Learn Vue' }, { id: 'b2', text: 'Build app' } ]) return () => ( ) } } ``` ```javascript 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: 1. The list is static and will never be reordered 2. Items will never be inserted or removed from the middle 3. Items have no internal component state ```javascript // 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: 1. It reuses DOM elements in place 2. Updates element content to match new data 3. This breaks when components have internal state or transitions With proper keys: 1. Vue tracks each item's identity 2. Elements are moved, created, or destroyed correctly 3. Component state is preserved during reorders ## Reference - [Vue.js Render Functions - v-for](https://vuejs.org/guide/extras/render-function.html#v-for)