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, h2 or h3.
  • In a template, that needs three v-if branches.
  • A render function needs one line: h("h" + level, text).
  • What happened: one component made h1, h2 and h3 tags.
  • slots.default?.() rendered the text between the tags.

What You Will Learn

  1. What h() and VNodes are.
  2. How to pass props, attributes and children.
  3. How to render components and slots.
  4. How to render lists and conditions.
  5. When to pick a template or a render function, and what JSX is.
  6. Presentational and container components.
  7. Controlled components.
  8. Headless components.
  9. 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 a key.
  • Conditions: use if, ? : or && in plain JavaScript.

Templates or Render Functions?

PointTemplateRender function
Easy to read for HTML peopleYesLess
Compiler speed-upsYes: Vue marks what can never changeNo
Very dynamic output (tag names, many branches)ClumsyEasy
Used inAlmost all componentsLibrary 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 PickList in 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 use T.
  • 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.detail as 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 are onClick.
  • Slots in h() = out: slots.default?.(). In: { default: () => ... }.
  • Lists and conditions = map with key, and plain if / ? :.
  • 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

MistakeWrong ideaCorrect ideaWhat goes wrong
h("button", { click: fn })Event names stay as in templatesWrite onClick: fnThe click does nothing
Returning VNodes from setup oncereturn h(...) is enoughReturn a function: () => h(...)The output never updates
No key in mapped listsLists work without keysAdd key to each VNodeWrong items update and warnings
Render functions everywhereThey are always more powerfulTemplates by defaultHarder code and no compiler speed-ups
Presentational component fetching dataEvery component can fetchFetch in a container, pass props downHard to reuse and test
generic in a JavaScript fileGenerics work in JSUse runtime props in JavaScriptA syntax error

Check Yourself

Exercises

What to Remember

  • Templates compile into render functions that return VNodes.
  • h(type, props, children): listeners are onX, lists use map with 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