Vue.js v-html Directive
Introduction
In Vue.js applications, we often need to display text content dynamically. The default way to display data in Vue templates is using the mustache syntax ({{ }}
), which automatically escapes HTML characters. However, there are scenarios where we need to render raw HTML content. This is where the v-html
directive comes into play.
The v-html
directive allows you to update the inner HTML of an element with the provided value. It's similar to setting the innerHTML
property in vanilla JavaScript, but wrapped in Vue's reactive system.
Basic Syntax
The basic syntax of the v-html
directive is straightforward:
<element v-html="expression"></element>
Where:
element
is any valid HTML elementexpression
is a Vue data property or expression that evaluates to a string containing HTML
How v-html Works
Let's look at a simple example to understand how v-html
works:
<template>
<div>
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
</div>
</template>
<script>
export default {
data() {
return {
rawHtml: '<span style="color: red">This should be red</span>'
}
}
}
</script>
Output:
- The first paragraph will display the literal string:
<span style="color: red">This should be red</span>
- The second paragraph will render the HTML, showing "This should be red" in red color
v-html vs. Mustache Syntax
To understand when to use v-html
over the mustache syntax, let's compare the two:
Feature | Mustache Syntax {{ }} | v-html Directive |
---|---|---|
HTML Escaping | Automatically escapes HTML | Renders raw HTML |
XSS Protection | Safer (escapes content) | Potentially unsafe |
Use Case | Plain text display | When you need to render HTML markup |
Common Use Cases
1. Rendering Rich Text Editor Content
When working with content from rich text editors (like TinyMCE, CKEditor, etc.):
<template>
<div>
<h2>Blog Post</h2>
<div class="content" v-html="blogPost.content"></div>
</div>
</template>
<script>
export default {
data() {
return {
blogPost: {
title: 'Learning Vue.js',
content: '<p>This is a <strong>formatted</strong> blog post with <em>rich text</em>.</p>'
}
}
}
}
</script>
2. Displaying API-Returned HTML
When your backend API returns formatted HTML content:
<template>
<div>
<div class="api-content" v-html="apiContent"></div>
</div>
</template>
<script>
export default {
data() {
return {
apiContent: ''
}
},
created() {
// Simulating an API call
this.fetchContentFromAPI();
},
methods: {
fetchContentFromAPI() {
// In a real app, this would be an actual API call
setTimeout(() => {
this.apiContent = '<h3>API Response</h3><p>This content came from the server with <strong>HTML formatting</strong>.</p>';
}, 1000);
}
}
}
</script>
3. Dynamically Creating UI Elements
When you need to create UI elements based on data:
<template>
<div>
<div v-html="generateStarRating(productRating)"></div>
<p>Rating: {{ productRating }}/5</p>
</div>
</template>
<script>
export default {
data() {
return {
productRating: 4
}
},
methods: {
generateStarRating(rating) {
let stars = '';
// Create filled stars based on rating
for (let i = 0; i < 5; i++) {
if (i < rating) {
stars += '<span style="color: gold; font-size: 24px;">★</span>';
} else {
stars += '<span style="color: gray; font-size: 24px;">★</span>';
}
}
return stars;
}
}
}
</script>
Security Considerations: XSS Vulnerability
The v-html
directive can be a security risk if used with untrusted content, as it can lead to Cross-Site Scripting (XSS) attacks.
When using v-html
, you're essentially allowing arbitrary HTML to be executed in your application, which can include malicious JavaScript.
Example of potential vulnerability:
<template>
<div>
<h3>User Comment:</h3>
<div v-html="userComment"></div>
</div>
</template>
<script>
export default {
data() {
return {
userComment: '<p>Nice product!</p><script>alert("This could be malicious code!")<\/script>'
}
}
}
</script>
Best Practices to Reduce Risk
- Only use
v-html
with trusted content - Avoid using it with user-generated content or any data from external sources - Sanitize HTML content - Use libraries like DOMPurify to clean HTML before rendering:
<template>
<div>
<div v-html="sanitizedComment"></div>
</div>
</template>
<script>
import DOMPurify from 'dompurify';
export default {
data() {
return {
userComment: '<p>Nice product!</p><script>alert("Malicious code!")<\/script>'
}
},
computed: {
sanitizedComment() {
return DOMPurify.sanitize(this.userComment);
}
}
}
</script>
Practical Example: Creating a Simple Markdown Preview
Let's create a simple markdown preview application using the v-html
directive and a markdown parsing library:
<template>
<div class="markdown-editor">
<div class="editor-container">
<h3>Markdown Input</h3>
<textarea
v-model="markdownInput"
placeholder="Write your markdown here..."
rows="10"
class="markdown-textarea"
></textarea>
</div>
<div class="preview-container">
<h3>Preview</h3>
<div
class="markdown-preview"
v-html="markdownOutput"
></div>
</div>
</div>
</template>
<script>
// You would need to install marked: npm install marked
import { marked } from 'marked';
export default {
data() {
return {
markdownInput: '# Hello Vue!\n\nThis is **bold** text and this is *italic* text.\n\n- List item 1\n- List item 2'
}
},
computed: {
markdownOutput() {
// Convert markdown to HTML
return marked(this.markdownInput);
}
}
}
</script>
<style scoped>
.markdown-editor {
display: flex;
gap: 20px;
}
.editor-container, .preview-container {
flex: 1;
}
.markdown-textarea {
width: 100%;
font-family: monospace;
}
.markdown-preview {
padding: 10px;
border: 1px solid #ddd;
min-height: 200px;
}
</style>
This example creates a simple markdown editor where users can type markdown on the left side and immediately see a preview on the right. The marked
library converts markdown to HTML, and then we use v-html
to render that HTML.
Understanding When Not to Use v-html
While v-html
is powerful, there are cases where you should avoid it:
- Text-only content: Use mustache syntax (
{{ }}
) for regular text - Dynamic class or style binding: Use
v-bind:class
or:class
instead - Creating complex components: Consider using proper component architecture instead
- Untrusted content: Avoid using
v-html
with user input
Summary
The v-html
directive is a powerful tool in Vue.js that allows you to render HTML content dynamically.
Key takeaways:
v-html
updates the element'sinnerHTML
with the provided value- It's useful when you need to render HTML markup that comes from external sources
- Always be cautious about security concerns and only use with trusted content
- When possible, sanitize HTML content with libraries like DOMPurify
- For most scenarios where you just need to display text, the mustache syntax is safer
Exercises
- Create a simple Vue application that fetches an article with HTML formatting from a mock API and displays it using
v-html
- Build a tooltip component that can display formatted HTML content
- Create a "highlight search results" feature that bolds search terms in text using
v-html
- Implement a simple HTML sanitizer before using content with
v-html
Additional Resources
If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)