Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/in-dom-template-parsing-caveats.md
1---2title: In-DOM Template Parsing Caveats3impact: HIGH4impactDescription: Browser HTML parsing before Vue compilation causes case sensitivity, self-closing tag, and element nesting issues5type: gotcha6tags: [vue3, templates, in-dom, html-parsing, kebab-case, self-closing-tags]7---89# In-DOM Template Parsing Caveats1011**Impact: HIGH** - When writing Vue templates directly in the DOM (not in `.vue` files), the browser's native HTML parser processes the template BEFORE Vue sees it. This causes three critical issues: case sensitivity problems, self-closing tag failures, and element placement restrictions.1213These issues do NOT apply to Single-File Components (SFCs) or string templates where Vue's compiler handles parsing directly.1415## Task Checklist1617- [ ] Use kebab-case for component names in in-DOM templates18- [ ] Use kebab-case for prop names in in-DOM templates19- [ ] Use explicit closing tags (not self-closing) in in-DOM templates20- [ ] Use `is="vue:component-name"` for components inside restricted elements21- [ ] Prefer SFCs to avoid all in-DOM parsing issues2223## Issue 1: Case Insensitivity2425HTML is case-insensitive. The browser lowercases everything before Vue sees it.2627**Incorrect (in-DOM template):**28```html29<!-- Browser converts to: <blogpost posttitle="hello"> -->30<BlogPost postTitle="hello" @updatePost="onUpdate"></BlogPost>31```3233**Correct (in-DOM template):**34```html35<!-- Use kebab-case for everything -->36<blog-post post-title="hello" @update-post="onUpdate"></blog-post>37```3839**In SFCs, PascalCase works fine:**40```vue41<!-- BlogPost.vue - PascalCase recommended -->42<template>43<BlogPost postTitle="hello" @updatePost="onUpdate" />44</template>45```4647## Issue 2: Self-Closing Tags Fail4849HTML only allows self-closing syntax for void elements (`<input>`, `<img>`, etc.). For all others, the browser expects closing tags.5051**Incorrect (in-DOM template):**52```html53<!-- Browser thinks the tag never closed, breaks nesting -->54<my-component />55<another-component />56```5758**Correct (in-DOM template):**59```html60<!-- Explicit closing tags required -->61<my-component></my-component>62<another-component></another-component>63```6465**In SFCs, self-closing works fine:**66```vue67<template>68<MyComponent />69<AnotherComponent />70</template>71```7273## Issue 3: Element Placement Restrictions7475Some HTML elements have strict rules about valid children. Invalid elements are hoisted out by the browser before Vue sees the template.7677**Restricted parent elements:**78- `<ul>`, `<ol>` - only allow `<li>`79- `<table>` - only allows `<thead>`, `<tbody>`, `<tfoot>`, `<tr>`, `<caption>`, `<colgroup>`80- `<tr>` - only allows `<td>`, `<th>`81- `<select>` - only allows `<option>`, `<optgroup>`8283**Incorrect (in-DOM template):**84```html85<!-- Browser hoists blog-post-row outside the table -->86<table>87<blog-post-row v-for="post in posts" :post="post"></blog-post-row>88</table>8990<!-- Renders as: -->91<blog-post-row></blog-post-row>92<blog-post-row></blog-post-row>93<table></table>94```9596**Correct (in-DOM template):**97```html98<!-- Use is="vue:component-name" on a valid native element -->99<table>100<tr is="vue:blog-post-row" v-for="post in posts" :key="post.id" :post="post"></tr>101</table>102```103104```html105<ul>106<li is="vue:todo-item" v-for="todo in todos" :key="todo.id" :todo="todo"></li>107</ul>108```109110**Important:** The `vue:` prefix is required! Without it, `is` is treated as a native customized built-in element attribute.111112```html113<!-- WRONG: Missing vue: prefix -->114<tr is="blog-post-row"></tr>115116<!-- CORRECT: With vue: prefix -->117<tr is="vue:blog-post-row"></tr>118```119120## When Do These Apply?121122| Template Type | Affected? | Example |123|---------------|-----------|---------|124| Single-File Component (`.vue`) | No | `<template>` section |125| String template | No | `template: '<div>...</div>'` |126| In-DOM template | **Yes** | `<div id="app">...</div>` |127| `<script type="text/x-template">` | **Yes** | Browser parses the script content |128129## Best Practice: Use SFCs130131The simplest solution is to use Single-File Components (`.vue` files) which completely avoid in-DOM parsing issues:132133```vue134<!-- MyComponent.vue - All issues avoided -->135<script setup>136import BlogPost from './BlogPost.vue'137</script>138139<template>140<BlogPost postTitle="hello" @updatePost="onUpdate" />141142<table>143<BlogPostRow v-for="post in posts" :key="post.id" :post="post" />144</table>145</template>146```147148## Reference149- [Vue.js - In-DOM Template Parsing Caveats](https://vuejs.org/guide/essentials/component-basics.html#in-dom-template-parsing-caveats)150