codejump

AcademyVue

Template Syntax: Interpolation and Bindings

What {{ }} can and cannot render, why every dynamic attribute needs v-bind, and how :class and :style differ from a plain HTML string.

3 min readUpdated

3 exercises

Waiting at the end of this page.

{{ }} renders an expression, not a statement

Vue's mustache syntax evaluates a single JavaScript expression and stringifies the result into the DOM as text:

vue
<template>
  <p>{{ user.name }}</p>
  <p>{{ items.length }} items</p>
  <p>{{ isActive ? 'Online' : 'Offline' }}</p>
</template>

An expression, not a statement — {{ if (x) { ... } }} is not valid inside {{ }}, and neither are variable declarations. The ternary above is the idiomatic way to branch inline; anything with real control flow belongs in a computed property or a method, called from the template instead of written into it.

The other thing {{ }} cannot do is render an object usefully. {{ user }} stringifies the whole object via JSON.stringify-like formatting — it is a bug every Vue beginner hits once, and the fix is always the same: reach one level deeper, {{ user.name }}.

Every dynamic attribute needs v-bind

{{ }} only works inside text content. An attribute written as a plain string is exactly that — a string, evaluated never:

vue
<!-- Wrong: renders the literal text "userId" as the id attribute -->
<div id="userId"></div>

<!-- Right: v-bind evaluates the expression -->
<div v-bind:id="userId"></div>
<div :id="userId"></div>

:attr is shorthand for v-bind:attr — the two are identical, and every Vue codebase you will read uses the shorthand exclusively. The rule to remember is simple: no colon, no evaluation. This catches beginners specifically on boolean attributes like disabled: writing disabled="isFollowing" sets a real DOM attribute to the literal string "isFollowing", which the browser's boolean-attribute reflection then treats as present — disabling the button permanently, regardless of what isFollowing actually holds.

:class and :style take objects, not strings

Concatenating class names or CSS by hand is the most common Vue anti-pattern outside of the {{ }}-on-attributes mistake above:

vue
<template>
  <!-- Works, but string-builds and re-parses on every change -->
  <span :class="'item ' + (done ? 'done' : '')"></span>

  <!-- Idiomatic: object syntax, each key a class, each value a boolean -->
  <span :class="{ item: true, done: done }"></span>

  <!-- Same idea for inline styles, camelCase keys -->
  <span :style="{ color: done ? 'gray' : 'black', textDecoration: done ? 'line-through' : 'none' }"></span>
</template>

The object form composes with a static class attribute on the same element — Vue merges them — and it reads as data rather than as a string you have to mentally re-parse. :style's object form takes camelCase CSS properties (backgroundColor, not background-color), matching the DOM's own CSSStyleDeclaration rather than the stylesheet syntax.

Compilation, not a virtual DOM diff, is why this is fast

A template like the ones above is compiled ahead of time into a render function that Vue's reactivity system re-runs only when the specific values it reads change — a static <footer> with no bindings is hoisted out and never touched again after the first render. That is a different mechanism from React re-rendering a component function and diffing the result: Vue knows, at compile time, exactly which DOM nodes a given piece of state can affect, because the template told it so.

What interviewers check

Whether you can name the failure mode, not just the syntax: forgetting : on a bound attribute, rendering an object through {{ }}, and reaching for string concatenation instead of the object form of :class/:style are the three template-syntax bugs that show up in real codebases and in real interviews.

Now practice it

1 quiz · 2 drills

Reading this page is the cheap half. These are the exercises that make you use it.