Directives

Full reference for all template directives supported by htmlc.

v-if / v-else-if / v-else

Renders the element only when the expression is truthy. Whitespace-only text nodes between branches are ignored.

<p v-if="role === 'admin'">Admin panel</p>
<p v-else-if="role === 'editor'">Editor view</p>
<p v-else>Read-only view</p>

Works on <template> elements too (renders children only, no wrapper element):

<template v-if="items.length > 0">
  <ul>
    <li v-for="item in items">{{ item }}</li>
  </ul>
</template>
<template v-else>
  <p>No items.</p>
</template>

v-show

Adds style="display:none" when the expression is falsy. The element is always rendered (unlike v-if). Merges with any existing style attribute.

<div v-show="isVisible">Visible when isVisible is truthy</div>

v-switch / v-case / v-default

Switch/case conditional (implements Vue RFC #482). Must be on a <template> element. Renders the first matching v-case branch.

<template v-switch="status">
  <div v-case="'active'">Active</div>
  <div v-case="'pending'">Pending approval</div>
  <div v-default>Unknown status</div>
</template>

v-for

Repeats the element for each item in the iterable. Supports arrays, maps, and objects.

<!-- Array -->
<li v-for="item in items">{{ item }}</li>

<!-- With index -->
<li v-for="(item, index) in items">{{ index }}: {{ item }}</li>

<!-- Object/map -->
<li v-for="(value, key) in obj">{{ key }}: {{ value }}</li>

<!-- Range (integer) -->
<li v-for="i in 5">{{ i }}</li>

Note: Map iteration order follows Go's reflect.MapKeys() — not insertion order. Sort your maps before passing them if order matters.

v-bind / :attr

Dynamically binds an HTML attribute to an expression. The shorthand is :.

<!-- Long form -->
<a v-bind:href="url">Link</a>

<!-- Shorthand -->
<a :href="url">Link</a>
<img :src="imageUrl" :alt="imageAlt" />

<!-- Boolean attributes: rendered only when truthy -->
<button :disabled="isLoading">Submit</button>

<!-- Class binding -->
<div :class="isActive ? 'active' : ''">...</div>

When passing props to a component, :propName evaluates the expression:

<Card :title="post.title" :author="post.author" />

:class — object and array syntax

<!-- Object: keys with truthy values are included -->
<div :class="{ active: isActive, disabled: !isEnabled }">...</div>

<!-- Array: non-empty string elements are included -->
<div :class="['btn', isPrimary ? 'primary' : '']">...</div>

<!-- Static class and :class are merged -->
<div class="card" :class="{ featured: post.featured }">...</div>

:style — object syntax

<!-- camelCase keys are converted to kebab-case in output -->
<p :style="{ fontSize: '14px', backgroundColor: theme.bg }">...</p>

Boolean attributes

When a bound attribute name is a recognised boolean attribute (disabled, checked, selected, readonly, required, multiple, autofocus, open), it is omitted entirely when the value is falsy, and rendered without a value when truthy.

<button :disabled="isLoading">Submit</button>
<!-- renders as <button> when isLoading is false -->
<!-- renders as <button disabled> when isLoading is true -->

v-bind="obj" — attribute spreading

When v-bind is used without an attribute name its value must evaluate to a map[string]any. Each entry is spread as an HTML attribute. class and style keys follow the same merge rules. Boolean attribute semantics apply per key.

<!-- Spread HTMX attributes -->
<button v-bind="htmxAttrs">Delete</button>

<!-- Spread props into a child component -->
<Card v-bind="cardProps" :title="override" />

On child components, explicit :prop bindings take precedence over keys in the spread map.

v-html

Sets the element's inner HTML to the expression value. The value is not HTML-escaped. Only use with trusted content.

<div v-html="renderedMarkdown"></div>

Warning: Never use v-html with user-supplied data — it can introduce XSS vulnerabilities.

v-text

Sets the element's text content to the expression value. HTML-escaped. Replaces all child nodes.

<span v-text="message"></span>
<!-- equivalent to -->
<span>{{ message }}</span>

v-pre

Skips all interpolation and directive processing for the element and all its descendants. Mustache syntax ({{ }}) is emitted literally. The v-pre attribute itself is stripped from the output.

<!-- This renders literally: {{ raw }} -->
<code v-pre>{{ raw }}</code>

Use v-pre to show template syntax as documentation or source examples without the engine treating it as an expression.

v-slot / #slot

Passes content into a named slot of a child component.

<!-- In Layout.vue -->
<header><slot name="header" /></header>
<main><slot /></main>

<!-- Usage -->
<Layout>
  <template #header>
    <nav><a href="/">Home</a></nav>
  </template>
  <p>Page content goes into the default slot.</p>
</Layout>

Scoped slots (slot props):

<!-- In List.vue -->
<ul>
  <li v-for="item in items">
    <slot :item="item">{{ item }}</slot>
  </li>
</ul>

<!-- Usage -->
<List :items="posts">
  <template #default="{ item }">
    <a :href="item.url">{{ item.title }}</a>
  </template>
</List>

<component :is>

Renders a component whose name is determined at runtime. The :is expression must evaluate to a non-empty string naming a registered component or a standard HTML element.

<!-- Resolve from a variable -->
<component :is="activeView" />

<!-- Inline string literal -->
<component :is="'Card'" :title="pageTitle">
  <p>slot content</p>
</component>

<!-- Switch between components in a loop -->
<div v-for="item in items">
  <component :is="item.type" :data="item" />
</div>
  • All attributes other than :is are forwarded as props.
  • Default and named slots work exactly as with static component tags.
  • If the resolved name is a standard HTML element (e.g. "div"), it is rendered as a plain tag, not looked up in the component registry.
  • :is is required; omitting it is a render error.

Stripped directives

These directives are parsed but produce no output — they are client-side only and have no meaning in a server-side renderer:

  • v-model — two-way binding
  • @event / v-on:event — event listeners
  • v-once — one-time render optimisation hint
  • v-memo — memoisation hint
  • v-cloak — FOUC prevention