Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Enforce Vue 3 best practices—Composition API, script setup, TypeScript, component boundaries, and reactivity patterns
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/render-functions.md
1---2title: Render Function Patterns and Performance3impact: MEDIUM4impactDescription: Render functions require explicit patterns for lists, events, v-model, and performance to stay correct and maintainable5type: best-practice6tags: [vue3, render-function, h, v-model, directives, performance, jsx]7---89# Render Function Patterns and Performance1011**Impact: MEDIUM** - Render functions are powerful but opt out of template compiler optimizations. Use them intentionally and apply the key patterns below to keep output correct and performant.1213## Task List1415- Prefer templates; use render functions only when templates cannot express the logic16- Always add stable keys when rendering lists with `h()`/JSX17- Use `withModifiers` / `withKeys` for event modifiers18- Implement `v-model` via `modelValue` + `onUpdate:modelValue`19- Apply custom directives with `withDirectives`20- Use functional components for stateless presentational UI2122## Prefer templates over render functions2324**BAD:**25```vue26<script setup>27import { h, ref } from 'vue'2829const count = ref(0)30const render = () => h('div', `Count: ${count.value}`)31</script>32```3334**GOOD:**35```vue36<script setup>37import { ref } from 'vue'3839const count = ref(0)40</script>4142<template>43<div>Count: {{ count }}</div>44</template>45```4647## Always add keys for list rendering4849**BAD:**50```javascript51import { h, ref } from 'vue'5253export default {54setup() {55const items = ref([{ id: 1, name: 'Apple' }])5657return () => h('ul',58items.value.map(item => h('li', item.name))59)60}61}62```6364**GOOD:**65```javascript66import { h, ref } from 'vue'6768export default {69setup() {70const items = ref([{ id: 1, name: 'Apple' }])7172return () => h('ul',73items.value.map(item => h('li', { key: item.id }, item.name))74)75}76}77```7879## Use `withModifiers` / `withKeys` for event modifiers8081**BAD:**82```javascript83import { h } from 'vue'8485export default {86setup() {87const handleClick = (e) => {88e.stopPropagation()89e.preventDefault()90}9192return () => h('button', { onClick: handleClick }, 'Click')93}94}95```9697**GOOD:**98```javascript99import { h, withModifiers, withKeys } from 'vue'100101export default {102setup() {103const handleClick = () => {}104const handleEnter = () => {}105106return () => h('div', [107h('button', {108onClick: withModifiers(handleClick, ['stop', 'prevent'])109}, 'Click'),110h('input', {111onKeyup: withKeys(handleEnter, ['enter'])112})113])114}115}116```117118## Implement `v-model` explicitly119120**BAD:**121```javascript122import { h, ref } from 'vue'123import CustomInput from './CustomInput.vue'124125export default {126setup() {127const text = ref('')128return () => h(CustomInput, { modelValue: text.value })129}130}131```132133**GOOD:**134```javascript135import { h, ref } from 'vue'136import CustomInput from './CustomInput.vue'137138export default {139setup() {140const text = ref('')141return () => h(CustomInput, {142modelValue: text.value,143'onUpdate:modelValue': (value) => { text.value = value }144})145}146}147```148149## Use `withDirectives` for custom directives150151**BAD:**152```javascript153import { h } from 'vue'154155const vFocus = { mounted: (el) => el.focus() }156157export default {158setup() {159return () => h('input', { 'v-focus': true })160}161}162```163164**GOOD:**165```javascript166import { h, withDirectives } from 'vue'167168const vFocus = { mounted: (el) => el.focus() }169170export default {171setup() {172return () => withDirectives(h('input'), [[vFocus]])173}174}175```176177## Prefer functional components for stateless UI178179**BAD:**180```javascript181import { h } from 'vue'182183export default {184setup() {185return () => h('span', { class: 'badge' }, 'New')186}187}188```189190**GOOD:**191```javascript192import { h } from 'vue'193194function Badge(props, { slots }) {195return h('span', { class: 'badge' }, slots.default?.())196}197198Badge.props = ['variant']199200export default Badge201```202