Chapter 56: Vue template Element
The <template> element
This is not just some random HTML tag — it is the heart of how Vue turns your declarative markup into a reactive, live user interface.
In a .vue file, the <template> block is mandatory (unless you’re using render functions / JSX), and it contains almost everything the user actually sees on the screen.
1. What is the <template> element? (Very simple explanation)
The <template> tag in a .vue file is a special instruction to the Vue compiler:
“Everything inside me is the HTML-like structure that describes what this component should render in the browser. Please compile me into efficient JavaScript render functions, make all the {{ }} and directives reactive, and handle updates automatically.”
Important facts right at the beginning:
- There must be exactly one root <template> block per .vue file (Vue 3 still enforces single root — but it can be <template> itself for fragments)
- Everything inside <template> is not sent to the browser as-is — Vue compiles it at build time (Vite does this instantly)
- The final output is highly optimized JavaScript that creates/updates real DOM nodes
- The browser never sees the word <template> in the final DOM — it’s gone after compilation
2. Basic Structure of a Modern Vue SFC (2026 style)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<template> <!-- This is where ALL user-visible markup lives --> <div class="card"> <h2>{{ title }}</h2> <p>{{ description }}</p> <button @click="increment"> Count is {{ count }} </button> </div> </template> <script setup> import { ref } from 'vue' const title = 'My First Card' const description = 'This content lives inside <template>' const count = ref(0) function increment() { count.value++ } </script> <style scoped> .card { padding: 1.5rem; background: white; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.08); max-width: 400px; margin: 2rem auto; } </style> |
Key points about <template>
- It can contain normal HTML + Vue template syntax
- Vue template syntax includes:
- {{ }} text interpolation
- Directives: v-if, v-for, v-model, v-bind (:), v-on (@), etc.
- Dynamic attributes :class, :style
- Component tags <MyButton />
- <slot>, <component :is>, <Transition>, etc.
- No JavaScript code is allowed directly inside <template> (except inside {{ }} expressions)
3. Special Powers & Common Patterns Inside <template>
A. Fragments — multiple root nodes (Vue 3 only)
In Vue 2 you needed one root element. Vue 3 allows multiple root nodes — but only inside <template>.
|
0 1 2 3 4 5 6 7 8 9 10 11 |
<template> <!-- No extra wrapper div needed --> <h1>Title</h1> <p>Paragraph</p> <button>Click me</button> </template> |
→ Renders three sibling elements — very clean for layouts
B. <template> as wrapper for v-if / v-for (no extra DOM node)
This is extremely common — you want conditional or looped content without adding an extra <div>.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<template> <!-- v-if group – no wrapper div in DOM when false --> <template v-if="isLoggedIn"> <p>Welcome, {{ user.name }}</p> <button @click="logout">Logout</button> </template> <p v-else>Please log in</p> <!-- v-for group – no wrapper --> <template v-for="item in items" :key="item.id"> <div class="header">{{ item.title }}</div> <p>{{ item.description }}</p> </template> </template> |
→ When condition is false → nothing is rendered (no empty <div>)
C. <template> with v-slot (named/scoped slots)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<template> <MyCard> <template #header> <h3>Custom Header</h3> </template> Default body content <template #footer> <button>Action</button> </template> </MyCard> </template> |
D. Dynamic attributes & shorthand
|
0 1 2 3 4 5 6 7 |
<input :type="inputType" v-model="value" :disabled="isDisabled" /> <button @click="submit" :class="{ 'btn-primary': isPrimary }">Submit</button> |
4. What <template> does NOT allow
- No <script> or <style> tags inside <template>
- No raw JavaScript blocks (only expressions inside {{ }}, :attr, @event)
- No <template> inside <template> for nesting (use components instead)
5. Quick Summary Table – Most Important Uses of <template>
| Use-case | Syntax Example | Why it’s useful / common |
|---|---|---|
| Normal component template | <template> <div>…</div> </template> | Every SFC needs one |
| Multiple root nodes (fragments) | <template> <h1></h1> <p></p> </template> | No unnecessary wrapper |
| Conditional group without wrapper | <template v-if=”…”> … </template> | Clean DOM, no extra div |
| List group without wrapper | <template v-for=”…” :key=”…”> … </template> | Clean DOM, no extra div |
| Named / scoped slots in parent | <template #header> … </template> | Custom content injection |
| Dynamic slot names | <template #[slotName]> … </template> | Rare but powerful |
Pro Tips from Real Projects (Hyderabad 2026)
- Always use <template v-for> + <template v-if> when you don’t want extra wrapper <div> — cleaner DOM, better accessibility, smaller payload
- Keep <template>readable — if it’s longer than ~30–40 lines → extract into child components
- Use shorthand # for named slots — modern codebases almost always do this
- Never put complex logic inside {{ }} — move to computed or method
- For accessibility → prefer semantic HTML inside <template> (<section>, <article>, <nav>, etc.)
Your mini practice task:
- Create a component with multiple root nodes (no wrapper div)
- Use <template v-if> to conditionally render a group of elements
- Use <template v-for> to render multiple elements without wrapper
- In a parent component → use named slots (#header, #footer) on a child card
Any part still confusing? Want full examples for:
- <template> + v-for + v-if together?
- Dynamic slot names with #[dynamicSlot]?
- Real layout component using multiple roots + named slots?
- <template> vs render function / JSX comparison?
Just tell me — we’ll build the next clean template together 🚀
