Text

Simple text can be shown through text interpolation, using «Mustache» syntax (double curly braces):

<div id="app">
  {{ message }}
</div>

<script type="module">
  import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'

  const app = createApp({
    data() {
      return {
        message: 'Hello world'
      }
    }
  })
  app.mount('#app')
</script>

Result:

Hello World

Directives

Directives are special attributes with the v- prefix. Vue provides a number of built-in directives, including v-html and v-bind. Some directives take an argument (static or dynamic) and be shorthanded like v-bind (takes an attribute name) and v-on (takes an event name).

<!-- Simple directive -->
<p v-if="seen">Now you see me</p>

<!-- v-bind takes as argument the attributes name -->
<a v-bind:href="url"> ... </a>
<!-- Shorthanded -->
<a :href="url"> ... </a>

<!-- v-on takes as argument the event name -->
<a v-on:click="doSomething"> ... </a>
<!-- Shorthanded -->
<a @click="doSomething"> ... </a>

<!-- Attribute name taken from attributeName property value-->
<a v-bind:[attributeName]="url"> ... </a>
<!-- Shorthanded -->
<a :[attributeName]="url"> ... </a>

<!-- Event name taken from eventName property value-->
<a v-on:[eventName]="doSomething"> ... </a>
<!-- Shorthanded -->
<a @[eventName]="doSomething">

Directives can also add modifiers for specific behaviors (v-on modifiers, v-model modifiers).

<!-- The click event's propagation will be stopped -->
<a @click.stop="doThis"></a>
<!-- The submit event will no longer reload the page -->
<form @submit.prevent="onSubmit"></form>
<!-- Modifiers can be chained -->
<a @click.stop.prevent="doThat"></a>
<!-- Just the modifier -->
<form @submit.prevent></form>
<!-- Only trigger handler if event.target is the element itself -->
<!-- i.e. not from a child element -->
<div @click.self="doThat">...</div>

<!-- Synced after "change" instead of "input" -->
<input v-model.lazy="msg" />
<!-- Typecast value to number -->
<input v-model.number="age" />
<!-- Trim string values -->
<input v-model.trim="msg" />

Additionally, we need to take some considerations when using directives:

<!-- When dynamic argument is undefined or null, binding is removed -->
<a :[attributeName]="url"> ... </a>

<!-- Dynamic arguments that contains non HTML valid values will trigger a compiler warning -->
<a :['foo' + bar]="value"> ... </a>

<!-- In "in-DOM" templates, camelCase arguments are forced to be converted to lower case -->
<!-- "someAttr" will be "someattr" -->
<a :[someAttr]="value"> ... </a>

Raw HTML

If, instead of simple text you need to show HTML, you need to use v-html directive:

<div id="app">
  <div>{{ rawHTML }}</div>
  <div v-html="rawHTML"></div>
</div>

<script type="module">
  import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'

  const app = createApp({
    data() {
      return {
        rawHTML: '<span style="color: red">This should be red.</span>'
      }
    }
  })
  app.mount('#app')
</script>

Result:

<span style="color: red">This should be red.</span>
This should be red.

Attributes

In order to modify dynamically attributes’ values, we need to use v-bind directive. The : symbol can be used as a shorthand.

<!-- Attribute's value change according to dynamicId property value -->
<div v-bind:id="dynamicId"></div>

<!-- v-bind shorthand -->
<div :id="dynamicId"></div>

<!-- Boolean attributes -->
<button :disabled="isButtonDisabled">Button</button>

We can also add multiple attributes at once:

<div id="app">
  <div v-bind="objectOfAttrs"></div>
</div>

<script type="module">
  import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'

  const app = createApp({
    data() {
      return {
        objectOfAttrs: {
          id: 'container',
          class: 'wrapper'
        }
      }
    }
  })
  app.mount('#app')
</script>

Expressions

Expressions can be used in text interpolation and attributes values of directives. We can use single expressions, and also functions (preferably computed properties). Some examples are:

<!-- Arithmetic operation -->
<div>{{ number + 1 }}</div>

<!-- Conditional (ternary) operator -->
<div>{{ ok ? 'YES' : 'NO' }}</div>

<!-- Invoke object's methods -->
<div>{{ message.split('').reverse().join('') }}</div>

<!-- Concatenation -->
<div :id="`list-${id}`"></div>

<!-- Functions -->
<time :title="toTitleDate(date)" :datetime="date">
  {{ formatDate(date) }}
</time>

Inside expressions we are restricted to use some built-in globals. If we need to add more of these, we need to use app.config.globalProperties.