v-html — Rendering Raw HTML
Shelf's team writes a short "Book of the week" blurb in the admin panel, stored as HTML with bold titles and links. Printed with double braces, the reader sees raw tags like <strong>Dune</strong>. To have the browser treat the string as markup, you need v-html, and you need to know exactly when it is safe.
The rule
v-html trusts the string completely, so feed it only trusted HTML.
<script setup>
const blurb = '<strong>Dune</strong> is this week\'s pick. <a href="/books/1">Read more</a>'
</script>
<template>
<p>{{ blurb }}</p> <!-- shows the tags as text -->
<div v-html="blurb"></div> <!-- shows bold text and a link -->
</template>What v-html does, piece by piece:
- Replaces the content. Whatever was inside the
divis thrown away and the string's HTML takes its place. Do not put children inside an element that hasv-html. - Plain HTML only. The string is not a Vue template. If it contains
{{ title }}or<BookCard />, they are not processed; they just appear as text or an unknown tag. - Scoped styles do not reach it. Scoped CSS works by stamping elements Vue creates from the template. Elements that arrive through
v-htmlare not stamped, so scoped rules skip them. Style them with global CSS or the:deep()selector. - No escaping. This is the dangerous part. If the string came from a user, it can carry markup such as an image with an
onerrorattribute that runs JavaScript in every reader's browser. That is a cross-site scripting (XSS) attack.
The shortcut
Before typing v-html, ask "Who wrote this string?"
- Your own team or your own code →
v-htmlis fine. - Any user, or an outside service you do not control → use
{{ }}, or clean the HTML first with a well-tested sanitiser library.
The weekly blurb
Written by staff in the admin panel. Trusted, and it needs bold text and links. Use
v-html.
The trap is thinking "it is only our database". If users can write into that database, their text is not trusted.
Drills
Answer fast — trust the rule.
What is the main danger of v-html with user-written text?
A trusted string contains {{ user }}. What does v-html render for that part?
Scoped styles in the component do not affect the links inside a v-html blurb. Why?
Which content is safe to show with plain v-html, without sanitising?
Remember
v-htmlreplaces an element's content with the string parsed as plain HTML.- Shortcut: "who wrote this string?" Only trusted authors get
v-html. - Trap: user text in
v-htmlis an XSS hole; scoped styles also do not reach it.
