Render Functions and Component Patterns
By Syncfusion®, Inc. · Last updated
h() and virtual nodes, props, children, components, slots, lists and conditions in render functions, templates vs render functions and JSX, and component patterns: presentational and container components, controlled components, headless components and generic components with generic="T", and Vue components as web components with defineCustomElement.
Below the Template
- Vue compiles every template into a render function.
- A render function returns virtual nodes (VNodes): small JavaScript objects that describe elements.
- You can write render functions yourself with
h(). - It is rarely needed. But it explains how Vue works, and helps when a template becomes awkward.
Simple lesson: templates turn into render functions that return VNodes. h() writes them by hand.
A Real-World Example
- Meera wants a heading component where the level comes from a prop:
h1,h2orh3. - In a template, that needs three
v-ifbranches. - A render function needs one line:
h("h" + level, text).
- What happened: one component made
h1,h2andh3tags. slots.default?.()rendered the text between the tags.
What You Will Learn
- What
h()and VNodes are. - How to pass props, attributes and children.
- How to render components and slots.
- How to render lists and conditions.
- When to pick a template or a render function, and what JSX is.
- Presentational and container components.
- Controlled components.
- Headless components.
- Generic components and Vue components as web components.
h() and VNodes
h(type, props, children)creates a VNode.- type = a tag name like
"button", or a component. - props = an object of attributes, props and listeners:
{ class: "big", onClick: fn }. - children = text, a VNode, or an array of VNodes.
- Listeners are written
onClick,onInput:on+ the event name with a capital.
Animation: a template line becomes an h() call, which becomes a VNode object, which becomes a real button element on the page
template render functionh("button", "Add") VNode page
Add
The compiler writes the render function for you. Vue turns VNodes into real elements.
Template → render function → VNode → real element.
Simple lesson: h(type, props, children). Listeners are onClick, onInput and so on.
setup() must return a function that returns VNodes: return () => h(...). Returning h(...) directly renders once and never updates.
Components, Slots, Lists and Conditions
- Component: pass the imported component as the type:
h(DishCard, { name: "Idli" }). - Slots out:
slots.default?.()renders what the parent passed. - Slots in: pass an object of functions:
h(Card, null, { default: () => "Hi", footer: () => "…" }). - Lists: use
array.map(...). Give each VNode akey. - Conditions: use
if,? :or&&in plain JavaScript.
Templates or Render Functions?
| Point | Template | Render function |
|---|---|---|
| Easy to read for HTML people | Yes | Less |
| Compiler speed-ups | Yes: Vue marks what can never change | No |
| Very dynamic output (tag names, many branches) | Clumsy | Easy |
| Used in | Almost all components | Library components, special cases |
- JSX = HTML-like syntax inside JavaScript. With a plugin, Vue can use it instead of
h(). React uses it by default. - Choose templates by default. Reach for
h()only when a template fights you.
Templates are faster than hand-written render functions in most cases. The template compiler adds hints that tell Vue which parts can never change, so updates skip them. You will see this in How Vue Renders.
Simple lesson: templates by default, render functions for unusual, very dynamic output.
Component Patterns
Presentational and Container Components
- Presentational component = only shows data from props and emits events. No fetching, no stores.
- Container component = loads data or reads stores, then passes it to presentational components.
- Presentational components are easy to reuse and to test.
Controlled Components
- A controlled component does not own its value. The parent does, through
v-model. - It shows the value it gets, and asks for changes with an event.
defineModel()from Props and Events makes this easy.
Headless Components
- A headless component has behaviour but no markup of its own.
- It passes state and functions out through a scoped slot. You saw this as a renderless component in Slots, Attributes and Provide/Inject.
- Big UI libraries use it, so you can style everything yourself.
- A composable is often a simpler way to share the same behaviour.
Generic Components
- You built a generic
PickListin TypeScript with Vue. Here it is as a pattern. - Use it when one component must work with any item type: a list, a table, a select box.
generic="T"gives the component a type parameter. Props and events useT.- In the JavaScript flavour, the same component uses plain runtime props.
Generic components shine in shared libraries: one DataTable or SelectBox for dishes, orders and staff, all fully typed. In app code, plain props are often enough.
Simple lesson: presentational for looks, containers for data, controlled for v-model, headless for behaviour, generic for any type.
Vue Components as Web Components
- A web component (custom element) is a tag the browser itself knows, such as
<order-badge>. - It works in any page: plain HTML, React, Angular, WordPress. No Vue knowledge needed on that page.
defineCustomElement(Component)turns a Vue component into one.customElements.define("order-badge", Element)registers it.- Props become HTML attributes. Emitted events become DOM
CustomEvents. - The styles go inside the element's shadow DOM, so they do not leak out.
- Use it for widgets other teams embed. For a whole Vue app, keep normal components.
- What happened: the page used
<order-badge>like any HTML tag. Each click sent a DOM event. - Vue puts the emitted arguments into
event.detailas an array.
Build a web component library with Vite's library mode. Set compilerOptions.isCustomElement when you use other custom elements inside Vue templates, so Vue does not try to resolve them as components.
TL;DR
- Render function = a function that returns VNodes. Templates compile into one.
- VNode = a plain object that describes an element or component.
h(type, props, children)= creates a VNode. Listeners areonClick.- Slots in
h()= out:slots.default?.(). In:{ default: () => ... }. - Lists and conditions =
mapwithkey, and plainif/? :. - Template vs
h()= templates by default. They are also faster. - Presentational / container = shows props / loads data and passes it down.
- Controlled / headless = parent owns the value / behaviour without markup.
generic="T"= a typed component for any item type (TypeScript).
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
h("button", { click: fn }) | Event names stay as in templates | Write onClick: fn | The click does nothing |
Returning VNodes from setup once | return h(...) is enough | Return a function: () => h(...) | The output never updates |
No key in mapped lists | Lists work without keys | Add key to each VNode | Wrong items update and warnings |
| Render functions everywhere | They are always more powerful | Templates by default | Harder code and no compiler speed-ups |
| Presentational component fetching data | Every component can fetch | Fetch in a container, pass props down | Hard to reuse and test |
generic in a JavaScript file | Generics work in JS | Use runtime props in JavaScript | A syntax error |
Check Yourself
Exercises
What to Remember
- Templates compile into render functions that return VNodes.
h(type, props, children): listeners areonX, lists usemapwith keys.- Use templates by default. They are clearer and faster.
- Presentational components show. Containers load. Controlled components follow
v-model. - Headless components share behaviour.
generic="T"types reusable components.
Next: How Vue Renders