SyntaxStudy
Sign Up
Vue.js Dynamic Templates and v-html
Vue.js Beginner 1 min read

Dynamic Templates and v-html

While `{{ }}` interpolation HTML-escapes its output for safety, there are legitimate cases where you need to render raw HTML — such as CMS-provided content you already trust and sanitise server-side. The `v-html` directive injects raw HTML into an element's `innerHTML`. Use it cautiously and never with user-supplied unsanitised input, as it creates XSS vulnerabilities. Vue templates fully support JavaScript expressions inside binding attributes and interpolations: ternary operators, method calls, template literals, array methods, and even short-circuit evaluation all work. However, complex logic should live in `computed` properties or component methods rather than bloating the template. Vue also provides the `v-pre` directive to skip compilation of an element and its children, which is useful when you want to display raw mustache syntax as literal text in documentation components. The `v-cloak` directive can be combined with CSS to hide un-compiled templates during page load.
Example
<template>
  <!-- Raw HTML — trust the source! -->
  <div v-html="sanitisedHtml"></div>

  <!-- Complex expressions in interpolation -->
  <p>
    {{ items.length > 0
        ? items.map(i => i.name).join(', ')
        : 'No items yet' }}
  </p>

  <!-- v-pre: skip Vue compilation -->
  <span v-pre>{{ this will render as literal text }}</span>

  <!-- v-cloak: hide before hydration -->
  <!-- Add [v-cloak] { display: none } to CSS -->
  <div v-cloak>
    <p>{{ lazyMessage }}</p>
  </div>

  <!-- Dynamic attribute names (rarely needed) -->
  <button :[dynamicAttr]="dynamicValue">Dynamic Attr</button>
</template>

<script setup>
import { ref } from 'vue';

const sanitisedHtml = ref('<strong>Bold</strong> and <em>italic</em>.');
const lazyMessage   = ref('Loaded!');
const items         = ref([{ name: 'Alpha' }, { name: 'Beta' }]);
const dynamicAttr   = ref('title');
const dynamicValue  = ref('Tooltip text');
</script>