Tutorials Logic, IN info@tutorialslogic.com

Vue Template Syntax: Interpolation, v-bind, and Directives

Vue Template Syntax

Vue template syntax is the layer where component state becomes visible DOM. Interpolation prints text, bindings feed attributes and props, and directives control whether a node exists at all.

The tricky part is that the template is still HTML-shaped while Vue is compiling expressions behind it. A value can update text, a class list, a boolean attribute, or a whole repeated block depending on the directive you choose.

This page focuses on the exact boundary between markup and data: what belongs in {{ }}, what belongs in :attribute, and what should move into script because the expression has become too large to live in the template.

Interpolation Prints Text

Interpolation is for plain text nodes. Vue evaluates the expression inside the braces, converts the result to a string, escapes it, and places that text into the DOM.

If the data changes later, Vue reruns the render function and updates only the text node that depended on that value.

  • Use interpolation for visible text, labels, counts, and formatted strings.
  • Keep the expression small enough that the text reads cleanly in the template.
  • If the value should become HTML markup, do not use interpolation.

Station status banner

Station status banner
<template>\n  <section class="departure-card">\n    <h2>{{ train.line }} {{ train.number }}</h2>\n    <p>Platform {{ train.platform }} · {{ train.departureTime }}</p>\n    <p>{{ train.statusText }}</p>\n  </section>\n</template>\n\n<script setup>\nimport { reactive } from 'vue'\n\nconst train = reactive({\n  line: 'Harbor Express',\n  number: 'A-19',\n  platform: 4,\n  departureTime: '18:25',\n  statusText: 'Boarding at Gate 2'\n})\n</script>

v-bind Connects Data to Attributes

`v-bind` is the bridge between reactive state and HTML attributes. A bound attribute can become a URL, a disabled flag, a class map, a style object, or a whole set of attributes expanded from one object.

The shorthand `:` is only syntax sugar; Vue treats it the same as `v-bind`. The important part is that the attribute value stays synchronized with component state instead of being copied once and forgotten.

  • Use `:` for values that should follow reactive state.
  • Use object and array forms for classes and styles when the attribute has multiple moving parts.
  • Use `v-bind="object"` when a form field or component prop bundle is already stored as an object.

Ticket form fields

Ticket form fields
<template>\n  <form class="ticket-form">\n    <input\n      :type="field.type"\n      :placeholder="field.placeholder"\n      :disabled="field.locked"\n      :aria-label="field.label"\n    >\n    <button :disabled="field.locked">Update</button>\n  </form>\n</template>\n\n<script setup>\nimport { reactive } from 'vue'\n\nconst field = reactive({\n  type: 'text',\n  placeholder: 'Enter gate code',\n  label: 'Gate code field',\n  locked: false\n})\n</script>

Expressions, Not Statements

Template expressions can read values, call simple methods, use ternaries, and access properties. They cannot contain full control flow statements like if, for, or return.

When logic starts to spread across several bindings, Vue code is usually clearer if that logic moves into a computed property or a helper in <script setup>.

  • Keep interpolation and bindings short enough to scan in one glance.
  • Move repeated calculations out of the template when they start to duplicate.
  • Treat boolean attributes as booleans, not strings.
  • Remember that template expressions run in component scope, not global scope.

Class and Style Binding

Class and style bindings are where template syntax becomes especially practical. Vue lets you feed object and array forms directly into :class and :style, which keeps conditional UI state near the element that uses it.

This is easier to read when the component has a few explicit visual states: selected, delayed, muted, highlighted, or locked.

  • Use object syntax for toggled classes.
  • Use array syntax when classes come from multiple sources.
  • Use style bindings for values that are truly dynamic.
  • Keep one source of truth for the state that drives the visuals.

Gate state chip

Gate state chip
<template>\n  <span\n    :class="[\n      'gate-chip',\n      { open: gate.isOpen, delayed: gate.delayMinutes > 0 }\n    ]"\n    :style="{ opacity: gate.isOpen ? 1 : 0.65 }"\n  >\n    {{ gate.label }}\n  </span>\n</template>\n\n<script setup>\nimport { reactive } from 'vue'\n\nconst gate = reactive({\n  label: 'Gate 12',\n  isOpen: true,\n  delayMinutes: 15\n})\n</script>

Why the Template Compiles Cleanly

Vue does not keep parsing the template as the page runs. It compiles the template into a render function once, then lets reactive reads inside that render function determine what must update.

That is why template syntax feels declarative: you describe the desired DOM shape, and Vue performs the bookkeeping for text nodes, attributes, and conditional branches.

  • Compilation turns the template into DOM update logic.
  • Reactive reads determine the update dependencies.
  • Patching changes only the parts that depend on changed values.
  • The browser still receives ordinary DOM nodes, not a live template engine.

Harbor departure panel

Harbor departure panel
<template>\n  <article class="departure-panel">\n    <h3>{{ departure.name }}</h3>\n    <p :class="{ delayed: departure.delayMinutes > 0 }">\n      {{ departure.delayMinutes > 0 ? departure.delayMinutes + ' minute delay' : 'On time' }}\n    </p>\n    <a :href="departure.ticketUrl" :aria-label="'Open ticket for ' + departure.name">\n      View ticket\n    </a>\n  </article>\n</template>\n\n<script setup>\nimport { reactive } from 'vue'\n\nconst departure = reactive({\n  name: 'Harbor Express 19',\n  delayMinutes: 12,\n  ticketUrl: '/tickets/harbor-express-19'\n})\n</script>
Key Takeaways
  • Use interpolation only for text nodes that should display reactive values.
  • Use `:` whenever an attribute must stay in sync with state.
  • Use ternaries or computed values when a binding needs a decision, not a block of logic.
  • Bind classes and styles with the structure that matches the number of states you need.
  • Keep the template readable enough that the component still makes sense without opening the script file.
Common Mistakes to Avoid
WRONG Writing `src="{{ imageUrl }}"` or `disabled="{{ locked }}"`.
RIGHT Use `:src="imageUrl"` and `:disabled="locked"` so Vue owns the live value.
Mustaches are for text nodes, not attribute values.
WRONG Putting `if`, `for`, or assignment logic directly inside `{{ }}`.
RIGHT Use a ternary, computed property, or method result that already returns the final value.
Templates read better when they stay expression-based.
WRONG Using `v-html` for content that should be safe plain text.
RIGHT Use interpolation for text and keep `v-html` for trusted markup only.
Rendered HTML changes the security model of the page.

Practice Tasks

  • Build a departure card that shows line name, gate number, and boarding state from reactive data.
  • Add one boolean that changes both the class and the button label without touching the DOM manually.
  • Replace a template expression with a computed property when the expression starts getting noisy.
  • Bind a form field object into several attributes with `v-bind="object"` and verify the output.

Frequently Asked Questions

Text nodes and attributes are different parts of the DOM. Interpolation writes text, while `v-bind` updates attributes or properties.

Vue converts the value to a string for text rendering. That is usually not what you want unless the string form is intentional.

HTML treats the presence of many attributes as truthy. A string like "false" still counts as present, so the binding must pass a boolean value.

Move it when the binding becomes hard to scan or when the same logic appears in several places. That usually means the value belongs in a computed property.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.